计算机基础学习日志(九) 进程控制及fork函数分析

计算机基础学习日志(九) 进程控制及fork函数例子分析

  • 进程控制过程
    • 进程ID
    • 创建进程
    • 终止进程
    • 回收子进程
  • fork函数的一些例子
    • fork1
    • fork2
    • fork4
    • fork5
    • fork6
    • fork7
    • fork8
    • fork9
    • fork10
    • fork11

进程控制过程

进程ID

唯一的非零正数:PID。
getpid函数返回调用进程的PID,getppid返回父进程的PID。

#include
#include

pid_t getpid(void);
pid_t getppid(void);

创建进程

父进程通过调用fork函数创建一个新的运行的子进程。 返回值有三种:0,大于0的数,-1表示出错。

一次调用,两次返回:子进程返回0,父进程返回大于0的数。

父进程和子进程是并发运行的独立进程。内核能够以任意方式交替执行他们的逻辑控制流中的指令。

父进程和子进程拥有相同但独立的地址空间,同时子进程继承了父进程所有的打开文件。

终止进程

进程永远停止。进程有三种停止的原因:
1.收到终止进程的信号;
2.从主程序返回;
3.调用exit函数。

exit函数以status退出状态来终止进程。

回收子进程

ps -ef //显示当前PID和爷爷shell的PID

waitpid函数挂起调用进程,直到等待集合中的一个子进程终止,返回值为导致其返回的已终止子进程的PID。

重要的宏:

修改默认行为——
WNOHANG: 挂起调用进程,直到有子进程终止。立即返回,子进程未终止则返回0
WUNTRACED: 挂起调用进程的执行,直到有子进程终止。返回的PID为导致返回的子进程的PID。

常见的方式为组合使用:
WNOHANG | WUNTRACED: 立即返回,如果等待集合中的子进程都没有被停止或终止,则返回值为0;如果有一个停止或终止,则返回值为该子进程的PID。

检查已回收子进程的退出状态——
WIFEXITED(status): wait if exited 如果子进程通过调用exit或者一个返回(return)正常终止,就返回真。
WEXITSTATUS(status): 返回一个正常终止的子进程的退出状态。只有在WIFEXITED()返回为真时,才会定义这个状态。

fork函数的一些例子

fork1

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函数分析_第1张图片运行结果:在这里插入图片描述

fork2

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

进程图:
计算机基础学习日志(九) 进程控制及fork函数分析_第2张图片
运行结果:计算机基础学习日志(九) 进程控制及fork函数分析_第3张图片

fork4

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

进程图:计算机基础学习日志(九) 进程控制及fork函数分析_第4张图片

运行结果:
计算机基础学习日志(九) 进程控制及fork函数分析_第5张图片

fork5

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

进程图:
计算机基础学习日志(九) 进程控制及fork函数分析_第6张图片
运行结果:计算机基础学习日志(九) 进程控制及fork函数分析_第7张图片

fork6

void cleanup(void) {
    printf("Cleaning up\n");
}

/*
 * fork6 - Exit system call terminates process
 * call once, return never
 */
void fork6()
{
    atexit(cleanup);
    fork();
    exit(0);
}

atexit函数可以进行程序退出时的必要处理,用atexit()函数来注册程序正常终止(也就是通过exit(0)、_exit(0)或return结束的程序)时要被调用的函数。
exit函数运行时首先会执行由atexit()函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流并且关闭通过标准I/O函数tmpfile()创建的临时文件,最后调用_exit系统函数。
参考博客:
https://blog.csdn.net/tf_apologize/article/details/53162218

进程图:
计算机基础学习日志(九) 进程控制及fork函数分析_第8张图片
运行结果:在这里插入图片描述

fork7

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 */
    }
}

子进程正常退出,父进程最后进入死循环。

进程图:
计算机基础学习日志(九) 进程控制及fork函数分析_第9张图片
运行结果:
在这里插入图片描述

fork8

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);
    }
}

父进程正常退出,子进程最后进入死循环。

进程图:计算机基础学习日志(九) 进程控制及fork函数分析_第10张图片

运行结果:
在这里插入图片描述

fork9

计算机基础学习日志(九) 进程控制及fork函数分析_第11张图片

/*
 * fork9 - synchronizing with and reaping children (wait)
 */
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");
}

进程图:
计算机基础学习日志(九) 进程控制及fork函数分析_第12张图片
运行结果:计算机基础学习日志(九) 进程控制及fork函数分析_第13张图片

fork10

#define N 5
/* 
 * fork10 - Synchronizing with multiple children (wait)
 * Reaps children in arbitrary order
 * WIFEXITED and WEXITSTATUS to get info about terminated children
 */
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);
    }
}

for循环中执行5次fork函数生成5个子进程,子进程以exit退出。父进程在另一个for循环中wait函数挂起调用进程的执行,直到等待集合中的一个子进程结束。

进程图:

运行结果:
在这里插入图片描述

fork11

#define N 5
/* 
 * fork11 - Using waitpid to reap specific children
 * Reaps children in reverse order
 */
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);
    }
}

与fork10相同,for循环中执行5次fork函数生成5个子进程,子进程以exit退出。但之后父进程在另一个for循环中waitpid函数默认情况下(options=0时)waitpid挂起调用进程的执行,直到等待集合中的一个子进程结束。

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

返回:如果成功返回子进程PID,如果WNOHANG,则为0,如果其他错误,则为-1.
pid_t wait(int *statusp);

返回:如果成功返回子进程PID,如果其他错误,则为-1.

调用wait(&status)等价于调waitpid(-1,&status,0)。

进程图:

运行结果:
在这里插入图片描述

你可能感兴趣的:(计算机基础学习日志)