Linux程设复习提纲(三)
Chapter3-1(续)
系统调用(掌握)和库函数(当题目指定系统调用时不能使用)
系统调用和库函数都以C函数的形式出现
系统调用
- Linux内核的对外接口
- 用户程序和内核之间唯一的接口
- 提供最小接口
库函数
- 依赖于系统调用
- 提供复杂功能(例如:标准I/O库)
以I/O作为样例
- 非缓存IO
- 读写操作直接调用系统调用
- 文件描述符
- Not in ANSI C, but in POSIX.1 and XPG3
- 缓存IO
- 使用标准IO库
- 处理很多细节,如缓存分配以及优化长度执行IO
- 流是一个文件中的指针
系统IO调用
文件描述符
一个非负的整形(如在
一般的文件操作的步骤:打开,读/写,(lseek),关闭
基本的IO函数
先给一个文件的最简单的读写样例程序
1 | /* a rudimentary example program */ |
open/creat方法
用于打开或者创建一个文件或者设备
1 |
|
flags参数:文件的访问模式
- O_RDONLY:只读操作
- O_WRONLY:只写操作
- O_RDWR:读写操作
- O_APPEND:文件以追加(append)模式打开
- O_TRUNC:如果文件已经存在且是一个常规文件且允许以写模式打开则截断该文件长度为0(清空文件?)
- O_CREAT:如果文件不存在则被创建
- O_EXCL:和O_CREAT一起使用,如果文件已经存在则是一个错误且打开文件失败。
creat方法等价于以(O_CREAT|O_WRONLY|O_TRUNC)作为flag使用open方法
mode参数:指定创建新文件的权限,列表如下:
新建文件权限由mode和umask共同决定,如图
close方法
1 |
|
- 主要系统调用要求掌握,指定系统调用的题不能用C库
- 进程相关的系统调用不要求掌握
- LCTL不考
- C库简答题可能要考
- 权限要掌握
- 文件锁要求掌握,扩展的文件锁不要求掌握
- 锁的标志位不要求掌握
- 锁的系统调用要求掌握
read/write函数
- 从一个文件描述符进行读取
-
1
2
3
ssize_t read(int fd, void *buf, size_t count);
(返回值: 读到的字节数,若已到文件尾为0,若出错为-1)1
2
3
4
5
6
7
- 写到一个文件描述符
```c
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
(返回值: 若成功为已写的字节数,若出错为-1)
lseek 函数
用于重新计算读写的偏移量
用法:
1 |
|
关于whence参数
SEEK_SET: the offset is set to “offset” bytes,这个表示把文件指针放在你设置的偏移量的位置。
SEEK_CUR: the offset is set to its current location plus “offset” bytes,这个表示文件指针的位置设置在当前位置加上你设置的offset值的位置。
SEEK_END: the offset is set to the size of the file plus “offset” bytes,这个表示文件指针的位置是文件末尾的位置加上设置的offset值的和的位置。
dup/dup2函数
复制一个文件描述符,返回一个新的文件描述符,指向同一个文件
1 |
|
fcntl函数
对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性。
1 |
|
cmd参数
- F_DUPFD:和dup函数相同
- F_GETFD/F_SETFD:get/set文件的close-on-exec标志
- F_GETFL/F_SETFL:get/set文件打开方式的标识
- F_GETOWN/F_SETOWN:get/set文件的IO能力标识
- F_GETLK/F_SETLK/F_SETLKW:获得,设置文件锁(封锁,解封),第二个在设置时如果失败会导致直接结束并返回,第三个是会阻塞自身等待文件解锁
ioctl函数
控制设备的函数
1 |
|
标准I/O库
文件锁
当几个进程同时操作文件时起到作用
记录锁
劝告锁
检查,加锁由应用程序自己控制
强制锁
检查,加锁由内核控制
影响open,read,write等函数
共享锁
排他锁
fcntl设置锁
上文系统调用中的fcntl已经有cmd参数的详细介绍,这里不再做赘述,主要讲一下struct flock的使用
1 |
|
1 | struct flock{ |
Chapter 4
内核概念
操作系统是一系列程序的集合,其中最重要的部分构成了内核。
单内核/微内核
- 单内核是一个很大的进程,内部可以分为若干模块,运行时是一个独立的二进制文件,模块间通讯通过直接调用函数实现
- 微内核中大部分内核作为独立的进程在特权下运行,通过消息传递进行通讯
Linux内核的能力
- 内存管理,文件系统,进程管理,多线程支持,抢占式,多处理支持
Linux内核区别于其他UNIX商业内核的优点
- 单内核,模块支持
- 免费/开源
- 支持多种CPU,硬件支持能力非常强大
- Linux开发者都是非常出色的程序员
- 通过学习Linux内核的源码可以了解现代操作系统的实现原理
驱动
常见的驱动代码集成在内核源码中
也有第三方开发的驱动可以单独编译为模块.ko文件
编译需要内核头文件的支持
加载模块
- 底层命令
- insmod
- rmmod
- 高层命令
- modprobe
- modprobe -r
模块依赖
一个模块A应用另一个模块B所导出的符号我们就说模块B被模块A引用,如果要装载A,则必须要线装载B,否则,模块B所导出的那些符号的应用就不可能被链接到A中,这就叫模块的依赖
系统可以实现自动按需加载以及自动按需卸载
moddep,ismod,modinfo
模块之间的通讯
模块是为了完成某种特定任务而设计的,功能单一,为了丰富系统的功能,所以模块之间常常需要通信,可以共享变量,数据结构,调用对方的功能函数
模块的命令
- insmod
[module parameters] - 用于装载模块
- 注意:只有超级用户能够使用这个命令
- rmmod
- 卸载模块
- lsmod
- 查看内核中已经装载的所有模块
- 这个命令和cat /proc/modules等价
- modprobe [-r] \
- 装载一个模块以及这个模块依赖的所有模块
Linux内核模块和应用程序的区别
C语言程序 | Linux内核模块 | |
---|---|---|
运行 | 用户空间 | 内核空间 |
入口 | main | module_init()指定 |
出口 | 无 | module_exit()指定 |
运行 | 直接运行 | insmod |
调试 | gdb | kdbug,kdb,kgdb |
内核模块不能使用C库进行开发
没有内存保护机制
小内核栈
需要考虑并发
一个简单的内核模块的代码示例
1 |
|
内核程序和用户态程序区别
内核态和用户态是处理器的两个状态
当发生中断或者系统调用时暂停正在运行的进程,把处理器状态从用户态转到内核态,执行操作系统服务例程。这就是一次状态转换,此时进程仍然在自己的上下文中执行,仅处理器状态发生了变化,内核在被中断的进程的上下文中进行处理。
在大多数情况下处理器状态的转换不会影响到上下文。
读懂内核程序代码
实在过于硬核,代码长达5页pdf,可以自行学习,在最后一个PDF的第25页开始
字符型驱动程序概念
Linux系统将设备分为三种类型:字符设备、块设备和网络接口设备
文件操作是字符设备驱动的对上接口
两个基本的结构体分别是:file结构体和inode结构体
驱动程序的初始化加载过程是
- 申请设备号
- 定义文件操作结构体file_operations
- 创建并初始化定义结构体cdev
- 将cdev注册到系统,并和对应的设备号绑定
- 在/dev文件系统中用mknod创建设备文件,并将该文件绑定到设备号上
每个字符设备或者是块设备都有主设备号和次设备号,主设备号表示一个特定的驱动程序,次设备号用来表示使用该驱动程序的各设备。
注:申请设备号之后的内容过于繁琐,个人认为不考,可以从参考pdf