waitid函数详解 链接到标题

1. 函数介绍 链接到标题

waitid函数是Linux系统中用于等待子进程状态变化的高级进程控制函数。它是wait()和waitpid()函数的增强版本,提供了更灵活和详细的功能。可以把waitid想象成一个"进程状态监视器",它能够精确地监控指定子进程或进程组的各种状态变化。

与传统的wait函数相比,waitid提供了更多的控制选项和更丰富的状态信息。它不仅能够等待子进程结束,还能监控子进程的暂停、继续等状态变化,返回详细的状态信息,包括退出状态、信号信息等。

使用场景:

  • 多进程程序中的子进程管理
  • 服务器程序中的子进程监控
  • 系统工具和shell的进程控制
  • 调试和测试程序中的进程跟踪
  • 实现进程池和作业控制

2. 函数原型 链接到标题

#include <sys/wait.h>

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

3. 功能 链接到标题

waitid函数的主要功能是等待指定进程或进程组的状态变化,并返回详细的状态信息。它支持多种等待模式和选项,可以精确控制等待行为。

4. 参数 链接到标题

  • idtype: 标识类型

    • 类型:idtype_t
    • 含义:指定要等待的进程标识类型
    • 常用值:
      • P_ALL:等待所有子进程
      • P_PID:等待指定进程ID的进程
      • P_PGID:等待指定进程组ID的进程组
  • id: 进程标识符

    • 类型:id_t
    • 含义:根据idtype指定的标识符值
      • 当idtype为P_PID时,id为进程ID
      • 当idtype为P_PGID时,id为进程组ID
      • 当idtype为P_ALL时,id被忽略(通常设为0)
  • infop: 状态信息缓冲区

    • 类型:siginfo_t*
    • 含义:指向siginfo_t结构体的指针,用于返回子进程的详细状态信息
  • options: 等待选项

    • 类型:int
    • 含义:控制等待行为的标志位组合
    • 常用值:
      • WEXITED:等待子进程正常退出
      • WSTOPPED:等待子进程被信号停止
      • WCONTINUED:等待被停止的子进程继续执行
      • WNOHANG:非阻塞等待(立即返回)
      • WNOWAIT:不回收子进程(可多次等待同一子进程)

5. 返回值 链接到标题

  • 成功: 返回0
  • 失败: 返回-1,并设置errno错误码
    • ECHILD:没有符合条件的子进程
    • EINTR:等待被信号中断
    • EINVAL:参数无效

6. 相似函数或关联函数 链接到标题

  • wait(): 等待子进程结束
  • waitpid(): 等待指定子进程
  • wait3(): 等待子进程并返回资源使用信息
  • wait4(): 等待子进程并返回详细资源使用信息
  • fork(): 创建子进程
  • kill(): 向进程发送信号
  • signal()/sigaction(): 信号处理函数

7. 示例代码 链接到标题

示例1:基础waitid使用 - 等待子进程结束 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

// 信号处理函数
void signal_handler(int sig) {
    printf("父进程收到信号 %d\n", sig);
}

// 创建测试子进程
pid_t create_test_child(const char* name, int exit_code) {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("fork失败");
        return -1;
    }
    
    if (pid == 0) {
        // 子进程
        printf("子进程 %s (PID: %d) 启动\n", name, getpid());
        sleep(2);  // 模拟工作
        printf("子进程 %s 准备退出,退出码: %d\n", name, exit_code);
        exit(exit_code);
    }
    
    // 父进程返回子进程PID
    return pid;
}

// 解析siginfo_t信息
void parse_siginfo(const siginfo_t* info) {
    printf("子进程状态信息:\n");
    printf("  进程ID: %d\n", info->si_pid);
    printf("  信号码: %d\n", info->si_signo);
    printf("  错误码: %d\n", info->si_errno);
    printf("  信号值: %d\n", info->si_code);
    
    switch (info->si_code) {
        case CLD_EXITED:
            printf("  状态: 正常退出,退出码: %d\n", info->si_status);
            break;
        case CLD_KILLED:
            printf("  状态: 被信号杀死,信号: %d (%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_DUMPED:
            printf("  状态: 被信号杀死并产生core dump,信号: %d (%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_STOPPED:
            printf("  状态: 被信号停止,信号: %d (%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_CONTINUED:
            printf("  状态: 继续执行\n");
            break;
        default:
            printf("  状态: 未知 (%d)\n", info->si_code);
            break;
    }
}

int main() {
    printf("=== 基础waitid使用示例 ===\n");
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    
    // 创建多个测试子进程
    printf("1. 创建测试子进程:\n");
    pid_t child1 = create_test_child("Child1", 0);
    pid_t child2 = create_test_child("Child2", 42);
    pid_t child3 = create_test_child("Child3", 1);
    
    if (child1 == -1 || child2 == -1 || child3 == -1) {
        exit(EXIT_FAILURE);
    }
    
    printf("创建了3个子进程: %d, %d, %d\n", child1, child2, child3);
    
    // 使用waitid等待所有子进程结束
    printf("\n2. 使用waitid等待子进程结束:\n");
    
    siginfo_t info;
    int finished_children = 0;
    
    while (finished_children < 3) {
        // 等待任意子进程结束
        if (waitid(P_ALL, 0, &info, WEXITED) == -1) {
            if (errno == EINTR) {
                printf("等待被信号中断,继续等待...\n");
                continue;
            } else {
                perror("waitid失败");
                break;
            }
        }
        
        printf("\n检测到子进程状态变化:\n");
        parse_siginfo(&info);
        finished_children++;
    }
    
    printf("\n所有子进程已完成,总计: %d\n", finished_children);
    
    // 演示P_PID用法
    printf("\n3. 演示P_PID用法:\n");
    
    pid_t child4 = create_test_child("Child4", 99);
    if (child4 != -1) {
        // 等待特定子进程
        if (waitid(P_PID, child4, &info, WEXITED) == 0) {
            printf("等待特定子进程 %d 完成:\n", child4);
            parse_siginfo(&info);
        } else {
            perror("等待特定子进程失败");
        }
    }
    
    printf("\n=== 基础waitid演示完成 ===\n");
    
    return 0;
}

示例2:waitid高级用法 - 监控进程状态变化 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <time.h>

// 创建可控制的子进程
pid_t create_controllable_child(const char* name) {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("fork失败");
        return -1;
    }
    
    if (pid == 0) {
        // 子进程
        printf("可控子进程 %s (PID: %d) 启动\n", name, getpid());
        
        // 工作循环
        for (int i = 0; i < 10; i++) {
            printf("子进程 %s 工作中... (%d/10)\n", name, i + 1);
            sleep(1);
        }
        
        printf("子进程 %s 正常退出\n", name);
        exit(0);
    }
    
    return pid;
}

// 发送信号给进程
void send_signal_to_process(pid_t pid, int signal) {
    printf("向进程 %d 发送信号 %d (%s)\n", pid, signal, strsignal(signal));
    if (kill(pid, signal) == -1) {
        perror("发送信号失败");
    }
}

// 显示详细状态信息
void show_detailed_status(const siginfo_t* info) {
    time_t current_time = time(NULL);
    printf("[%s", ctime(&current_time));
    // 移除ctime返回的换行符
    char* newline = strchr(ctime(&current_time), '\n');
    if (newline) *newline = '\0';
    printf("] 进程 %d 状态变化:\n", info->si_pid);
    
    switch (info->si_code) {
        case CLD_EXITED:
            printf("  正常退出,退出状态: %d\n", info->si_status);
            break;
        case CLD_KILLED:
            printf("  被信号 %d (%s) 杀死\n", info->si_status, strsignal(info->si_status));
            break;
        case CLD_DUMPED:
            printf("  被信号 %d (%s) 杀死并产生core dump\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_STOPPED:
            printf("  被信号 %d (%s) 停止\n", info->si_status, strsignal(info->si_status));
            break;
        case CLD_CONTINUED:
            printf("  继续执行\n");
            break;
        default:
            printf("  未知状态: %d\n", info->si_code);
            break;
    }
    
    printf("  用户ID: %d\n", info->si_uid);
    printf("  进程组ID: %d\n", getpgid(info->si_pid));
}

int main() {
    printf("=== waitid高级用法示例 ===\n");
    
    // 创建可控子进程
    printf("1. 创建可控子进程:\n");
    pid_t child = create_controllable_child("MonitorChild");
    if (child == -1) {
        exit(EXIT_FAILURE);
    }
    
    printf("创建子进程 PID: %d\n", child);
    
    // 使用waitid监控所有状态变化
    printf("\n2. 监控子进程状态变化:\n");
    
    siginfo_t info;
    int monitoring = 1;
    int status_changes = 0;
    
    while (monitoring) {
        // 非阻塞等待
        if (waitid(P_PID, child, &info, WEXITED | WSTOPPED | WCONTINUED | WNOHANG) == -1) {
            if (errno == ECHILD) {
                printf("子进程已不存在\n");
                break;
            } else if (errno == EINTR) {
                printf("等待被中断\n");
                continue;
            } else {
                perror("waitid失败");
                break;
            }
        }
        
        // 检查是否有状态变化
        if (info.si_pid != 0) {
            show_detailed_status(&info);
            status_changes++;
            
            // 如果子进程退出,停止监控
            if (info.si_code == CLD_EXITED || 
                info.si_code == CLD_KILLED || 
                info.si_code == CLD_DUMPED) {
                monitoring = 0;
            }
        } else {
            // 没有状态变化,发送一些控制信号
            static int signal_count = 0;
            signal_count++;
            
            switch (signal_count) {
                case 3:
                    send_signal_to_process(child, SIGSTOP);
                    break;
                case 6:
                    send_signal_to_process(child, SIGCONT);
                    break;
                case 9:
                    send_signal_to_process(child, SIGTERM);
                    break;
            }
            
            sleep(1);
        }
    }
    
    printf("\n监控结束,总计状态变化: %d 次\n", status_changes);
    
    // 演示P_PGID用法
    printf("\n3. 演示P_PGID用法:\n");
    
    // 创建进程组
    pid_t group_leader = fork();
    if (group_leader == 0) {
        // 组长进程
        setpgid(0, 0);  // 创建新进程组
        
        // 创建组内进程
        for (int i = 0; i < 3; i++) {
            pid_t member = fork();
            if (member == 0) {
                setpgid(0, getpid());  // 加入进程组
                printf("进程组成员 %d 启动\n", getpid());
                sleep(2 + i);
                exit(i);
            } else {
                setpgid(member, getpid());
            }
        }
        
        // 组长等待所有成员
        sleep(5);
        exit(0);
    }
    
    if (group_leader != -1) {
        printf("创建进程组,组长PID: %d\n", group_leader);
        sleep(1);  // 等待进程组创建完成
        
        // 等待进程组中的任意进程
        printf("等待进程组中的进程状态变化:\n");
        int group_finished = 0;
        while (group_finished < 4) {  // 组长 + 3个成员
            if (waitid(P_PGID, group_leader, &info, WEXITED | WNOHANG) == 0) {
                if (info.si_pid != 0) {
                    printf("进程组成员 %d 退出,状态: %d\n", 
                           info.si_pid, info.si_status);
                    group_finished++;
                } else {
                    sleep(1);
                }
            } else {
                if (errno != ECHILD) {
                    perror("等待进程组失败");
                }
                break;
            }
        }
        printf("进程组监控完成\n");
    }
    
    printf("\n=== 高级waitid演示完成 ===\n");
    
    return 0;
}

示例3:进程池管理与批量等待 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define MAX_PROCESSES 10
#define POOL_SIZE 5

// 进程信息结构
typedef struct {
    pid_t pid;
    int job_id;
    time_t start_time;
    int status;
    int finished;
} process_info_t;

process_info_t process_pool[MAX_PROCESSES];
int process_count = 0;

// 创建工作进程
pid_t create_worker_process(int job_id, int work_time) {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("创建进程失败");
        return -1;
    }
    
    if (pid == 0) {
        // 工作进程
        printf("工作进程 %d (PID: %d) 启动,任务ID: %d\n", 
               getpid(), getpid(), job_id);
        
        // 模拟工作
        for (int i = 0; i < work_time; i++) {
            printf("工作进程 %d 执行任务 %d (%d/%d)\n", 
                   getpid(), job_id, i + 1, work_time);
            sleep(1);
        }
        
        int exit_code = job_id % 100;  // 生成退出码
        printf("工作进程 %d 完成任务 %d,退出码: %d\n", 
               getpid(), job_id, exit_code);
        exit(exit_code);
    }
    
    // 父进程记录进程信息
    if (process_count < MAX_PROCESSES) {
        process_pool[process_count].pid = pid;
        process_pool[process_count].job_id = job_id;
        process_pool[process_count].start_time = time(NULL);
        process_pool[process_count].status = 0;
        process_pool[process_count].finished = 0;
        process_count++;
    }
    
    return pid;
}

// 显示进程池状态
void show_process_pool_status() {
    printf("\n进程池状态 (%d 个进程):\n", process_count);
    printf("%-8s %-8s %-10s %-10s %-15s\n", 
           "PID", "JobID", "状态", "退出码", "运行时间");
    printf("%-8s %-8s %-10s %-10s %-15s\n", 
           "---", "-----", "----", "------", "--------");
    
    time_t current_time = time(NULL);
    
    for (int i = 0; i < process_count; i++) {
        const process_info_t* proc = &process_pool[i];
        char status_str[16];
        char runtime_str[16];
        
        if (proc->finished) {
            snprintf(status_str, sizeof(status_str), "完成");
        } else {
            snprintf(status_str, sizeof(status_str), "运行中");
        }
        
        int runtime = current_time - proc->start_time;
        snprintf(runtime_str, sizeof(runtime_str), "%ds", runtime);
        
        printf("%-8d %-8d %-10s %-10d %-15s\n",
               proc->pid, proc->job_id, status_str, 
               proc->status, runtime_str);
    }
    printf("\n");
}

// 批量启动进程
int start_process_pool(int pool_size) {
    printf("启动进程池 (大小: %d):\n", pool_size);
    
    for (int i = 0; i < pool_size; i++) {
        int work_time = 3 + (i % 4);  // 3-6秒的工作时间
        pid_t pid = create_worker_process(i + 1, work_time);
        
        if (pid != -1) {
            printf("启动工作进程: PID %d (任务 %d)\n", pid, i + 1);
        } else {
            return -1;
        }
    }
    
    return 0;
}

// 使用waitid批量等待进程
int wait_for_process_pool() {
    printf("开始批量等待进程池中的进程:\n");
    
    siginfo_t info;
    int finished_count = 0;
    int timeout = 30;  // 30秒超时
    
    while (finished_count < process_count && timeout > 0) {
        // 非阻塞等待
        if (waitid(P_ALL, 0, &info, WEXITED | WNOHANG) == -1) {
            if (errno == ECHILD) {
                printf("没有子进程可等待\n");
                break;
            } else if (errno == EINTR) {
                printf("等待被中断\n");
                continue;
            } else {
                perror("waitid失败");
                break;
            }
        }
        
        // 检查是否有进程结束
        if (info.si_pid != 0) {
            printf("进程 %d 结束,退出码: %d\n", 
                   info.si_pid, info.si_status);
            
            // 更新进程池信息
            for (int i = 0; i < process_count; i++) {
                if (process_pool[i].pid == info.si_pid) {
                    process_pool[i].finished = 1;
                    process_pool[i].status = info.si_status;
                    break;
                }
            }
            
            finished_count++;
            show_process_pool_status();
        } else {
            // 没有进程结束,等待一段时间
            printf("等待进程结束... (%d/%d 完成)\n", 
                   finished_count, process_count);
            sleep(1);
            timeout--;
        }
    }
    
    if (timeout <= 0) {
        printf("等待超时\n");
        return -1;
    }
    
    printf("所有进程已完成\n");
    return 0;
}

// 清理剩余进程
void cleanup_remaining_processes() {
    printf("清理剩余进程:\n");
    
    for (int i = 0; i < process_count; i++) {
        if (!process_pool[i].finished) {
            printf("终止进程 %d\n", process_pool[i].pid);
            kill(process_pool[i].pid, SIGTERM);
        }
    }
    
    // 等待所有进程结束
    siginfo_t info;
    while (waitid(P_ALL, 0, &info, WEXITED | WNOHANG) == 0 && info.si_pid != 0) {
        printf("进程 %d 已终止\n", info.si_pid);
    }
}

int main() {
    printf("=== 进程池管理与批量等待示例 ===\n");
    
    // 启动进程池
    if (start_process_pool(POOL_SIZE) == -1) {
        exit(EXIT_FAILURE);
    }
    
    show_process_pool_status();
    
    // 等待进程池中的进程
    printf("\n开始等待进程池中的进程:\n");
    if (wait_for_process_pool() == -1) {
        printf("等待过程中出现问题\n");
        cleanup_remaining_processes();
    }
    
    show_process_pool_status();
    
    // 演示WNOWAIT选项
    printf("\n演示WNOWAIT选项:\n");
    
    // 创建测试进程
    pid_t test_pid = fork();
    if (test_pid == 0) {
        printf("测试进程 %d 启动\n", getpid());
        sleep(2);
        exit(42);
    }
    
    if (test_pid != -1) {
        printf("创建测试进程 PID: %d\n", test_pid);
        
        // 第一次等待(使用WNOWAIT)
        siginfo_t info1;
        if (waitid(P_PID, test_pid, &info1, WEXITED | WNOWAIT) == 0) {
            printf("第一次等待结果:\n");
            printf("  进程 %d 退出,状态: %d\n", info1.si_pid, info1.si_status);
        }
        
        // 第二次等待(正常等待,回收进程)
        siginfo_t info2;
        if (waitid(P_PID, test_pid, &info2, WEXITED) == 0) {
            printf("第二次等待结果:\n");
            printf("  进程 %d 退出,状态: %d\n", info2.si_pid, info2.si_status);
        }
        
        printf("WNOWAIT演示完成\n");
    }
    
    // 演示错误处理
    printf("\n错误处理演示:\n");
    
    // 等待不存在的进程
    siginfo_t info;
    if (waitid(P_PID, 999999, &info, WEXITED) == -1) {
        if (errno == ECHILD) {
            printf("✓ 正确处理了不存在的进程\n");
        } else {
            printf("✗ 其他错误: %s\n", strerror(errno));
        }
    }
    
    // 使用无效的idtype
    if (waitid((idtype_t)999, 0, &info, WEXITED) == -1) {
        if (errno == EINVAL) {
            printf("✓ 正确处理了无效的idtype\n");
        } else {
            printf("✗ 其他错误: %s\n", strerror(errno));
        }
    }
    
    printf("\n=== 进程池管理演示完成 ===\n");
    
    return 0;
}

示例4:信号处理与进程监控 链接到标题

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define MAX_MONITORED_PROCESSES 20

// 被监控的进程信息
typedef struct {
    pid_t pid;
    char name[64];
    time_t start_time;
    int monitoring;
    int stop_signal_sent;
} monitored_process_t;

monitored_process_t monitored_processes[MAX_MONITORED_PROCESSES];
int monitored_count = 0;
volatile sig_atomic_t stop_monitoring = 0;

// 信号处理函数
void signal_handler(int sig) {
    printf("\n收到信号 %d (%s)\n", sig, strsignal(sig));
    if (sig == SIGINT || sig == SIGTERM) {
        stop_monitoring = 1;
        printf("开始停止进程监控...\n");
    }
}

// 创建长时间运行的进程
pid_t create_long_running_process(const char* name) {
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("创建进程失败");
        return -1;
    }
    
    if (pid == 0) {
        // 子进程
        printf("长时间运行进程 %s (PID: %d) 启动\n", name, getpid());
        
        // 设置进程组
        setpgid(0, 0);
        
        // 工作循环
        int counter = 0;
        while (1) {
            counter++;
            printf("进程 %s 工作中... (%d)\n", name, counter);
            
            // 模拟可能的异常退出
            if (counter == 15) {
                printf("进程 %s 模拟异常退出\n", name);
                exit(1);
            }
            
            sleep(1);
        }
    }
    
    // 父进程记录监控信息
    if (monitored_count < MAX_MONITORED_PROCESSES) {
        monitored_processes[monitored_count].pid = pid;
        strncpy(monitored_processes[monitored_count].name, name, 
                sizeof(monitored_processes[monitored_count].name) - 1);
        monitored_processes[monitored_count].start_time = time(NULL);
        monitored_processes[monitored_count].monitoring = 1;
        monitored_processes[monitored_count].stop_signal_sent = 0;
        monitored_count++;
    }
    
    return pid;
}

// 显示监控状态
void show_monitoring_status() {
    printf("\n=== 进程监控状态 ===\n");
    time_t current_time = time(NULL);
    
    printf("%-8s %-20s %-12s %-12s %-10s\n",
           "PID", "名称", "运行时间", "监控状态", "停止信号");
    printf("%-8s %-20s %-12s %-12s %-10s\n",
           "---", "----", "--------", "--------", "--------");
    
    for (int i = 0; i < monitored_count; i++) {
        const monitored_process_t* proc = &monitored_processes[i];
        int runtime = current_time - proc->start_time;
        char runtime_str[16];
        char monitor_str[16];
        char signal_str[16];
        
        snprintf(runtime_str, sizeof(runtime_str), "%ds", runtime);
        snprintf(monitor_str, sizeof(monitor_str), 
                 proc->monitoring ? "监控中" : "已停止");
        snprintf(signal_str, sizeof(signal_str), 
                 proc->stop_signal_sent ? "已发送" : "未发送");
        
        printf("%-8d %-20s %-12s %-12s %-10s\n",
               proc->pid, proc->name, runtime_str, 
               monitor_str, signal_str);
    }
    printf("==================\n\n");
}

// 处理进程状态变化
void handle_process_status_change(const siginfo_t* info) {
    printf("[%ld] 进程状态变化: PID=%d ", time(NULL), info->si_pid);
    
    switch (info->si_code) {
        case CLD_EXITED:
            printf("正常退出,退出码=%d\n", info->si_status);
            break;
        case CLD_KILLED:
            printf("被信号杀死,信号=%d(%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_DUMPED:
            printf("被信号杀死并产生core dump,信号=%d(%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_STOPPED:
            printf("被信号停止,信号=%d(%s)\n", 
                   info->si_status, strsignal(info->si_status));
            break;
        case CLD_CONTINUED:
            printf("继续执行\n");
            break;
        default:
            printf("未知状态=%d\n", info->si_code);
            break;
    }
    
    // 更新监控信息
    for (int i = 0; i < monitored_count; i++) {
        if (monitored_processes[i].pid == info->si_pid) {
            if (info->si_code == CLD_EXITED || 
                info->si_code == CLD_KILLED || 
                info->si_code == CLD_DUMPED) {
                monitored_processes[i].monitoring = 0;
            }
            break;
        }
    }
}

// 监控循环
int monitoring_loop() {
    printf("开始进程监控循环...\n");
    printf("按 Ctrl+C 停止监控\n\n");
    
    siginfo_t info;
    int active_processes = monitored_count;
    
    while (!stop_monitoring && active_processes > 0) {
        // 非阻塞等待进程状态变化
        if (waitid(P_ALL, 0, &info, WEXITED | WSTOPPED | WCONTINUED | WNOHANG) == -1) {
            if (errno == ECHILD) {
                printf("没有子进程可监控\n");
                break;
            } else if (errno == EINTR) {
                printf("监控被信号中断\n");
                continue;
            } else {
                perror("waitid监控失败");
                break;
            }
        }
        
        // 处理状态变化
        if (info.si_pid != 0) {
            handle_process_status_change(&info);
            active_processes--;
            show_monitoring_status();
        } else {
            // 没有状态变化,定期显示状态
            static int status_counter = 0;
            status_counter++;
            if (status_counter % 10 == 0) {  // 每10秒显示一次
                show_monitoring_status();
            }
            sleep(1);
        }
    }
    
    return 0;
}

// 优雅停止所有进程
void graceful_stop_all_processes() {
    printf("优雅停止所有监控的进程:\n");
    
    time_t start_time = time(NULL);
    
    // 发送停止信号给所有活跃进程
    for (int i = 0; i < monitored_count; i++) {
        if (monitored_processes[i].monitoring && 
            !monitored_processes[i].stop_signal_sent) {
            printf("发送SIGTERM给进程 %s (PID: %d)\n", 
                   monitored_processes[i].name, 
                   monitored_processes[i].pid);
            kill(monitored_processes[i].pid, SIGTERM);
            monitored_processes[i].stop_signal_sent = 1;
        }
    }
    
    // 等待进程结束(最多10秒)
    siginfo_t info;
    int remaining_processes = 0;
    
    do {
        remaining_processes = 0;
        
        // 检查还有多少进程在运行
        for (int i = 0; i < monitored_count; i++) {
            if (monitored_processes[i].monitoring) {
                remaining_processes++;
            }
        }
        
        if (remaining_processes > 0) {
            printf("等待 %d 个进程结束...\n", remaining_processes);
            
            // 等待进程结束
            if (waitid(P_ALL, 0, &info, WEXITED | WNOHANG) == 0) {
                if (info.si_pid != 0) {
                    handle_process_status_change(&info);
                    remaining_processes--;
                }
            }
            
            // 检查超时
            if (time(NULL) - start_time > 10) {
                printf("超时,强制终止剩余进程\n");
                for (int i = 0; i < monitored_count; i++) {
                    if (monitored_processes[i].monitoring) {
                        printf("发送SIGKILL给进程 %s (PID: %d)\n", 
                               monitored_processes[i].name, 
                               monitored_processes[i].pid);
                        kill(monitored_processes[i].pid, SIGKILL);
                    }
                }
                break;
            }
            
            sleep(1);
        }
    } while (remaining_processes > 0);
    
    printf("所有进程已停止\n");
}

int main() {
    printf("=== 信号处理与进程监控示例 ===\n");
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // 创建被监控的进程
    printf("1. 创建被监控的进程:\n");
    
    pid_t proc1 = create_long_running_process("Worker1");
    pid_t proc2 = create_long_running_process("Worker2");
    pid_t proc3 = create_long_running_process("Worker3");
    
    if (proc1 == -1 || proc2 == -1 || proc3 == -1) {
        fprintf(stderr, "创建监控进程失败\n");
        exit(EXIT_FAILURE);
    }
    
    printf("创建了 %d 个监控进程\n", monitored_count);
    show_monitoring_status();
    
    // 演示不同类型的等待选项
    printf("2. 演示不同等待选项:\n");
    
    // 创建测试进程用于演示
    pid_t test_proc = fork();
    if (test_proc == 0) {
        printf("测试进程启动\n");
        sleep(3);
        printf("测试进程正常退出\n");
        exit(0);
    }
    
    if (test_proc != -1) {
        printf("创建测试进程 PID: %d\n", test_proc);
        
        // 等待特定进程正常退出
        siginfo_t info;
        printf("等待进程 %d 正常退出...\n", test_proc);
        if (waitid(P_PID, test_proc, &info, WEXITED) == 0) {
            printf("进程 %d 正常退出,退出码: %d\n", 
                   info.si_pid, info.si_status);
        }
    }
    
    // 启动监控循环
    printf("\n3. 启动进程监控循环:\n");
    monitoring_loop();
    
    // 优雅停止所有进程
    printf("\n4. 优雅停止所有进程:\n");
    graceful_stop_all_processes();
    
    show_monitoring_status();
    
    // 演示资源清理
    printf("\n5. 资源清理演示:\n");
    
    // 确保没有僵尸进程
    siginfo_t cleanup_info;
    int cleanup_count = 0;
    while (waitid(P_ALL, 0, &cleanup_info, WEXITED | WNOHANG) == 0 && 
           cleanup_info.si_pid != 0) {
        printf("清理僵尸进程: PID %d\n", cleanup_info.si_pid);
        cleanup_count++;
    }
    
    if (cleanup_count > 0) {
        printf("共清理 %d 个僵尸进程\n", cleanup_count);
    } else {
        printf("没有需要清理的僵尸进程\n");
    }
    
    printf("\n=== 信号处理与进程监控演示完成 ===\n");
    
    return 0;
}

编译和运行 链接到标题

# 编译示例1
gcc -o waitid_example1 waitid_example1.c
./waitid_example1

# 编译示例2
gcc -o waitid_example2 waitid_example2.c
./waitid_example2

# 编译示例3
gcc -o waitid_example3 waitid_example3.c
./waitid_example3

# 编译示例4
gcc -o waitid_example4 waitid_example4.c
./waitid_example4

重要注意事项 链接到标题

  1. siginfo_t结构: 提供比传统wait函数更详细的状态信息
  2. 非阻塞选项: WNOHANG允许非阻塞等待
  3. 多次等待: WNOWAIT允许不回收进程,可多次等待
  4. 状态监控: 支持监控进程的暂停、继续等状态
  5. 进程组支持: 可以等待整个进程组
  6. 错误处理: 必须检查返回值并处理EINTR等错误
  7. 资源管理: 及时清理僵尸进程

最佳实践 链接到标题

  1. 使用siginfo_t: 充分利用详细的状态信息
  2. 非阻塞等待: 在需要响应其他事件时使用WNOHANG
  3. 信号处理: 正确处理EINTR错误
  4. 进程组管理: 合理使用P_PGID管理进程组
  5. 优雅停止: 实现进程的优雅停止机制
  6. 资源清理: 确保及时清理僵尸进程
  7. 错误日志: 记录进程状态变化的详细信息

通过这些示例,你可以理解waitid在进程管理方面的强大功能,它为Linux系统提供了灵活、精确的进程控制能力,特别适用于复杂的多进程应用程序。