操作系统编程
open
进程打开文件的内核数据结构
task_struct—->file_struct——->file———->dentry———>inode(索引节点号,文件信息)
task_struct: 进程控制块
file_struct: 该结构体包含了进程已经打开的文件表
file:代表一个已经打开的文件
dentry:文件相关目录项
create
返回只写打开的文件描述符
lseek
修改当前文件的偏移量,打开一个文件时,偏移量为0,只修改内核中的记录
1 | off_t CurrentPosition=off_t lseek(int filedes,off_t offset,int whence); |
filedes: 文件描述符
offset:相对偏移量,需要结合whence才能算出真的偏移量
off_t: 在32位机是32位,在64位机是64位
whence:常见三个参数
- SEEK_SET: 当前文件偏移量为:距离文件开始处的offset个字节
- SEEK_CUR: 距离当前文件偏移量+offset
- SEEK_END:当前文件长度+offset
空洞地方是0
静态库
静态库打包
1 | g++ -c a1.cpp a2.cpp |
nm命令查看符号信息
1 | g++ statictest.cpp libtest.a |
动态库 so shared object
1 | g++ -fpic -shared -o libtest.so a1.cpp a2.cpp |
$ -fpic $ 位置无关代码
1 |
|
1 | // 映射动态链接库里的函数 |
库的编写注意事项
导出函数的名称
函数调用的约束
结构体对齐
谁分配谁释放
函数导出名
使用 extern “C” 导出的函数函数名不会改变
extern “C”:告诉编译器按照C语言的方式设定函数的导出名
接口用 extern “C”
extern
在头文件里面定义一个变量,被多个源文件包含之后,会有重定义错误。
1 | // a.h |
结构体大小
1 | struct A{ |
4个字节大小
1 | struct A{ |
缓冲
全缓存
fclose、fflush、程序结束、全缓存满了会刷新到文件里面。
行缓存
终端(标准输入输出),使用行缓存。
遇到 “\n” 、执行fflush、程序结束、行缓存满
1 | int fflush(FILE* stream); |
进程控制
fork函数
1 | pid_t fork(void); |
一次调用,返回两次(自然返回;当子进程返回时再次返回)。
子进程复制父进程的fd(已经打开文件表),复制mm_struct(pgd表),复制父进程地址空间,复制text段。子进程复制父进程地址空间,把页框全部复制过去(新的一份,不同的物理地址),让映射关系保持一致(映射到我们的新复制的那一份)。
- 子进程获得父进程数据空间、堆和栈的副本
- 父子进程不共享存储空间
- 父子进程共享正文段
用COW,不立刻复制页框,仅仅复制需要修改的地方。(一旦读写就产生缺页,就全复制过去)
vfork函数
创建新的进程,执行一个新的程序(可执行文件)。
子进程子啊调用exec或exit之前,在父进程地址空间中运行
exit函数
当父进程调用wait函数,内核将释放终止进程所使用的所有内存,关闭打开的文件
已经终止但是没有父进程没调用wait,就是僵尸进程。
当一个进程正常获知异常终止时,内核给父进程发送SIGCHLD信号
wait函数
1 | pid_t wait(int *staloc); |
waitpid等待指定子进程返回
exec函数
用可执行文件来替换当前地址空间的一切
execl函数成功不返回值(原来的程序都被替换掉了)
execve系统调用,其他都是库函数
execlp只给出了文件名,路径从环境变量中寻找
l表示list;v表示vector;p表示用路径前缀