Linux的fork知识点整理及文件描述符

张开发
2026/5/19 9:39:39 15 分钟阅读
Linux的fork知识点整理及文件描述符
一、fork 核心知识点整理1. 进程定义进程是一个正在运行的程序在Linux系统中通过结构体struct task_struct 来描述进程属性进程的状态有就绪运行、阻塞2. fork工作原理为子进程申请新PID复制父进程PCB进程控制块给子进程修改子进程PCB中的PID为新申请的值复制父进程所有资源给子进程父子进程并发执行完整复制父进程的地址空间逻辑地址、代码、数据等进程实体3. 返回值差异父进程中fork返回子进程PID(0),子进程中fork返回0返回-1表示fork返回失败通过 fork() 返回值设置不同的循环次数实现并区分执行逻辑4. 执行起点子进程从 fork() 返回处开始执行不会重复执行 fork() 之前的代码5. 子进程和父进程示例1#includestdio.h #includestdlib.h #includeunistd.h #includestring.h int main() { int n 0; char* s NULL; pid_t pid fork(); if(pid -1) { exit(1); } if(pid 0) { n 3; s child; } else { n 7; s parent; } for(int i 0;i n;i) { printf(s %s pid%d,ppid%d\n,s,getpid(),getppid()); sleep(1); } exit(0);//用exit(0);代替return 0;结束进程 } //父进程循环 7 次输出 parent 子进程循环 3 次输出 child输出示例s parent s child s child s parent s child s parent s parent s parent s parent s parent 终端提示符2#includestdio.h #includestdlib.h #includeunistd.h #includestring.h int main() { int n 0; char* s NULL; pid_t pid fork(); if(pid -1) { exit(1); } if(pid 0) { n 7; s child; } else { n 3; s parent; } for(int i 0;i n;i) { printf(s %s pid%d,ppid%d\n,s,getpid(),getppid()); sleep(1); } exit(0); } //父进程循环 3 次输出 parent 子进程循环 7 次输出 child输出示例s parent s child s child s parent s parent s child s child 终端提示符 s child s child s child执行特点不确定性父进程与子进程的执行顺序由操作系统调度决定输出顺序可能交替出现终端提示符行为父进程结束后立即显示提示符子进程继续执行不影响提示符显示若子进程先结束无特殊表现只管父进程不管子进程独立运行共享代码单数据独立每个进程拥有独立的变量副本修改互不干扰写时复制机制只有在修改时才会复制变量副本6. 获取当前进程 ID1PID获取方法getpid()PID进程 ID唯一标识一个进程函数原型 pid_t getpid(void);返回值 pid_t 类型本质是 int 返回调用进程的 PID功能获取当前进程的进程 ID常用于生成唯一临时文件名等场景库依赖标准 C 库 #include unistd.h 2 PPID获取方法getppid()PPID父进程 ID标识创建当前进程的父进程函数原型 pid_t getppid(void);功能返回父进程 ID创建当前进程的进程 ID每个进程除0号进程都有父进程PID0的进程由系统创建特殊情况若父进程已终止返回 init 或 systemd 等进程的 PID孤儿进程被收养进程树 pstree 命令可查看进程家族树 init / systemd 是所有进程的祖先3ID分配机制从1开始顺序递增达到INT_MAX会回收已终止进程的ID早期进程ID小新创建的进程ID大7. 僵死进程1子进程先于父进程结束父进程没有获取子进程的退出码那么子进程变为僵死进程2进程都具有状态就绪、运行、阻塞条件满足进程就可以继续运行、僵死3子进程结束父进程没有wait子进程占用内核空间ID号耗光新进程无法产生自己fork的子进程需要自己wait系统不会自动回收4父进程先结束子进程变成孤儿进程被系统接管子进程结束后由系统进行wait不会变成僵死进程5解决僵死进程父进程调用wait()示例#includestdio.h #includestdlib.h #includeunistd.h #includestring.h #includesys/wait.h int main() { int n 0; char* s NULL; pid_t pid fork(); if(pid -1) { exit(1); } if(pid 0) { n 3; s child; } else { n 7; s parent; int val 0; wait(val);//获取子进程的退出码若子进程没有结束父进程wait就会被阻塞住子进程在3秒以后结束wait才会在3秒以后返回 if(WIFEXITED(val))//判断程序是否正常结束能拿到退出码 { printf(val %d\n,WEXITSTATUS(val));//获取退出码 } //printf(val %d\n,val); //printf(val %d\n,val8);右移8个位 } for(int i 0;i n;i) { printf(s %s pid%d,ppid%d\n,s,getpid(),getppid()); sleep(1); } exit(3);//退出码默认一般为0此处改为3子进程结束之后执行此行代码将退出码的值换为3退出码一般为一个字节8个bitval为int类型占四个字节退出码只占其中的一个字节还有一个字节存放程序是否正常结束其他的字节表示其他内容 }输出示例s child s child s child val 3 //val 768 //0011 0000 0000不在最低的字节存放不是最低位的第一个字节 //val 3 右移之后正常输出为3 s parent s parent s parent s parent s parent s parent s parent6#includestdio.h #includestdlib.h #includeunistd.h #includestring.h int main() { int n 0; char* s NULL; pid_t pid fork(); if(pid -1) { exit(1); } if(pid 0) { n 3; s child; } else { n 7; s parent; } for(int i 0;i n;i) { printf(s %s pid%d,ppid%dn%p,n%d\n,s,getpid(),getppid(),n,n); sleep(1); } exit(0); }输出示例Pasted image 20260408174634.png此处看到的n的地址并不是物理地址而是逻辑地址(距离起始地址的偏移量对于同一个程序逻辑地址相同那么所对应的物理地址一定是相同的但对于不同的程序逻辑地址相同它所对应的物理地址不一定相同所以此处n的值不同程序中打印、调试看到的地址都是逻辑地址nm 目标文件(.o)、库文件(.a/.so)显示逻辑地址8. fork写时拷贝对用户是透明的可以推迟甚至免除拷贝数据的技术内核此时并不复制整个进程地址空间而是让父进程和子进程共享同一个拷贝只有在需要写入时数据不论是父进程还是子进程才会被复制从而使各个进程拥有各自的拷贝在此之前只是以只读的方式共享可以提高fork复制效率减少内存空间的需求二、文件描述符1.内核中的函数open read write close都是系统调用编号为2编写内核代码时实现的功能留给用户可以调用产生中断陷入内核切换到内核态如c语言中访问文件的方法 库函数fopen() 其本质就是通过系统调用open()fclose()2.对文件的操作方法打开文件读、写关闭文件3.示例#includestdio.h #includestdlib.h #includeunistd.h #includestring.h #includefcntl.h int main() { /* int fd open(./file.txt,O_WRONLY|O_CREAT,0600);//./可以不写默认当前位置该文件不存在要创建O_CREAT若文件已存在会自动忽略O_CREAT只读O_RDONLY 读写O_RDWR按位或在一起每一个标志对应一个二进制位这个位置置1表示该操作有被设置权限0600属主自己可读可写8进制 if(fd -1) { printf(open failed\n); exit(1); } write(fd,hello,5);//5为写入的字符数 close(fd); */ int fd open(file.txt,O_RDONLY);//文件已存在权限已经确定下来就不用再给出 if(fd -1) { printf(open failed\n); exit(1); } char buff[128]{0}; read(fd,buff,127);//读取fd文件中的数据存到buff中期望读取127个具体能读多少取决于fd文件中的数据量 printf(buff%s\n,buff); close(fd); exit(0); }4.fd表示正在打开的文件 0为键盘 1为屏幕文件打开之后会有一张文件表记录打开的文件程序启动默认打开前三个文件0stdin标准输入1stdout标准输出2stderr标准错误输出3file.txt...

更多文章