Download presentation
Presentation is loading. Please wait.
1
2014 操作系统课程设计 Operating System Practicum
2
内容简介 设计目的 设计内容 实施方法及要求 时间安排 辅导
3
设 计 目 的 掌握Linux操作系统的使用方法; 了解Linux系统内核代码结构; 掌握实例操作系统的实现方法。
4
内容简介 设计目的 设计内容 实施方法及要求 时间安排 辅导
5
设 计 内 容(1) 要求:熟悉和理解Linux编程环境 内容 编写一个C程序,使用Linux下的图形库,分窗口显示三个并发进程的运行。
6
设 计 内 容(2) 要求:掌握添加系统调用的方法 内容 采用编译内核的方法,添加一个新的系统调用。
编写一个应用程序,测试新添加的系统调用。
7
设 计 内 容(3) 要求:掌握添加设备驱动程序的方法 内容: 采用模块方法,添加一个新的设备驱动程序。 要求添加字符设备的驱动。
编写一个应用程序,测试添加的驱动程序。
8
设 计 内 容(4) 要求:理解和分析/proc文件 内容 了解/proc文件的特点和使用方法。
监控系统状态,显示系统中若干部件的使用情况。 用图形界面显示系统监控状态。
9
设 计 内 容(5) 要求:理解和掌握文件系统的设计方法 内容 设计、实现一个模拟的文件系统。
10
内容简介 设计目的 设计内容 实施方法及要求 时间安排 辅导
11
实施方法及要求 每位同学都必须独立完成课程设计内容。 上机考核 演示完成的系统,并回答老师的问题。 提交
文本的课程设计报告,内容包括课程设计内容分析、程序清单(附注释)、调试记录(碰到的问题和解决方案)以及课程设计心得。 程序(电子版)。
12
实施方法及要求(续) 支持借鉴和学习已有的优秀知识! 反对全盘拷贝,不求甚解! 吸收和消化他人经验,做自己的课程设计!
自学能力的培养:学会上网查资料、解决问题!
13
内容简介 设计目的 设计内容 实施方法及要求 时间安排 辅导
14
时间安排 答疑 石柯 13517283980 keshi@mail.hust.edu.cn 每周四下午检查进展
刘水兵 刘承全 每周四下午检查进展
15
内容简介 设计目的 设计内容 实施方法及要求 时间安排 辅导
16
课程设计辅导 Linux系统的相关知识 进程并发 添加系统调用 添加设备驱动程序 /proc文件分析
17
Linux系统的相关知识 Linux版本 Linux通过简单的编号来区别内核的稳定版和开发版。每个版本用三个数字描述,由圆点分隔。前两个数表示版本号,第三个数表示发布号,如2.4.20。(2.6版本和2.4版本在具体的操作细节上有很大差异) 如果第二个数为偶数,则表示稳定的内核;否则,表示开发中的内核。 稳定版的发布主要用来纠正用户所报告的错误,但实现内核的主要算法和数据结构基本不变。然而,开发版本间可能存在很大的差异。
18
Linux系统的常用目录 文件目录结构——树型结构 常用目录有:
/dev: dev是device的缩写。这个目录包含Linux的所有设备文件,如/dev/hda代表第一个物理IDE硬盘。 /etc: 这个目录用来存放系统管理所需要的配置文件和子目录。 /lib: 这个目录里存放着系统最基本的动态链接共享库,几乎所有的应用程序都需要用到这些共享库。
19
Linux系统的常用目录(续) /usr: 这是最庞大的目录,我们要用到的应用程序和文件几乎都存放在这个目录下。其中包含以下子目录:
/usr/include: Linux下开发和编译应用程序所需的头文件; /usr/lib: 常用的动态链接共享库和静态档案库; /usr/local: 这是提供给一般用户的目录,在这里安装软件最适合; /usr/man: 帮助文档的存放目录; /usr/src: Linux的源代码目录。
20
Linux系统的核心源码 Linux核心源代码位于/usr/src/linux下,包括:
arch: 包括所有和CPU类型相关的核心代码。它的每一个子目录都代表一种支持的CPU类型,例如i386就是关于Intel CPU及与之相兼容的体系结构的子目录,PC机一般都基于此目录; drivers: 放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录,如/block下为块设备驱动程序; include: 包括编译核心所需要的大部分头文件。与平台无关的头文件在include/linux子目录下,与Intel CPU相关的头文件在include/asm-i386子目录下;
21
Linux系统的核心源码(续) Linux核心源代码中的内容还包括:
init: 包含核心的初始化代码(注:不是系统的引导代码),包含两个文件 main.c和version.c,这是研究核心如何工作的一个非常好的起点; mm: 包括所有独立于CPU体系结构的内存管理代码,如页式存储管理中内存的分配和释放等,而和体系结构相关的内存管理代码则位于arch/*/mm/下; kernel: 主要的核心代码,此目录下的文件实现了大多数Linux系统的内核函数,其中最重要的文件当属进程调度sched.c,同样,和体系结构相关的代码在arch/*/kernel中。
22
课程设计辅导 Linux系统的相关知识 进程并发 添加系统调用 添加设备驱动程序 /proc文件分析
23
进程并发 pid=fork():创建子进程。 返回值:0 从子进程返回, > 0 从父进程返回
返回值:0 从子进程返回, > 0 从父进程返回 exit进程自我终止,进入僵死状态,仍然保留task_struct结构。 wait( ) 等待进程终止(由父进程调用) exec( ) 执行一个可执行程序(文件)
24
进程并发 窗口、进度条和按钮的创建 程序流程 如何同时显示三个并发进程 编译命令
25
进程并发
26
进程并发 GTK(GIMP Toolkit) 控件、消息处理器和回调函数 利用控件可以实现一些图形的显示,比如显示窗口等等
27
进程并发 main(int argc, char* argv[]) {
son = fork(); if (son == 0) { 创建窗口; exit(0); } else if (son < 0) { printf("儿子进程创建失败。\n"); } if (son > 0) { waitpid(son, &status, 0); } return 0; } void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } int g_window(int argc, char *argv[], char s[]) { …;
28
课程设计辅导 Linux系统的相关知识 进程并发 添加系统调用 添加设备驱动程序 /proc文件分析
29
添加系统调用 Linux系统调用机制 系统调用与普通函数调用的区别 Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。
用户可以通过系统调用命令在自己的应用程序中调用它们。 系统调用与普通函数调用的区别 系统调用 核心态 操作系统核心提供 普通的函数调用 用户态 函数库或用户自己提供
30
Linux系统调用机制 添加系统调用(续) int 0x80
使用寄存器中适当的值跳转到内核中事先定义好的代码中执行:跳转到系统调用的总入口system_call,检查系统调用号,再查找系统调用表sys_call_table,调用内核函数,最后返回。 系统调用是靠一些宏、一张系统调用表、一个系统调用入口来完成的。
31
添加系统调用(续) 与系统调用相关的内核代码文件(2.6内核):
/usr/src/linux/include/asm-i386/unistd.h 系统调用清单(为每个系统调用分配唯一号码) /usr/src/linux/arch/i386/kernel/entry.S 包含系统调用和异常的底层处理程序、信号量的识别程序 ret_from_sys_call:调用和中断的返回点 对sys_call_table[ ](系统功能调用表)进行初始化 /usr/src/linux/kernel/sys.c 系统调用实现代码 /usr/src/linux/arch/i386/kernel/traps.c 定义许多出错处理程序
32
添加系统调用(续) 步骤_1 添加源代码 编写添加到内核中的源程序,函数名以sys_开头。
步骤_1 添加源代码 编写添加到内核中的源程序,函数名以sys_开头。 如:mycall(int num),在/usr/src/linux/kernel/sys.c文件中添加如下代码: asmlinkage int sys_mycall(int number) { return number; //该系统调用仅返回一个整型值 }
33
添加系统调用(续) 步骤_2 连接新的系统调用 使内核的其余部分知道该系统调用的存在。为此,需编辑两个文件:
步骤_2 连接新的系统调用 使内核的其余部分知道该系统调用的存在。为此,需编辑两个文件: /usr/src/linux/include/asm-i386/unistd.h——系统调用清单(为每个系统调用分配唯一号码) #define _NR_name nnn 这里,name:系统调用名;nnn:系统调用对应的号码,不能与内核自身的系统调用号相同。 /usr/src/linux/arch/i386/kernel/entry.S——对sys_call_table[ ]进行初始化(增加新的内核函数的指针) .long SYMBOL_NAME(sys_mycall)
34
添加系统调用(续) 步骤_3 重建Linux内核 配置内核的方式 对每个配置选项,有三种选择:
以root身份进入/usr/src/linux目录,重建内核 #make config //基于文本的传统配置界面 #make dep //检验内核源代码文件的依赖性和完整性 #make clean //清除以前编译的目标文件 #make bzImage //编译内核,也可采用make zImage 编译生成的内核文件为 /usr/src/linux/arch/i386/boot/bzImage 配置内核的方式 make config 基于文本的传统配置界面 make menuconfig 基于文本的菜单形式配置界面,字符终端下使用 make xconfig 基于图形窗口模式的配置界面,Xwindow下使用 对每个配置选项,有三种选择: “Y”——将该功能编译进内核 “N”——不将该功能编译进内核 “M”——将该功能编译成可动态载入的内核模块
35
补充说明 配置内核的方式 对每个配置选项,有三种选择: make config 基于文本的传统配置界面
make menuconfig 基于文本的菜单形式配置界面,字符终端下使用 make xconfig 基于图形窗口模式的配置界面,Xwindow下使用 make oldconfig 在原来的配置上作小的修改 对每个配置选项,有三种选择: “Y”——将该功能编译进内核 “N”——不将该功能编译进内核 “M”——将该功能编译成可动态载入的内核模块
36
若采用grub,修改/etc/grub.conf,添加新的引导内核
添加系统调用(续) 步骤_4 重启内核 将/usr/src/linux/arch/i386/boot/bzImage拷贝到/boot/bzImage 配置启动文件 若采用lilo,修改/etc/lilo.conf,添加新的引导内核 image=/boot/bzImage // 上面编译生成的内核映象 label=Linuxtest // 给该系统取个名字 root=/dev/hda // 根目录所在的分区,可用命令df查看 read-only 若采用grub,修改/etc/grub.conf,添加新的引导内核 title Linuxtest root(hd0,4) kernel /boot/bzImage ro root=/dev/hda5 initrd /boot/initrd img 重启后,出现Linuxtest选项,选择它进入新的内核
37
添加系统调用(续) 步骤_5 使用新的系统调用 应用程序app.c中调用新添加的系统调用mycall: 编译gcc –o app app.c
步骤_5 使用新的系统调用 应用程序app.c中调用新添加的系统调用mycall: 宏指令说明:_syscalln(parameters):n表示系统调用所需参数,parameters为参数。如上面的宏中,第一个参数int表示返回值类型,第二个参数mycall为函数名。第三个和第四个分别用来指定参数的类型和名称。 编译gcc –o app app.c int main(int argv, char *argc[]) { int a = mycall(100); printf(“%d\n”,a); return 0; } #include<stdio.h> #include</usr/src/linux-2.4/ include/asm/unistd.h> #include<errno.h> _syscall1(int, mycall, int, ret) _syscall1(int mycall, int ret),讲这个函数时一定要强调,syscall后面紧紧跟了一个1,这个1时系统调用所需要的参数。
38
添加系统调用(续) 函数调用: 相同点:功能相同 不同点:作为内核代码,不能直接调用系统调用命令,应直接使用系统调用的实际函数
printf —— printk open() —— sys_open() close() —— sys_close() read() —— sys_read() write() —— sys_write()
39
添加系统调用(续)-3.2内核 修改(添加)源代码
第一个任务是编写加到内核中的源程序,即将要加到一个内核文件中去的一个函数,该函数的名称应该是新的系统调用名称前面加上sys_标志。假设新加的系统调用为mycall(int number),在/usr/src/linux-3.2.4/kernel/sys.c文件中添加源代码,如下所示: asmlinkage int sys_mycall(int number) { return number; }
40
添加系统调用(续)-3.2内核 连接新的系统调用
/usr/src/linux-3.2.4/arch/x86/include/asm/unistd_32.h 。 该文件中包含了系统调用清单,用来给每个系统调用分配一个唯一的号码。文件中每一行的格式如下: #define __NR_name NNN #define __NR_mycall /*这是我们自己添加的系统调用*/ #define NR_syscalls /usr/src/linux-3.2.4/arch/x86/kernel/syscall_table_32.s 对sys_call_table[]数组进行初始化。该数组包含指向内核中每个系统调用的指针.long sys_name .long sys_mycall 必须注意添加的行的位置,否则容易造成内核编译的失败
41
添加系统调用(续)-3.2内核 开始对新的内核进行编译(取决于所用的系统,以ubuntu为例)
cd /usr/src/linux-3.2.4/ sudo make mrproper sudo make menuconfig (第一次这步可能执行出错,需要安装什么包如下命令安装:sudo apt-get install libncurses5-dev,安装好后,再make menuconfig sudo 此步由make bzImage 和 make modules两步组成,两步操作都要等很长时间,还不如等一步,之后就是让机子慢慢的编译内核去了) sudo make modules_install sudo make install sudo update-grub
42
添加系统调用(续)-3.2内核 对新加的系统调用进行测试 #include<stdio.h>
#include</usr/src/linux-3.2.4/arch/x86/include/asm//unistd_32.h> #include<errno.h> #include<sys/syscall.h> int main(int argc,char **argv) { int b=syscall(349,200); /*第一个参数系统调用号,第二个参数,给的任意数值参数*/ printf("%d\n",b); return 0; }
43
课程设计辅导 Linux系统的相关知识 进程并发 添加系统调用 添加设备驱动程序 /proc文件分析
44
添加设备驱动程序 内核模块 LKM Loadable Kernel Modules
Linux核心是一种monolithic类型的内核,即单一的大核心。 linux内核是一个整体结构,因此向内核添加任何东西.或者删除某些功能,都十分困难。为了解决这个问题,引入了模块机制,从而可以动态的在内核中添加或者删除模块。一旦被插入内核,他就和内核其他部分一样。
45
模块的实现机制 添加设备驱动程序(续) 模块初始化 int init_module( ){ };
模块卸载 int cleanup_module( ){ }; 操作 unsigned long sys_create_module (char *name, unsigned long size); //重新分配内存 int sys_delete_module (char *name); //卸载 int sys_query_module (const char *name, int which, void *buf, size_t bufsize, size_t *ret); //查询 头文件:/usr/scr/linux/include/linux/module.h
46
添加设备驱动程序(续) 模块的实现机制 模块加入:insmod modulename.o 查看模块:lsmod
完成:加载目标文件 调用create_module重新分配内存 内核符号用get_kernel_syms解析未解析的引用 调用init_module初始化LKM->执行init_module(void)函数 查看模块:lsmod 结果:Module Page Used by modulename 1(内存信息) (使用次数) 删除模块:rmmod modulename
47
添加设备驱动程序(续) 模块编程实例 hello.c源码 编译
gcc –DMODULE –D_KERNEL_ -I /usr/src/linux_ /include -c hello.c printk("hello world !\n'); printk("I have runing in a kernel mod! \n"); return 0; } void cleanup_module() /* 模块卸载 */ { printk(" I will shut down myself in kernel mod!\n)"; } #include "linux/kernerl.h" #include "linux/module.h" /*处理版本问题CONFIG_MODVERSIONS */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include "linux/version.h" #end if int init_module() /* 模块初始化*/ {
48
添加设备驱动程序(续) Linux支持的设备类型 字符设备—— c 块设备—— b 网络设备
存取时没有缓存;对字符设备发出读写请求时,实际的I/O就发生了。如:鼠标、键盘等。 块设备—— b 利用一块系统内存区域作缓冲区,当用户进程对设备请求能满足用户要求时,返回请求数据,否则,调用请求函数进行实际的I/O操作。如:硬盘、软盘、CD-ROM等。 网络设备
49
添加设备驱动程序(续) 设备驱动程序 一组常驻内存的具有特权的共享库,是低级硬件处理例程。 设备等同文件处理,每个设备文件有两个设备号
主设备号:标识驱动程序 从设备号:表示使用同一个设备驱动程序的不同硬件设备。 设备驱动程序工作的基本原理 用户进程利用系统调用对设备进行诸如read/write操作,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。
50
添加设备驱动程序(续) 设备驱动程序的功能 对设备初始化和释放; 把数据从内核传送到硬件和从硬件读取数据;
读取应用程序传输给设备文件的数据和回送应用程序请求的数据; 检测和处理设备出现的错误。
51
添加设备驱动程序(续) Linux系统采用一组固定的入口点来实现驱动设备的功能。
open入口点: 打开设备准备I/O操作。open子程序必须对将要进行的I/O操作做好必要的准备工作,如清除缓冲区等。 close入口点: 关闭一个设备。当最后一次使用设备终结后,调用close子程序。 read入口点: 从设备上读数据。 write入口点: 往设备上写数据。 ioctl入口点: 执行读、写之外的操作。 select入口点: 检查设备,看数据是否可读或设备是否可用于写数据。 如果设备驱动程序没有提供上述入口点中的某一个,系统会用缺省的子程序来代替。对于不同的系统,也还有一些其它的入口点。
52
添加设备驱动程序(续) 入口点采用如下数据结构实现: int (*flush) (struct file *);
int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); } #include</usr/src/linux/include/linux/fs.h> struct file_operations { struct module *owner; loff_t (*llseek) (struct file *,loff_t ,int); ssize_t (*read) (struct file *,char *,size_t,loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); 这一页比较繁琐,可讲可不讲。
53
添加设备驱动程序(续) 注册设备:向系统登记设备及驱动程序的入口点
int register_chrdev (unsigned int major, const char *name, struct file_operations *fops); //向系统的字符设备表登记一个字符设备 //major:希望获得的设备号,为0时系统选择一个没有被占用的设备号返回。 //name:设备名 //fops:登记驱动程序实际执行操作的函数的指针 //登记成功,返回设备的主设备号,否则,返回一个负值 int register_blkdev (unsigned int major, const char *name, struct file_operations *fops); //向系统的块设备表登记一个块设备
54
添加设备驱动程序(续) 设备卸载 int unregister_chrdev (unsigned int major, const char *name); //卸载字符设备 //major:要卸载设备的主设备号 //name:设备名 int unregister_blkdev (unsigned int major, const char *name); //卸载块设备
55
典型驱动程序的结构 // 在注销模块中卸载设备 void cleanup_module(void) { unregister_chrdev(test_major, "test"); } // 驱动子程序 static int open(struct inode *inode, struct file *file ) { …… } static void release(struct inode *inode, { …… } …… //将系统调用和驱动程序关联起来 struct file_operations *test_fops= .open; .release; }; // 在初始化模块中注册设备 int init_module(void) result = register_chrdev(0, "test", &test_fops); 字符设备提供的主要入口有:open ()、release ()、read ()、write ()、ioctl ()、llseek()、poll()等。
56
添加设备驱动程序(续) 添加设备驱动程序的方法 mknod /dev/test c 254 0 编写设备驱动程序mydev.c
在模块的初始化init_module()中调用设备注册函数; 在模块的卸载cleanup_module()中调用设备的卸载函数。 设备驱动模块的编译 gcc -O2 -DMODULE -D__KERNEL__ -I/usr/src/linux /include -c mydev.c 加载设备驱动模块: insmod –f mydev.o 若加载成功,在文件/proc/devices中能看到新增加的设备,包括设备名mydev和主设备号。 生成设备文件 mknod /dev/test c 254 0 //其中,test为设备文件名,254为主设备号,0为从设备号,c表示字符设备
57
添加设备驱动程序(续) 编写应用程序,测试驱动程序 编译 gcc hello.c –o hello
if ( testdev == -1 ) { printf("Cann't open file \n"); exit(0); } read(testdev,buf,10); for (i = 0; i < 10;i++) printf("%d\n",buf[i]); close(testdev); #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() int testdev; int i; char buf[10]; testdev = open("/dev/test",O_RDWR);
58
添加设备驱动程序(续)
59
添加设备驱动程序(续) .o的文件是object文件,.ko是kernel object,与.o的区别在于其多了一些sections, 比如.modinfo。 在2.4内核中,生成的就是.o文件,2.6 kernel对内核模块的管理做了一些扩展,从此生成的就是.ko文件。
60
课程设计辅导 Linux系统的相关知识 进程并发 添加系统调用 添加设备驱动程序 /proc文件分析
61
/proc文件分析 /proc PROC文件系统是进程文件系统和内核文件系统的组成的复合体,是将内核数据对象化为文件形式进行存取的一种内存文件系统, 是监控内核的一种用户接口. 它拥有一些特殊的文件(纯文本),从中可以获取系统状态信息。 系统信息 与进程无关,随系统配置的不同而不同。 命令procinfo可以显示这些文件的大量信息。 进程信息 系统中正在运行的每一个用户级进程的信息。
62
/proc文件分析 系统信息 进程信息 /proc/cmd/line: 内核启动的命令行 /proc/cpuinfo: CPU信息
/proc/stat: CPU的使用情况、磁盘、页面、交换、所有的中断、最后一次的启动时间等。 /proc/meminfo: 内存状态的有关信息。 进程信息 /proc/$pid/stat /proc/$pid/status /proc/$pid/statm ……etc
63
/proc文件分析 监控系统功能 /proc文件系统的详细信息通过以下命令获取:
使用 GTK+ Linux下的c语言开发。 具体包括: 主机名、系统启动时间、系统运行时间、版本号、所有进程信息、CPU类型、CPU的使用率、内存使用率…… ----参照WINDOWS的任务管理器,实现其中的部分功能。 /proc文件系统的详细信息通过以下命令获取: man proc
64
/proc文件分析
65
/proc文件分析
66
模拟文件系统 实现方式 以一个独立运行的程序形式显现,运行程序后以命令的形式实现文件操作接口 以库的形式提供,可供用户编写程序调用 问题
文件系统:目录、文件 磁盘空间的管理:直接管理原始磁盘空间,用文件模拟 文件结构 文件操作接口 创建、删除、打开、关闭、读、写
67
模拟文件系统 磁盘空间管理 超级块 空闲空间管理:链表、成组连接 文件组织结构 目录结构 根目录
68
模拟文件系统 文件物理结构 链接、索引 文件操作接口
69
模拟文件系统
70
模拟文件系统
71
模拟文件系统
72
模拟文件系统
73
模拟文件系统
74
模拟文件系统
75
课程设计辅导——参考资料 Linux的“man”帮助 书籍 网站 《边干边学——Linux内核指导》李善平,陈文智等编著.浙江大学出版社.
《Linux Device Driver 2》 《Linux内核编程》 网站
Similar presentations