学习笔记——进程控制函数

进程控制函数整理笔记

一、 进程回收函数

wait() - 阻塞回收

复制代码
#include <sys/wait.h>
pid_t wait(int *status);

功能:阻塞等待任意子进程退出并回收状态(只能父进程回收子进程)

参数

  • status:进程退出时的状态

    • 不关心退出状态:NULL

    • 需要回收状态:传变量地址

返回值

  • 成功:回收的子进程PID

  • 失败:-1

状态宏使用

复制代码
int status;
pid_t pid = wait(&status);

if (WIFEXITED(status)) {           // 是否正常结束
    int exit_code = WEXITSTATUS(status);  // 获取退出码
    printf("子进程正常退出,退出码: %d\n", exit_code);
} 
else if (WIFSIGNALED(status)) {    // 是否被信号终止
    int sig_num = WTERMSIG(status);      // 获取信号编号
    printf("子进程被信号终止,信号: %d\n", sig_num);
}

waitpid() - 精确控制回收

复制代码
pid_t waitpid(pid_t pid, int *status, int options);

参数

  1. pid

    • >0:指定回收特定PID的子进程

    • -1:回收任意子进程(等价于wait()

    • 0:回收同进程组的子进程

    • <-1:回收指定进程组ID的绝对值

  2. status:同wait()

  3. options

    • 0:阻塞模式(默认)

    • WNOHANG:非阻塞模式

返回值

  • 成功:回收的子进程PID

  • 0WNOHANG模式下,子进程未退出

  • -1:错误

二、 非阻塞回收示例

方式1:循环检查

复制代码
// 非阻塞回收所有子进程
int status;
pid_t pid;

while (1) {
    pid = waitpid(-1, &status, WNOHANG);
    
    if (pid > 0) {
        // 成功回收一个子进程
        printf("回收子进程 %d\n", pid);
        if (WIFEXITED(status)) {
            printf("退出码: %d\n", WEXITSTATUS(status));
        }
    } 
    else if (pid == 0) {
        // 没有子进程退出,可以做其他事情
        printf("没有子进程退出,继续工作...\n");
        sleep(1);
    } 
    else if (pid == -1) {
        // 错误或没有子进程了
        printf("所有子进程已回收完毕\n");
        break;
    }
}

方式2:信号驱动(SIGCHLD)

复制代码
#include <signal.h>

void sigchld_handler(int sig) {
    int status;
    pid_t pid;
    
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        printf("子进程 %d 已结束\n", pid);
    }
}

int main() {
    signal(SIGCHLD, sigchld_handler);
    // ... 创建子进程
}

三、 exec函数族

内存变化

执行exec前 :原进程有自己的代码段、数据段、堆栈
执行exec后:原进程的代码段被新程序替换,PID不变,其他资源可能变化

函数对比表

函数 路径指定 参数传递 PATH查找
execl() 完整路径 参数列表
execlp() 文件名 参数列表
execv() 完整路径 参数数组
execvp() 文件名 参数数组

函数详解

execl() - 参数列表形式
复制代码
int execl(const char *path, const char *arg, ..., NULL);
  • path:完整路径(如"/usr/bin/ls"

  • arg:参数列表,以NULL结束

复制代码
execl("/usr/bin/ls", "ls", "-l", "/home", NULL);
execlp() - 自动PATH查找
复制代码
int execlp(const char *file, const char *arg, ..., NULL);
  • file:程序名,自动在PATH中查找
复制代码
execlp("ls", "ls", "-l", "/home", NULL);
execv() - 参数数组形式
复制代码
int execv(const char *path, char *const argv[]);
  • argv:参数数组,最后一项必须是NULL
复制代码
char *args[] = {"ls", "-l", "/home", NULL};
execv("/usr/bin/ls", args);
execvp() - 自动PATH查找+数组参数
复制代码
int execvp(const char *file, char *const argv[]);
复制代码
char *args[] = {"ls", "-l", "/home", NULL};
execvp("ls", args);

调用自己的程序

复制代码
// 假设当前目录有程序 myapp
execl("./myapp", "myapp", "arg1", "arg2", NULL);  // 必须带路径
execlp("./myapp", "myapp", "arg1", "arg2", NULL); // 带路径也可
execv("./myapp", args);    // 必须带路径
execvp("./myapp", args);   // 带路径也可

四、 fork + exec 配合使用

复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程执行新程序
        execlp("ls", "ls", "-l", NULL);
        
        // 如果exec失败才会执行到这里
        perror("execlp failed");
        exit(1);
    } 
    else if (pid > 0) {
        // 父进程等待子进程
        int status;
        waitpid(pid, &status, 0);
        
        if (WIFEXITED(status)) {
            printf("子进程退出码: %d\n", WEXITSTATUS(status));
        }
    }
    
    return 0;
}

五、 system()函数

复制代码
#include <stdlib.h>
int system(const char *command);

功能 :执行shell命令(内部实现:fork() + exec()

限制

  • 不能执行需要修改父进程状态的命令(如cd, export等)

  • 适合执行信息输出、文件操作等命令

示例

复制代码
system("ls -l");          // 列出文件
system("echo hello");     // 输出信息
system("cp file1 file2"); // 复制文件

// ❌ 这些无效(不会影响父进程):
system("cd /tmp");        // 只在子shell中切换
system("export VAR=1");   // 只在子shell中设置

六、 工作目录函数

getcwd() - 获取当前工作目录

复制代码
#include <unistd.h>
char *getcwd(char *buf, size_t size);

参数

  • buf:存储路径的字符数组

  • size:数组最大长度

返回值

  • 成功:指向buf的指针

  • 失败:NULL

示例

复制代码
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
    printf("当前目录: %s\n", cwd);
} else {
    perror("getcwd失败");
}

chdir() - 改变工作目录

复制代码
int chdir(const char *path);

参数path - 要切换的路径

返回值

  • 成功:0

  • 失败:-1

示例

复制代码
if (chdir("/tmp") != 0) {
    perror("chdir失败");
} else {
    printf("成功切换到 /tmp\n");
}

七、 关键点总结

  1. wait() vs waitpid()

    • wait():简单但只能阻塞回收

    • waitpid():功能更强,支持非阻塞和指定进程

  2. exec函数选择

    • 知道完整路径:用execl()execv()

    • 想用PATH查找:用execlp()execvp()

    • 参数动态:用execv()execvp()

    • 参数固定:用execl()execlp()

  3. 非阻塞回收

    • WNOHANG选项

    • 返回值需要特殊处理

    • 通常放在循环中

  4. cd命令特殊性

    • 必须在当前进程执行chdir()

    • 不能用fork()+exec()实现

    • 不能用system()实现

  5. 僵尸进程避免

    • 及时用wait()waitpid()回收

    • 对于后台进程,可以用信号处理SIGCHLD

相关推荐
小O的算法实验室6 小时前
2022年CIE SCI2区TOP,双向交替搜索 A* 算法的移动机器人全局路径规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
木头左6 小时前
多任务联合训练框架下的遗忘门协同优化趋势跟踪与均值回归双目标平衡
算法·均值算法·回归
xu_yule6 小时前
算法基础-(单调队列)
算法·单调队列
冬夜戏雪6 小时前
【学习日记】【12.15】【13/60】
学习
QiZhang | UESTC6 小时前
学习日记day49
学习
代码不停6 小时前
Java递归综合练习
java·开发语言·算法·回归
通义灵码6 小时前
如何将 Qoder 融入实际研发与运维流程
运维·人工智能·qoder
前端小白在前进6 小时前
力扣刷题:删除排序链表的重复元素Ⅱ
算法·leetcode·链表
石像鬼₧魂石6 小时前
Fail2Ban 一键部署 + 管理脚本(可直接执行)
linux·windows·学习·ubuntu