Linux进程管理-创建进程与进程回收

  • Post author:
  • Post category:linux






两个函数


创建进程:fork()

暂缓进程执行:sleep()



1.fork()函数


1.使用方法:

#include <unistd.h>
pid_t fork(void);


2.函数功能:


函数执行后,系统会创建一个与原进程几乎相同的进程,之后父子进程都继续执行。子进程创建成功后,原程序会被复制,就有了两个fork函数。若子进程创建失败,原程序不会复制。


3.返回值:


成功:返回两个值:

  1. 父进程的fork函数会返回子进程的pid,
  2. 子进程的fork函数会返回0。

不成功:只有父进程的fork函数返回-1。


Eg.1.创建单个子进程


使用fork函数创建一个进程,创建成功后父子进程分别执行不同的功能。

test_fork.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
	pid_t tempPid;
	tempPid = fork();
	if(tempPid == -1){
		perror("fork error");
	}else if(tempPid > 0){//parent
		printf("parent process, pid = %d, chilpid = %d\n", getpid(), tempPid);
	}else{//child
		printf("child process, pid = %d, ppid = %d\n", getpid(), getppid());
	}//of if
	printf("......finish......");
	return 0;
}//of main


补充说明:


getpid()返回值为调用getpid()进程的PID,而getppid()返回调用这个函数进程的父进程的PID


执行结果:


在这里插入图片描述

在这里插入图片描述


Question1:


?两次执行,结果不同:一个是先调用父进程,一个是先调用子进程

因为两进程优先级相同,两进程同时抢资源,所以执行顺序可能不同。

在这里插入图片描述


Question2:


?发现子进程的ppid并不等于父进程的pid

我们可以先来看一下1412是个什么东西

在这里插入图片描述

/usr/lib/systemd/system目录自动存放启动文件的配置位置

而老师上课的时候提到了init,说会被init进行领养,下面问题变成孤儿进程为什么不是被init领养

在这里插入图片描述

在Ubuntu20.04中:

在这里插入图片描述

所以归根结底还是init领养了子进程

fork()创建子进程后,在两进程抢夺资源的时候,父进程先于子进程被调用,然后父进程被摧毁,子进程接着被调用,但是父进程已经被摧毁,子进程变为孤儿进程,init会“收养”这个子进程所以该子进程的父进程就变为了init。

我们可以和图一进行对比,发现虽然两次运行都是父进程先运行,但是第一次子进程运行的时候父进程并没有结束运行,所以子进程的父进程显示的还是我们的父进程而不是init。

为了防止子进程变为孤儿进程,可以使父进程后于子进程执行,即让父进程等待子进程执行结束后再执行。

此处我们用到sleep()函数:此处我们先卖个关子,在第二点进行详细案例说明。

我们创建了一个子进程,那么要想通过父进程创建多个子进程又该怎么办呢?


Eg.2.创建多个子进程

int i;
for(i = 0; i < 2; i ++){
	tempPid = fork();
}//of for i


执行结果:


在这里插入图片描述

此时会发现创建进程数量与预计的不同,产生了4个进程并且两个父进程,两个子进程。

父进程在创建子进程的同时,之前创建的子进程也会调用fork函数进行子进程的创建,成为了之后创建的进程的父进程,如果只想父进程才进行子进程的创建的话,必须进行判断:如果是父进程才能创建子进程。


改进版:Eg.2.创建多个子进程

test_fork2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
	pid_t tempPid;
	int i;
	for(i = 0; i < 2; i ++){
		if((tempPid = fork()) == 0){
			break;
		}//of if
	}//of for i
	if(tempPid == -1){
		perror("fork error");
	}else if(tempPid > 0){//parent
		printf("parent process, pid = %d, ppid = %d\n", getpid(), getppid());
	}else{//child
		printf("I am child process = %d, pid = %d, ppid = %d\n", i + 1, getpid(), getppid());
	}//of if
	printf("......finish......");
	return 0;
}//of main


执行结果:


在这里插入图片描述



2.sleep()函数


函数功能:


暂缓进程执行


Eg.1.父进程回收全部子进程

test_fork3.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
	pid_t tempPid;
	int i;
	for(i = 0; i < 2; i ++){
		if((tempPid = fork()) == 0){
			break;
		}//of if
	}//of for i
	if(tempPid == -1){
		perror("fork error");
	}else if(tempPid > 0){//parent
		printf("parent process, pid = %d, ppid = %d\n", getpid(), getppid());
		sleep(2);
	}else{//child
	 	sleep(i);
		printf("I am child process = %d, pid = %d, ppid = %d\n", i + 1, getpid(), getppid());
	}//of if
	printf("......finish......");
	return 0;
}//of main


运行结果:


在这里插入图片描述

完美解决了孤儿进程的问题。


补充:

僵尸进程:


僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。僵尸进程会占用进程号。

下面摘自百度百科:

由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。但UNⅨ提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。



版权声明:本文为njrdear原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。