1)进程控制• 进程创建:要求学生使用fork()系统调用创建新的进程,分配进程控制块(PCB),初始化进程的相关信息,如进程标识符、状态、优先级等,并将新进程插入到就绪队列中。• 进程撤销:要求学生使用wait()或waitpid()系统调用回收进程占用的资源,包括内存、文件描述符等,并将进程从系统中移除。
时间: 2025-08-06 21:21:17 AIGC 浏览: 17
<think>我们面对的问题是如何正确使用fork()创建新进程以及使用wait()/waitpid()回收子进程。下面我将按照步骤详细解释。###1.使用fork()创建新进程fork()系统调用会创建一个新进程(子进程),该进程是调用进程(父进程)的副本。创建后,父子进程从fork()返回处开始并发执行。**关键点:**-父进程中:fork()返回子进程的PID(正整数)-子进程中:fork()返回0-出错时:返回-1,不创建子进程**初始化相关信息**:子进程会继承父进程的大部分属性,但有时需要显式初始化(如修改某些变量、重置状态等)。通常在子进程分支(pid==0)内进行初始化操作。示例代码:```c#include<stdio.h>#include<unistd.h>//提供fork(),getpid(),getppid()#include<sys/wait.h>//提供wait(),waitpid()#include<stdlib.h>//提供exit()intmain(){pid_tpid;printf("Parentprocess(PID=%d)starts\n",getpid());pid=fork();//创建子进程if(pid==-1){perror("forkfailed");exit(EXIT_FAILURE);}if(pid==0){//子进程分支printf("Childprocess(PID=%d)created,parentPID=%d\n",getpid(),getppid());//子进程初始化操作:例如执行新程序或执行特定任务//这里简单示例:子进程睡眠2秒后退出sleep(2);printf("Childprocess(PID=%d)terminates\n",getpid());exit(EXIT_SUCCESS);//子进程退出}else{//父进程分支printf("Parentprocess(PID=%d)createdchild(PID=%d)\n",getpid(),pid);//父进程可以继续执行其他任务,然后等待子进程结束}//此处代码父子进程都会执行到?注意:上面子进程分支有exit(),所以子进程不会执行到这里//而父进程会继续执行下面的代码return0;}```###2.使用wait()或waitpid()回收子进程当子进程终止时,内核会保留其退出状态(称为“僵尸进程”)直到父进程获取该状态。使用wait()或waitpid()可以:-回收子进程资源(移除僵尸进程)-获取子进程退出状态####wait()函数-阻塞等待任意一个子进程终止-函数原型:`pid_twait(int*status);`-参数:`status`指针,用于存储子进程退出状态(可用宏解析)-返回值:成功时返回终止子进程的PID;出错返回-1####waitpid()函数-可以指定等待哪个子进程,以及是否阻塞-函数原型:`pid_twaitpid(pid_tpid,int*status,intoptions);`-参数:-`pid`:指定等待的子进程PID,若为-1则等待任意子进程(类似wait)-`status`:同wait()-`options`:常用选项:-`0`:阻塞等待-`WNOHANG`:非阻塞,即立即返回,即使子进程尚未结束-返回值:-正常:返回终止子进程的PID-若使用WNOHANG且无子进程终止:返回0-出错:-1####在父进程中添加等待逻辑修改上面父进程分支后的代码:```c//...(前面代码同上面示例)}else{printf("Parentprocess(PID=%d)createdchild(PID=%d)\n",getpid(),pid);intstatus;pid_tchild_pid=waitpid(pid,&status,0);//阻塞等待指定子进程if(child_pid==-1){perror("waitpidfailed");exit(EXIT_FAILURE);}if(WIFEXITED(status)){printf("Parent:child(PID=%d)exitedwithstatus%d\n",child_pid,WEXITSTATUS(status));}elseif(WIFSIGNALED(status)){printf("Parent:child(PID=%d)killedbysignal%d\n",child_pid,WTERMSIG(status));}}printf("Parentprocess(PID=%d)ends\n",getpid());return0;```###3.完整示例代码结合上述步骤,完整代码如下:```c#include<stdio.h>#include<unistd.h>#include<sys/wait.h>#include<stdlib.h>intmain(){pid_tpid;printf("Parentprocess(PID=%d)starts\n",getpid());//创建子进程pid=fork();if(pid==-1){perror("forkfailed");exit(EXIT_FAILURE);}if(pid==0){//子进程printf("Childprocess(PID=%d)created,parentPID=%d\n",getpid(),getppid());sleep(2);//模拟子进程工作printf("Childprocess(PID=%d)terminates\n",getpid());exit(42);//子进程退出,返回状态42(注意:退出状态低8位有效,范围0-255)}else{//父进程printf("Parentprocess(PID=%d)createdchild(PID=%d)\n",getpid(),pid);intstatus;pid_tchild_pid=waitpid(pid,&status,0);//阻塞等待子进程if(child_pid==-1){perror("waitpidfailed");exit(EXIT_FAILURE);}if(WIFEXITED(status)){printf("Parent:child(PID=%d)exitednormallywithstatus%d\n",child_pid,WEXITSTATUS(status));//输出42}elseif(WIFSIGNALED(status)){printf("Parent:child(PID=%d)killedbysignal%d\n",child_pid,WTERMSIG(status));}}printf("Parentprocess(PID=%d)ends\n",getpid());return0;}```###运行结果分析运行该程序,输出将类似于:```Parentprocess(PID=1234)startsParentprocess(PID=1234)createdchild(PID=1235)Childprocess(PID=1235)created,parentPID=1234Childprocess(PID=1235)terminatesParent:child(PID=1235)exitednormallywithstatus42Parentprocess(PID=1234)ends```###关键知识点1.**fork()**:创建子进程,父子进程共享代码段,数据段、堆栈段采用写时复制(Copy-On-Write)[^4]。2.**exit()**:子进程通过exit()结束,退出状态由父进程获取。3.**waitpid()**:父进程通过waitpid()等待子进程结束,并获取退出状态(避免僵尸进程)。4.**状态解析宏**:-`WIFEXITED(status)`:子进程正常退出(调用exit或return)时为真-`WEXITSTATUS(status)`:获取子进程退出状态(即exit()的参数的低8位)-`WIFSIGNALED(status)`:子进程被信号终止时为真-`WTERMSIG(status)`:导致子进程终止的信号编号§§相关问题§§1.fork()之后父子进程的文件描述符是如何继承的?2.如何处理多个子进程的回收(使用循环等待)?3.wait()和waitpid()的主要区别是什么?4.什么是僵尸进程?如何避免僵尸进程的产生?</think>
### 🖥 使用 `fork()` 创建进程与 `wait()/waitpid()` 回收进程的实现步骤
#### 1. **`fork()` 创建新进程**
`fork()` 是 Linux 中的系统调用,用于创建子进程作为父进程的副本。创建后父子进程从相同位置继续执行,通过返回值区分角色:
```c
#include <stdio.h>
#include <unistd.h> // 包含 fork()
#include <sys/types.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid == -1) { // 创建失败
perror("fork");
return 1;
} else if (pid == 0) { // 子进程分支
printf("子进程 PID=%d (父进程 PID=%d)\n", getpid(), getppid());
// 初始化子进程独有信息(如重置计数器、加载新任务等)
// ...
_exit(0); // 子进程结束
} else { // 父进程分支
printf("父进程 PID=%d (子进程 PID=%d)\n", getpid(), pid);
// 父进程继续执行其他任务
}
return 0;
}
```
**初始化的关键操作**:
- 子进程中调用 `exec()` 系列函数加载新程序[^4]
- 重新配置信号处理(`signal()` 或 `sigaction()`)
- 修改环境变量或资源限制(`setenv()`, `setrlimit()`)
#### 2. **`wait()/waitpid()` 回收进程**
当子进程终止后,父进程需回收其资源,避免僵尸进程(Zombie Process):
```c
#include <sys/wait.h> // 包含 wait()/waitpid()
// 在父进程中添加等待逻辑
int status;
pid_t child_pid = waitpid(pid, &status, 0); // 阻塞等待特定子进程
if (child_pid == -1) {
perror("waitpid");
} else {
if (WIFEXITED(status)) { // 正常退出
printf("子进程 %d 退出码: %d\n", child_pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) { // 信号终止
printf("子进程 %d 被信号 %d 终止\n", child_pid, WTERMSIG(status));
}
}
```
#### 3. **完整实现示例**
```c
#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 启动(将执行睡眠2秒)\n", getpid());
sleep(2); // 模拟任务执行
printf("[子进程] PID=%d 结束\n", getpid());
exit(42); // 退出码42
} else if (pid > 0) { // 父进程
printf("[父进程] PID=%d 等待子进程 %d\n", getpid(), pid);
int status;
waitpid(pid, &status, 0); // 阻塞等待
if (WIFEXITED(status)) {
printf("[父进程] 子进程退出码: %d\n", WEXITSTATUS(status));
}
} else {
perror("fork");
return 1;
}
return 0;
}
```
### 🔍 关键机制说明
1. **写时复制(Copy-On-Write)**
子进程创建时共享父进程内存空间,仅当修改数据时才复制物理内存[^4],显著降低创建开销。
2. **进程资源继承**
子进程继承父进程的:
- 打开的文件描述符
- 信号处理设置
- 工作目录
- 用户/组ID
3. **僵尸进程避免**
若父进程未调用 `wait()`:
- 子进程退出后保留退出状态(僵尸进程)
- 占用系统进程表资源
- 解决方案:父进程必须调用 `wait()/waitpid()` 或忽略 `SIGCHLD` 信号
### 🌟 使用场景建议
| 场景 | 推荐方式 | 说明 |
|---------------------|-------------------|--------------------------|
| 等待特定子进程结束 | `waitpid(pid,...)` | 精确控制回收目标 |
| 等待任意子进程结束 | `wait()` | 适用于单子进程场景 |
| 非阻塞回收 | `waitpid(-1, &status, WNOHANG)` | 轮询检查子进程状态 |
> **注**:多子进程场景中需循环调用 `waitpid(-1, ...)` 直至返回 `ECHILD`[^3]。
阅读全文
相关推荐




















