内容
设计系统调用,将系统的相关信息(CPU型号、操作系统的版本号、系统中的进程等类似于Windows的任务管理器的信息)以文本形式列表显示于屏幕,并编写用户程序予以验证。
思想
- 系统调用是应用程序和操作系统内核之间的功能接口,可以使用户使用操作系统提供的有关设备管理、输入输出系统、文件系统和进程控制、通信以及存储管理等方面的功能,不必了解系统程序的内部结构和有关硬件细节,从而减轻用户负担、保护系统以及提高系统资源利用率的作用。
- 模块是在内核空间运行的程序,实际上是一种目标文件,不能单独运行但其代码可在运行时链接到系统中作为内核的一部分运行或卸载。Linux内核模块是一个编译好的、具有特定格式的独立目标文件,用户可以通过系统提供的一组与模块相关的命令将模块加载进内核,当内核模块被加载后,有如下特点:
- 与内核一起运行在相同的内核态和内核地址空间;
- 运行时与内核具有同样的特权级;
- 可方便地访问内核中的各种数据结构。
- 内核模块还可以很容易地被移出内核,当用户不再需要某模块功能时,可以从内核卸载以节省系统主存开销。
- 用户修改代码后,只需重新编译加载模块,不必重新编译内核和引导系统。
基础知识
- 内核模块编程与用户态编程的区别:
- 内核模块编程不能使用C函数库,内核模块只能使用一些内核函数。比如,输出信息时使用内核函数
printk
,而不是标准库函数printf
。 - 内核模块代码运行在核心态,这意味着函数使用的栈是核心栈,这个空间非常有限,一般是4KB或者8KB,所以不能定义占用很大空间的自动变量。
- 内核代码为了节省开销,不能使用浮点运算。
- 典型内核模块组成:
- 头文件声明。其中
module.h
和init.h
是必不可少的。Module.h
包含加载模块时需要的函数和符号定义;init.h
包含模块初始化和清理函数的定义。如果在加载时允许用户传递参数,模块中还应包含moduleparam.h
头文件。 - 模块许可声明。从内核2.4.10版本开始,模块必须通过
MODULE_LICENSE
宏声明此模块的许可证,否则在加载模块时会显示"kernel tainted(内核被污染)"的警告信息。从linux/module.h
文件中可以看到,被内核接受的许可证有GPL、GPL v2、GPL and additional rights、Dual BSD/GPL、Dual MPL/GPL、Dual MIT/GPL和Proprietaty。 - 初始化和清理函数声明。
内核模块必须调用宏module_init
和module_exit
去注册初始化和清理函数。初始化和清理函数必须在宏module_init
和module_exit
使用前定义,否则会出现编译错误。这两个函数配对使用,例如当初始化函数申请了一个资源,那么清理函数就应该释放这个资源,使得模块不留下任何副作用。除了模块初始化函数和清理函数,还可以根据需要设计编写其它函数。
- proc文件系统
在linux操作系统中,提供了一套在用户态检查内核状态和系统特征的机制,就是进程文件系统(process
file system)。
Proc文件系统将进程信息、系统的硬件信息(包括CPU、内存状态及网卡等各种硬件设备)、系统相关机制(中断、I/O)等内容全部映射为虚拟的linux文件。它以一种特殊的文件系统的方式,为访问系统内核数据的操作提供接口。这些文件的内容都不存在于任何存储设备上,而是在读/写的时候才根据系统中的有关信息生成出来,或者映射到系统中的有关变量或者数据结构。
系统中当前运行的每一个每一个进程都有一个对应的目录在/proc下,以进程的ID为目录名:
除了与进程有关的子目录,/proc还有一些内核信息的目录:
fork()
fork()
系统调用用来创建一个子进程, 为了避免多余开销,
创建子进程时并不会立即将父进程的内存页面拷贝一份, Linux
kernel将所有内存页面标记为只读并由父子进程共享,
只有当需要写入这些页面时才进行拷贝, 即所谓的Copy-on-Write.