system、wait,waitpid,fork使用时注意

##system,waitpid使用时的坑
system这个函数可以用来执行shell命令,这也是我们平常使用比较多,或者说比较喜欢使用的地方,但使用时也有很多坑等着你。

下面这段代码大家应该不难理解,作用是忽略掉SIGCHLD这个信号量。

/* Ignore SIGCHLD to avoid zombie process */
    if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
   
   
        return -1;
    } else {
   
   
        return 0;
    }

你把SIGCHLD忽略掉,你会很理直气壮的说,防止wait/waitpid收尸时出现僵死进程,你说得很对,很合理,僵尸进程是需要有人来处理,否则也是一件很糟糕的事。

可是,在程序的另一个模块里,可能不是你这组的人开发的,是另一组负责开发的,他们想用system函数去执行一些shell命令来为他们处理一些业务逻辑之类的事,比如:
int status = system(“gzip -c /var/opt/abc.txt > /var/opt/abc.zip”);

你会发现status一直不等于0,为什么呢?在回答为什么前,先熟悉下system函数。

  • system函数
    system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.
    system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串

在 Unix/Linux 系统中,`fork`、`wait` 和 `exec` 是进程管理的核心函数,通常结合使用以实现多进程编程。以下是它们的详细说明和典型用法: --- ### 1. **`fork()`:创建子进程** - **功能**:复制当前进程,创建一个子进程(父子进程几乎完全相同)。 - **返回值**: - **父进程**:返回子进程的 PID(>0)。 - **子进程**:返回 0。 - **失败**:返回 -1(如系统资源不足)。 #### 示例: ```c #include <unistd.h> #include <stdio.h> int main() { pid_t pid = fork(); if (pid == -1) { perror("fork failed"); return 1; } else if (pid == 0) { printf("Child process (PID=%d)\n", getpid()); } else { printf("Parent process (PID=%d), child PID=%d\n", getpid(), pid); } return 0; } ``` #### 关键点: - 子进程是父进程的副本(代码、数据、堆栈等),但拥有独立的 PID 和内存空间。 - 父子进程的执行顺序由调度器决定,可能交替运行。 --- ### 2. **`wait()`:等待子进程结束** - **功能**:父进程调用 `wait()` 会阻塞,直到任意一个子进程退出。 - **返回值**: - 成功:返回退出的子进程的 PID。 - 失败:返回 -1(如无子进程)。 - **状态参数**:通过 `int *status` 获取子进程的退出状态(需用宏解析)。 #### 示例: ```c #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { pid_t pid = fork(); if (pid == 0) { printf("Child running\n"); sleep(2); // 模拟子进程工作 return 42; // 子进程退出状态 } else { int status; pid_t child_pid = wait(&status); if (WIFEXITED(status)) { printf("Child %d exited with status %d\n", child_pid, WEXITSTATUS(status)); } } return 0; } ``` #### 关键点: - **阻塞调用**:父进程会暂停,直到子进程结束。 - **状态解析**: - `WIFEXITED(status)`:子进程是否正常退出。 - `WEXITSTATUS(status)`:获取子进程的退出码(如 `return 42` 中的 42)。 - **避免僵尸进程**:`wait()` 会回收子进程资源,防止其成为僵尸进程。 --- ### 3. **`exec()`:替换进程映像** - **功能**:加载新程序替换当前进程的代码、数据等(PID 不变)。 - **常见变体**: - `execl()`、`execv()`:接受参数列表或数组。 - `execle()`、`execvp()`:支持环境变量或路径搜索。 - **返回值**:仅在失败返回 -1(成功无返回,原进程代码被覆盖)。 #### 示例: ```c #include <unistd.h> #include <stdio.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程执行 ls 命令 execlp("ls", "ls", "-l", NULL); perror("exec failed"); // 仅当 exec 失败执行 return 1; } else { wait(NULL); // 等待子进程结束 printf("Child completed\n"); } return 0; } ``` #### 关键点: - **参数格式**: - 第一个参数是程序名(通常与命令名相同)。 - 参数列表必须以 `NULL` 结尾。 - **典型用途**:`fork()` + `exec()` 实现“创建子进程并运行新程序”。 --- ### 4. **三者的典型协作流程** 1. **`fork()`**:父进程创建子进程。 2. **`exec()`**:子进程调用 `exec` 运行新程序(如 `ls`、`grep`)。 3. **`wait()`**:父进程调用 `wait` 等待子进程结束,避免僵尸进程。 #### 完整示例: ```c #include <unistd.h> #include <sys/wait.h> #include <stdio.h> int main() { pid_t pid = fork(); if (pid == -1) { perror("fork"); return 1; } else if (pid == 0) { // 子进程:执行 ls -l execlp("ls", "ls", "-l", NULL); perror("exec"); // 若 exec 失败 return 1; } else { // 父进程:等待子进程 int status; waitpid(pid, &status, 0); if (WIFEXITED(status)) { printf("Child exited with %d\n", WEXITSTATUS(status)); } } return 0; } ``` --- ### 5. **常见问题与注意事项** 1. **`fork()` 的开销**: - 子进程会复制父进程的内存页表(写复制优化减少实际拷贝)。 2. **`exec()` 的参数**: - 必须以 `NULL` 结尾,否则可能导致未定义行为。 3. **`wait()` 的变体**: - `waitpid()`:可指定等待特定子进程(非阻塞选项 `WNOHANG`)。 4. **信号处理**: - 子进程可能继承父进程的信号处理函数,需在 `fork` 后重置。 5. **错误处理**: - 始终检查 `fork` 和 `exec` 的返回值,避免静默失败。 --- ### 6. **与 `system()` 的区别** - `system("ls -l")` 内部自动调用 `fork` + `exec` + `wait`,但灵活性较低(无法直接控制子进程)。 - 手动组合 `fork`/`exec`/`wait` 更适合需要精细控制的场景(如管道、重定向)。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JXES智能生态系统

如文章对你有用,请作者喝个咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值