fork

fork

1. 进程标志

#include <unistd.h>

#include <sys/types.h>

uid_t getpid(void);

uid_t getppid(void);

uid_t getuid(void);

uid_t geteuid(void);

gid_t getgid(void);

git_t getegid(void);
struct passed *getpwuid(uid_t uid);

struct passwd {

char *pw_name; /* 登录名称 */

char *pw_passwd; /* 登录口令 */

uid_t pw_uid; /* 用户 ID */

gid_t pw_gid; /* 用户组 ID */

char *pw_gecos; /* 用户的真名 */

char *pw_dir; /* 用户的目录 */

char *pw_shell; /* 用户的 SHELL */

};

#include <pwd.h>;

#include <sys/types.h>;

2. fork

pid_t fork();

当 fork 掉用失败的时候(内存不足或者是用户的最大进程数已到)fork 返回-1.父进程中返回子进程id,子进程返回0.

3. wait和waitpid

#include <sys/types.h>;

#include <sys/wait.h>;

pid_t wait(int *stat_loc);

pid_t waitpid(pid_t pid,int *stat_loc,int options);

两个系统调用用于等待子进程状态改变,并获取状态信息。如果该进程没有子进程或子进程已经结束,则wait/waitpid就会立即返回。

wait()阻塞执行直到子进程终止。

waitpid()默认情况下仅仅等待终止的子进程。但可通过options指定另外两种子进程状况改变。状态改变包含: the child terminated(默认,options=0); the child was stopped by a signal(options=WUNTRACED); or the child was resumed by a signal (options=WCONTINUED). 在子进程终止情况下,wait/waitpid准许系统释放子进程相关资源;若没有调用wait或waitpid子进程变为僵尸进程。

wait <==> waitpid(-1, &status, 0);

假如chilid已经改变状态,wait/waitpid会立即返回,否则两函数阻塞直到子进程状态改变或两函数被中断。若子进程状态改变,但还没有被wait/waitpid,则该进程被称为waitable。

waitpid()中pid取值范围如下:

< -1 等子进程中进程组ID等于pid绝对值的任一进程。

-1 等任一子进程。

0 等进程组ID等于调用进程组ID的任一进程。

> 0 等指定pid的状态改变。

waitpid()的options为0或(|)下列值:

WNOHANG,父进程不阻塞直接返回。

WUNTRACED,假如一个子进程停止(不能通过ptrace跟踪)就返回。若此选项不指定,则可追踪(traced)的已停止子进程的状态被返回。

WCONTINUED,一个已停止的进程由于收到信号SIGCONT而恢复则返回。

stat_loc保存子进程退出状态(一般exit,_exit,return),是一个整型指针,存储退出状态。若stat_loc不为NULL,则wait/waitpid储存退出状态(int型)在stat_loc中。可通过下列宏判断,参数为整型数,非指针。

WIFEXITED(status) return true假如子进程正常终止(exit,_exit, return).

》》WEXITSTATUS(status) 返回子进程的退出状态,其由status的最低8bits组成。仅当WIFEXITED返回真时有效。

WIFSIGNALED(status) 返回真,假如子进程被信号终止。

》》WTERMSIG(status) 返回信号数字值(造成子进程终止的信号),仅当WIFSIGNALED为真时有效。

》》WCOREDUMP(status) 返回真,假如子进程生成core dump。仅当WIFSIGNALED为真时有效。

WIFSTOPPED(status) 返回真,假如子进程被信号stopped。仅当options包含WUNTRACED或子进程正在被traced时有效。

》》WSTOPSIG(status) 返回信号数字值(造成子进程stop的信号)。仅当WIFSTOPPED为真时有效。

WIFCONTINUED(status) 返回真,假如子进程被SIGCONT恢复。

RETURN VALUE

wait(): on success, returns the process ID of the terminated child; on error, -1 is returned

waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned.On error, -1 is returned.

4. SIGCHLD

SIGCHLD信号产生的条件

1.子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己,但该信号的默认处理动作为忽略,因此父进程仍然不会去回收子进程,需要捕捉处理实现子进程的回收;

(注意:需要注意的是,虽然进程对于 `SIGCHLD`的默认动作是忽略,但是还是显示写出来,才能有效;signal(SIGCHLD, SIG_IGN),这样子进程直接会退出。)

2.子进程接收到SIGSTOP(19)信号停止时;

3.子进程处在停止态,接受到SIGCONT后唤醒时。

综上:子进程结束、接收到SIGSTOP停止(挂起)和接收到SIGCONT唤醒时都会向父进程发送SIGCHLD信号。父进程可以捕捉该信号,来实现对子进程的回收,或者了解子进程所处的状态。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
 
void sys_err(char *str)
{
    perror(str);
    exit(1);
}
 
void do_sig_child(int signo)
{
    int status;
    pid_t pid;

// 不可以将捕捉函数内部的while替换为if。因为在执行捕捉函数期间,发送了多次SIGCHLD信号,未决信号集只是记录了一次
// 因此下一次再调用捕捉函数时,if只能完成对一个子进程的回收(即使有多个子进程都发了信号,但是只是调用一次捕捉函数)。
// 而while循环则可以对所有结束了的子进程都完成回收。因此对于多个子进程的回收,最好采用循环的方式,不采用if。
// if ((pid = waitpid(0, &status, WNOHANG)) > 0) { while ((pid = waitpid(0, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) printf("------------child %d exit with %d ", pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("child %d killed by the %dth signal ", pid, WTERMSIG(status)); } } int main(void) { pid_t pid; int i; //阻塞SIGCHLD for (i = 0; i < 10; i++) { if ((pid = fork()) == 0) break; else if (pid < 0) sys_err("fork"); } if (pid == 0) { //10个子进程 int n = 1; while (n--) { printf("child ID %d ", getpid()); sleep(1); } return i+1; //子进程结束状态依次为1、2、••••••、10 } else if (pid > 0) { struct sigaction act; act.sa_handler = do_sig_child; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGCHLD, &act, NULL); //解除对SIGCHLD的阻塞 while (1) { printf("Parent ID %d ", getpid()); sleep(1); } } return 0; }

父子进程信号处理:

1.子进程继承了父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集spending。

2.注意注册信号捕捉函数的位置。

3.应该在fork之前,阻塞SIGCHLD信号。注册完捕捉函数后解除阻塞。

参考:SIGCHLD信号 Linux: 关于 SIGCHLD 的更多细节

5. 僵尸进程危害

1)占用资源(少量);2)难以清除(杀死僵尸进程父进程)。

如果一个父进程终止,而子进程还存在(仍运行或僵尸进程),子进程的父进程改为init进程,子进程终止时,init调用wait函数清理它。

原文地址:https://www.cnblogs.com/embedded-linux/p/5011222.html