Linux系统编程

您所在的位置:网站首页 linux创建父进程 Linux系统编程

Linux系统编程

2024-07-17 12:33| 来源: 网络整理| 查看: 265

在应用程序设计中,有时候父进程需要知道某个子进程的状态(一般都是获取子进程的退出状态,避免其成为僵尸进程)

监控子进程的手段:

wait()函数及相关系统调用

信号SIGCHLD和SIGHLD(两个信号意义是一样的)**

wait()及相关系统调用 wait()系统调用 //头文件 #include //调用该函数的进程没有子进程终止,调用将一直阻塞,直至某个子进程终止 //有子进程终止,立即返回 pid_t wait(int *wstatus); //wstatus —— 保存进程终止信息 RETURN VALUE 返回子进程的进程ID,错误返回-1 waitpid()系统调用 //头文件 #include pid_t waitpid (pid_t pid, int *wstatus, int options); //pid —— 等待指定的子进程 //pid > 0, pid为等待的子进程的进程号 //pid = 0, 等待调用父进程同一进程组的所有子进程 //pid < -1, 等待进程组标识符与pid绝对值相等的所有子进程 //pid = -1, 等待任意子进程,waitpid(-1, &status, 0)与wait(&status)等价 //wstatus —— 子进程退出状态 //options —— 等待操作选项 // WNOHANG —— 指定的子进程状态没有改变立即返回,不阻塞 // WUNTRACED —— 除了返回终止的子进程信息外,还返回由于信号而终止的子进程信息 // WCONTINUED (since Linux 2.6.10)——返回因受到SIGCONT信号而恢复执行的已终止的子进程的信息 RETURN VALUE 成功时,返回状态已更改的子进程的进程ID;如果指定options为WNOHANG,以及pid指定一个或多个进程,但是没有子进程状态改变,则返回0。当出现错误时,返回-1。 使用以下宏来检查判断子进程退出的原因 WIFEXITED(wstatus) : 正常结束返回true,换句话就是, 调用exit(3) or exit(2)这些,或者在main()中return返回 WEXITSTATUS(wstatus) : 返回子程序的退出状态。它由状态参数中最不重要的8位组成,子元素在exit(3)或exit(2)调用中指定该参数,或者在main()中作为返回语句的参数。只有当WIFEXITED返回true时,才应该使用这个宏。 WIFSIGNALED(wstatus) : 如果子进程被一个信号终止,则返回true。. WTERMSIG(wstatus) : 返回导致子进程终止的信号编号。只有当WIFSIGNALED返回true时,才应该使用这个宏。 WIFSTOPPED(wstatus) : 如果子进程接收信号终止,返回true;只有当使用WUNTRACED这个options选项进行系统调用, 或者当子进程在调试被跟踪时(参见ptrace(2)),才有可能这样做。 WSTOPSIG(wstatus) : 返回导致孩子停止的信号编号。只有当WIFSTOPPED返回true时,才应该使用这个宏。 WIFCONTINUED(wstatus)(since Linux 2.6.10) : 如果子进程受到SIGCONT信号被恢复执行返回true //通过idtype and id 参数选择等待的子进程 int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); //idtype —— id类型 //idtype == P_PID 等待进程组ID为id进程的子进程 //idtype == P_PGID 等待进程组ID为id所有进程的所有子进程 //idtype == P_ALL 等待任意进程,忽略id参数 //id —— 指定的进程组id //options —— 操作选项 // WEXITED 等待已经终止的子进程,无论是否正常退出. // WSTOPPED 等待通过信号终止的子进程 // WCONTINUED 等待先前是终止但被信号SIGCONT唤醒恢复执行的子进程 //options附加的选项: // WNOHANG 与waitpid()的一样. // WNOWAIT 返回子进程状态,依然让子进程处于等待状态;稍后的系统调用可用于再次获取子进程状态信息。 //infop —— 子进程信息 //该系统调用执行成功后infop被填充的结构体字段: //si_pid 子进程ID. //si_uid 子进程真实用户ID. //si_signo 总是被设置为SIGCHLD. //si_status 要么是子程序的退出状态(如exit(2)(或exit(3)),要么是导致子程序终止、停止或继续的信号。si_code字段可用于确定如何解释该字段。 //si_code字段取值: // CLD_EXITED(子进程调用exit(2)退出); // CLD_KILLED (子进程被信号杀死) // CLD_DUMPED (子进程被信号杀死,及脱离内核) // CLD_STOPPED (子进程被信号终止) // CLD_TRAPPED(子进程被调试跟踪) // CLD_CONTINUED(信号SIGCONT恢复子进程继续执行)。 RETURN VALUE 成功或者如果WNOHANG被指定进程组ID为id的子进程状态没有更改返回0,执行成子进程信息被填充到结构体指针infop中;当出现错误时,返回-1 siginfo_t { int si_signo; /* Signal number */ int si_errno; /* An errno value */ int si_code; /* Signal code */ int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) */ pid_t si_pid; /* Sending process ID */ uid_t si_uid; /* Real user ID of sending process */ int si_status; /* Exit value or signal */ clock_t si_utime; /* User time consumed */ clock_t si_stime; /* System time consumed */ sigval_t si_value; /* Signal value */ int si_int; /* POSIX.1b signal */ void *si_ptr; /* POSIX.1b signal */ int si_overrun; /* Timer overrun count; int si_timerid; /* Timer ID; POSIX.1b timers */ void *si_addr; /* Memory location which caused fault */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since Linux 2.6.32) */ void *si_lower; /* Lower bound when address violation occurred (since Linux 3.19) */ void *si_upper; /* Upper bound when address violation occurred (since Linux 3.19) */ int si_pkey; /* Protection key on PTE that caused fault (since Linux 4.6) */ void *si_call_addr; /* Address of system call instruction (since Linux 3.5) */ int si_syscall; /* Number of attempted system call (since Linux 3.5) */ unsigned int si_arch; /* Architecture of attempted system call (since Linux 3.5) */ }

等待所有子进程退出:

while( (childPid = wait(NULL)) != -1 ) continue;

wait():

#include #include #include #include #include #include int main(int argc, char **argv) { pid_t pid = fork(); if (pid == 0) { static int cnt = 0; while(1) { cnt++; printf("child is running %d\r\n", cnt); if (cnt >= 10) { cnt = 0; exit(0); } sleep(1); } } if (pid > 0) { int status; pid_t child_pid; child_pid = wait(&status); //一直阻塞直到子进程退出 printf("child pid = %d, exit value = %d\r\n", child_pid, status); } return 0; }

waitpid():

#include #include #include #include #include #include int main(int argc, char **argv) { pid_t pid = fork(); if (pid == 0) { static int cnt = 0; while(1) { cnt++; printf("child is running %d\r\n", cnt); if (cnt >= 10) { cnt = 0; exit(0); } sleep(1); } } if (pid > 0) { int status; int f_cnt = 0; pid_t child_pid; while (1) { child_pid = waitpid(pid, &status, WNOHANG); if (child_pid == 0) { printf("father is runing %d\r\n", ++f_cnt); } else if (child_pid == -1) { printf("waitpid error\r\n"); break; } else { printf("child pid = %d, exit value = %d\r\n", child_pid, status); break; } sleep(1); } } return 0; }

waitid():

#include #include #include #include #include #include #include int main(int argc, char **argv) { pid_t pid = fork(); if (pid == 0) { static int cnt = 0; while(1) { cnt++; printf("child is running %d\r\n", cnt); if (cnt >= 10) { cnt = 0; exit(0); } sleep(1); } } if (pid > 0) { siginfo_t info; memset(&info, 0, sizeof(siginfo_t)); int f_cnt = 0; int ret; while (1) { if (ret = waitid(P_PGID, getpgid(getpid()), &info, WEXITED | WNOHANG) == -1) //执行失败 { perror("waitid\r\n"); break; } else if (ret == 0) //执行成功 { if (info.si_pid == 0) //没有子进程状态发生改变 { printf("no children changed state %d\r\n", f_cnt++); } else { printf("child pid = %d status = %d\r\n", pid, info.si_status ); } } sleep(1); } } return 0; } SIGCHLD信号

子进程终止属于异步事件,父进程无法预知子进程何时终止,即使父进程向子进程发送SIGKILL信号。无论子进程何时终止,系统都会向父进程发送SIGCHLD信号

是信号就默认处理,或忽略,或设置处理程序捕获处理它

问题:

相继多个子进程终止,即使产生多次SIGCHLD信号,父进程也只能捕获一个,那么就会导致一些子进程成为僵尸进程

解决办法:

在SIGCHLD信号的处理函数内部以WNOHANG标志调用waitpid()

while(waitpid(-1, NULL, WNOHAND) > 0) continue;

waitpid()返回0表明无僵尸子进程存在。返回-1表示发生有错误(可能是ECHILD即没有子进程)



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3