目录前言一、僵死进程(僵尸进程)1. 什么是僵死进程2.进程状态转换示例3.僵死进程的产生原因4.查看僵死进程5.如何处理僵死进程方法一使用wait()系列函数方法二使用信号处理方法三双重fork孤儿进程法二、Linux文件操作详解1. 文件描述符2. 基本文件操作函数3.文件操作标志三、总结僵死进程关键点文件操作关键点前言本文主要讨论了Linux系统中的僵死进程问题和文件操作机制。、关于僵死进程当子进程先于父进程终止且父进程未调用wait()获取其状态时子进程会变为僵死进程占用系统资源。文章介绍了三种处理方法使用wait()函数、信号处理机制和双重fork技术。在文件操作方面详细解释了文件描述符的概念以及open/close/read/write等基本函数的使用方法同时列举了常见的文件操作标志和权限标志。最后总结了僵死进程的危害和解决方案以及文件操作的关键要点强调资源管理和错误检查的重要性。一、僵死进程(僵尸进程)1. 什么是僵死进程僵死进程是指进程已经终止但其父进程尚未调用wait()或waitpid()来获取其终止状态的进程。此时进程的PCB进程控制块仍保留在系统中占用少量资源。当子进程先于父进程结束,父进程没有获取子进程的退出码,此时子进程变成僵死进程。简而言之,就是子进程先结束,并且父进程没有获取它的退出码。那么僵死进程产生的原因或者条件就是:子进程先于父进程结束,并且父进程没有获取子进程的退出码。2.进程状态转换示例// 进程状态转换示例 #include stdio.h #include unistd.h #include sys/wait.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { // 子进程 printf(子进程 PID: %d\n, getpid()); exit(0); // 子进程终止变为僵死进程 } else if (pid 0) { // 父进程 printf(父进程 PID: %d\n, getpid()); sleep(30); // 父进程睡眠此时子进程处于僵死状态 wait(NULL); // 回收子进程僵死进程消失 } return 0; }3.僵死进程的产生原因// 产生僵死进程的代码 #include stdio.h #include unistd.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程运行中... PID: %d\n, getpid()); exit(0); // 子进程退出变为僵尸 } else if (pid 0) { printf(父进程运行中... PID: %d\n, getpid()); printf(子进程变为僵死进程PID: %d\n, pid); sleep(60); // 父进程不调用wait() } return 0; }4.查看僵死进程# 查看系统中的僵死进程ps aux | grep Zps -el | grep Z# 输出示例# USER PID STAT COMMAND# root 1234 Z [process_name] defunct5.如何处理僵死进程方法一使用wait()系列函数#include stdio.h #include unistd.h #include sys/wait.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程退出\n); exit(0); } else if (pid 0) { int status; pid_t ret wait(status); // 阻塞等待子进程退出 if (WIFEXITED(status)) { printf(子进程正常退出退出码: %d\n, WEXITSTATUS(status)); } printf(父进程回收子进程\n); } return 0; }方法二使用信号处理#include stdio.h #include unistd.h #include sys/wait.h #include signal.h #include stdlib.h void sigchld_handler(int signo) { pid_t pid; int status; // 循环回收所有已终止的子进程 while ((pid waitpid(-1, status, WNOHANG)) 0) { printf(回收子进程 PID: %d\n, pid); } } int main() { // 注册SIGCHLD信号处理函数 signal(SIGCHLD, sigchld_handler); pid_t pid fork(); if (pid 0) { printf(子进程运行...\n); sleep(2); exit(0); } else if (pid 0) { printf(父进程继续执行...\n); sleep(10); // 父进程不需要主动wait } return 0; }方法三双重fork孤儿进程法#include stdio.h #include unistd.h #include sys/wait.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { // 第一子进程 pid_t pid2 fork(); // 创建第二子进程 if (pid2 0) { // 第二子进程将被init进程收养 printf(实际工作的子进程 PID: %d\n, getpid()); sleep(10); exit(0); } else if (pid2 0) { // 第一子进程立即退出 exit(0); } } else if (pid 0) { // 父进程回收第一子进程 wait(NULL); printf(父进程继续执行...\n); sleep(15); } return 0; }总结父进程先结束。父进程调用wait()方法获取子进程的退出码。其实这两种处理僵死进程的方法本质都是一样的,都调用了wait获取子进程退出码。方法一是父进程先结束后子进程被Init收养,Init之后调用wait获取子进程退出码。方法二是父进程直接调用wait()。但是两种方法又有区别,就是父进程调用wait会阻塞,等子进程执行完之后,父进程才会执行.若想父进程调用wait不阻塞,那么我们需要结合信号一起。二、Linux文件操作详解1. 文件描述符文件描述符是内核为每个进程维护的打开文件表的索引。0:标准输入1:标准输出2:标准错误输出#include stdio.h #include unistd.h #include fcntl.h #include stdlib.h int main() { // 标准文件描述符 printf(标准输入: %d\n, STDIN_FILENO); // 0 printf(标准输出: %d\n, STDOUT_FILENO); // 1 printf(标准错误: %d\n, STDERR_FILENO); // 2 // 打开文件获取新的文件描述符 int fd open(test.txt, O_CREAT | O_RDWR, 0644); if (fd -1) { perror(open); exit(1); } printf(新文件描述符: %d\n, fd); // 通常是3 close(fd); return 0; }2. 基本文件操作函数open/closeopen的返回值是int,称之为文件描述符。每打开一个文件,我们就会得到一个文件描述符,这个文件描述符是一个整型,通过文件描述符就可以对文件进行读写这样的操作。open失败返回-1,成功返回一个大于等于0的值; 0,1,2是默认打开的。#include fcntl.h #include unistd.h #include stdio.h int main() { // 打开文件 int fd open(example.txt, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd -1) { perror(open); return 1; } // 写入数据 char *data Hello, Linux!\n; ssize_t bytes_written write(fd, data, strlen(data)); printf(写入 %ld 字节\n, bytes_written); // 定位到文件开头 lseek(fd, 0, SEEK_SET); // 读取数据 char buffer[100]; ssize_t bytes_read read(fd, buffer, sizeof(buffer) - 1); buffer[bytes_read] \0; printf(读取内容: %s, buffer); // 关闭文件 close(fd); return 0; }3.文件操作标志printf(文件打开标志:\n);printf(O_RDONLY : 只读模式 (0)\n);printf(O_WRONLY : 只写模式 (1)\n);printf(O_RDWR : 读写模式 (2)\n);printf(O_CREAT : 如果文件不存在则创建\n);printf(O_TRUNC : 如果文件存在则截断\n);printf(O_APPEND : 追加模式\n);printf(O_EXCL : 与O_CREAT一起使用确保文件不存在\n);printf(O_NONBLOCK: 非阻塞模式\n);printf(\n文件权限标志:\n);printf(S_IRUSR : 用户读权限 (0400)\n);printf(S_IWUSR : 用户写权限 (0200)\n);printf(S_IXUSR : 用户执行权限 (0100)\n);printf(S_IRGRP : 组读权限 (0040)\n);printf(S_IWGRP : 组写权限 (0020)\n);printf(S_IXGRP : 组执行权限 (0010)\n);printf(S_IROTH : 其他读权限 (0004)\n);printf(S_IWOTH : 其他写权限 (0002)\n);printf(S_IXOTH : 其他执行权限 (0001)\n);三、总结僵死进程关键点产生原因子进程先于父进程退出父进程未回收危害占用系统资源PCB可能导致资源耗尽解决方法父进程调用wait()/waitpid()使用信号处理SIGCHLD双重fork技术重启父进程文件操作关键点文件描述符进程与文件交互的桥梁基本操作open/read/write/close/lseek高级功能内存映射、文件锁、目录遍历错误处理必须检查每个系统调用的返回值资源管理及时关闭文件描述符防止泄露