关于fork的习题

第0题

void fork0() 
{
    if (fork() == 0) {
	printf("Hello from child\n");
    }
    else {
	printf("Hello from parent\n");
    }
}

编译运行

进程结构图

第1题

void fork1()
{
    int x = 1;
    pid_t pid = fork();

    if (pid == 0) {
	printf("Child has x = %d\n", ++x);
    } 
    else {
	printf("Parent has x = %d\n", --x);
    }
    printf("Bye from process %d with x = %d\n", getpid(), x);
}

编译运行
在这里插入图片描述
进程结构图
在这里插入图片描述
因为fork()后子进程将复制父进程结构所以子进程的x不受父进程的影响

第2题

void fork2()
{
    printf("L0\n");
    fork();
    printf("L1\n");    
    fork();
    printf("Bye\n");
}

编译运行
在这里插入图片描述
进程结构图
在这里插入图片描述

第3题

void fork3()
{
    printf("L0\n");
    fork();
    printf("L1\n");    
    fork();
    printf("L2\n");    
    fork();
    printf("Bye\n");
}

编译运行
在这里插入图片描述
进程结构图
在这里插入图片描述
只是比上一题多了一个fork()稍微复杂一点

第4题

void fork4()
{
    printf("L0\n");
    if (fork() != 0) {
	printf("L1\n");    
	if (fork() != 0) {
	    printf("L2\n");
	}
    }
    printf("Bye\n");
}

编译运行
在这里插入图片描述
进程结构图
在这里插入图片描述
子进程fork=0所以第一个子进程无法输出L1也没法继续fork,第二个无法输出L2

第5题

void fork5()
{
    printf("L0\n");
    if (fork() == 0) {
	printf("L1\n");    
	if (fork() == 0) {
	    printf("L2\n");
	}
    }
    printf("Bye\n");
}

编译运行
在这里插入图片描述
进程结构图与上一题一样,区别在于一个是!=另一个是==

第6题

void cleanup(void) {
    printf("Cleaning up\n");
void fork6()
{
    atexit(cleanup);
    fork();
    exit(0);
}

编译运行
在这里插入图片描述
进程结构图在这里插入图片描述
函数名: atexit

头文件:#include<stdlib.h>

功 能: 注册终止函数(即main执行结束后调用的函数)

用 法: int atexit(void (*func)(void));

注意:一个进程可以登记32个函数,这些函数由exit自动调用,这些函数被称为终止处理函数,atexit函数可以登记这些函数。 exit调用终止处理函数的顺序和atexit登记的顺序相反,如果一个函数被多次登记,也会被多次调用。

第7题

void fork7()
{
    if (fork() == 0) {
	/* Child */
	printf("Terminating Child, PID = %d\n", getpid());
	exit(0);
    } else {
	printf("Running Parent, PID = %d\n", getpid());
	while (1)
	    ; /* Infinite loop */
    }
}

在这里插入图片描述
进程结构图
在这里插入图片描述
父进程将一直在运行除非使用kill命令才会运行子进程
getpid函数是显示进程号

第8题

void fork8()
{
    if (fork() == 0) {
	/* Child */
	printf("Running Child, PID = %d\n",
	       getpid());
	while (1)
	    ; /* Infinite loop */
    } else {
	printf("Terminating Parent, PID = %d\n",
	       getpid());
	exit(0);
    }
}

编译运行
在这里插入图片描述
进程结构图
在这里插入图片描述
这次轮到子进程进入死循环,但父进程已经运行完毕退出,所以子进程将一直运行,除非使用kill命令杀死它

第9题

void fork9()
{
    int child_status;

    if (fork() == 0) {
	printf("HC: hello from child\n");
        exit(0);
    } else {
	printf("HP: hello from parent\n");
	wait(&child_status);
	printf("CT: child has terminated\n");
    }
    printf("Bye\n");
}

程序编译
在这里插入图片描述
进程结构图
在这里插入图片描述
wait()函数一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。
如果一个子进程已经终止,并且是一个僵尸进程,则wait立即返回并取得该子进程的终止状态,否则wait使其调用者阻塞直到一个子进程终止。
如果调用者阻塞而且它有多个子进程,则在其一个子进程终止时,wait就立即返回。
因为wait的返回值是终止进程的进程ID,所以父进程总能知道哪一个子进程终止了。

第10题

void fork10()
{
    pid_t pid[N];
    int i, child_status;

    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0) {
	    exit(100+i); /* Child */
	}
    for (i = 0; i < N; i++) { /* Parent */
	pid_t wpid = wait(&child_status);
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminate abnormally\n", wpid);
    }
}

程序编译
在这里插入图片描述
这题目的进程结构图不太好画,先看一下这两个函数:
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
因为一个for循环,循环了五次,每次的fork返回一个子进程一个父进程,子进程调用exit直接退出,父进程每次调用wait返回子进程的状态,当结束时调用输出语句,输出多少进程号的子进程结束,并伴有状态码。

第11题

void fork11()
{
    pid_t pid[N];
    int i;
    int child_status;

    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0)
	    exit(100+i); /* Child */
    for (i = N-1; i >= 0; i--) {
	pid_t wpid = waitpid(pid[i], &child_status, 0);
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminate abnormally\n", wpid);
    }
}

编译运行
在这里插入图片描述
与上一题的区别是这边用的是waitpid,在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞;
waitpid()并不等待在其调用之后的第一个终止的子进程,它有若干个选项,可以控制它所等待的进程。
而这里父进程的循环是反着的,因此输出子进程的进程号也是从大到小输出的

第12题

void fork12()
{
    pid_t pid[N];
    int i;
    int child_status;

    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0) {
	    /* Child: Infinite Loop */
	    while(1)
		;
	}
    for (i = 0; i < N; i++) {
	printf("Killing process %d\n", pid[i]);
	kill(pid[i], SIGINT);
    }

    for (i = 0; i < N; i++) {
	pid_t wpid = wait(&child_status);
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminated abnormally\n", wpid);
    }
}

编译运行
在这里插入图片描述
先来看一下kill函数
[ KILL ]功能描述:
用于向任何进程组或进程发送信号。

头文件用法:

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int sig);

参数:
pid:可能选择有以下四种

  1. pid大于零时,pid是信号欲送往的进程的标识。
  2. pid等于零时,信号将送往所有与调用kill()的那个进程属同一个使用组的进程。
  3. pid等于-1时,信号将送往所有调用进程有权给其发送信号的进程,除了进程1(init)。
  4. pid小于-1时,信号将送往以-pid为组标识的进程。

sig:准备发送的信号代码,假如其值为零则没有任何信号送出,但是系统会执行错误检查,通常会利用sig值为零来检验某个进程是否仍在执行。

返回值说明: 成功执行时,返回0。失败返回-1,errno被设为以下的某个值 EINVAL:指定的信号码无效(参数 sig 不合法) EPERM;权限不够无法传送信号给指定进程 ESRCH:参数 pid 所指定的进程或进程组不存在

子进程进入死循环,而在父进程中杀死了子进程,子进程是非正常结束的,因此输出的是else里的,杀死输出是顺序从小到大,而else里输出的顺序就是随机的

第13题

void int_handler(int sig)
{
    printf("Process %d received signal %d\n", getpid(), sig); /* Unsafe */
    exit(0);
}
void fork13()
{
    pid_t pid[N];
    int i;
    int child_status;

    signal(SIGINT, int_handler);
    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0) {
	    /* Child: Infinite Loop */
	    while(1)
		;
	}

    for (i = 0; i < N; i++) {
	printf("Killing process %d\n", pid[i]);
	kill(pid[i], SIGINT);
    }

    for (i = 0; i < N; i++) {
	pid_t wpid = wait(&child_status);
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",
		   wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminated abnormally\n", wpid);
    }
}

在这里插入图片描述
signal的参数:
SIGINT (信号中断)交互式注意信号。通常由应用程序用户生成。

父进程调用kill时发出信号2,即进程中断,触发处理信号函数 int_handler,结束子进程,最后输出的结果也是正常退出。

第14题

int ccount = 0;
void child_handler(int sig)
{
    int child_status;
    pid_t pid = wait(&child_status);
    ccount--;
    printf("Received SIGCHLD signal %d for process %d\n", sig, pid); /* Unsafe */
    fflush(stdout); /* Unsafe */
}
void fork14()
{
    pid_t pid[N];
    int i;
    ccount = N;
    signal(SIGCHLD, child_handler);

    for (i = 0; i < N; i++) {
	if ((pid[i] = fork()) == 0) {
	    sleep(1);
	    exit(0);  /* Child: Exit */
	}
    }
    while (ccount > 0)
	;
}

在这里插入图片描述
SIGCHLD,在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程,按系统默认将忽略此信号,如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值