进程管理实验【1】

╄→гoц情女王★ 提交于 2019-12-15 06:43:27

fork函数:

fork函数用于创建一个新进程,称为子进程,它与调用它的进程同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器。

它不需要参数并返回一个整数值,下面是fork()返回的不同值:

        负值:创建子进程失败。
  :返回到新创建的子进程。
  正值:返回父母或来电者。该值包含新创建的子进程的进程ID

我们来看一个程序:

#include<stdio.h>
#include<unistd.h>
int main()
{
	pid_t pid ;
	pid = fork();	//fork一个进程
	int count = 0 ;
	if(pid == 0) {               //pid为0,
		printf("this is child process, pid is %d\n",getpid());//getpid返回的是当前进程的PID
		count++;
		printf("count = %d\n",count);
	} 
	else if(pid > 0) {
                printf("返回值是%d\n", pid);
		printf("this is father process, pid is %d\n",getpid());
		count++;
		printf("count = %d\n",count);
	} 
	else {
		fprintf(stderr,"ERROR:fork() failed!\n");
	}
	return 0;
}

其输出结果是:

可以发现0的返回值对应子进程,大于零且刚好是子进程的pid的返回值是父进程。

再来看一个程序:

#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h> 
#include <signal.h>
#include <ctype.h> 
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10 
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2 

int proc_number=0; /* 子进程的自编号,从0开始 */
void do_something(); 

main(int argc, char* argv[])
{ 
	int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
	int i, ch; 

	pid_t child_pid; 
	pid_t pid[10]={0}; /* 存放每个子进程的id */ 

	if (argc > 1)
	{	 
		/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */ 
		child_proc_number = atoi(argv[1]);	//将字符串转换成整数 
		child_proc_number = (child_proc_number > 10) ? 10 : child_proc_number;
	}	
	for (i=0; i<child_proc_number; i++)
	{ 
		/* 建立child_proc_number个子进程*/
		child_pid = fork() ;
		if(child_pid == 0){
			proc_number = i ;
			do_something() ;
			break ;
		}
		* 父进程把子进程的id保存到pid[i] */
		pid[i] = child_pid ;
	} 
	/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
	* 输入q退出 */
	while ((ch = getchar()) != 'q')
	{ 
		if (isdigit(ch)) 
		{ 
			/* 向pid[ch-'0']发信号SIGTERM,杀死该子进程 */ 
			kill(pid[ch-'0'], SIGTERM) ;
		}
	} 
	/* 杀死本组的所有进程 */ 
	kill(0, SIGTERM) ;
	return;
} 

void do_something()
{ 
	for(;;)
	{ 
		/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
		* 比如号码是1,就打印" 1" */
		printf("This is process No.%*d\n",
		proc_number+3, 
		proc_number); 
		sleep(2); /* 主动阻塞两秒钟,也给其他进程执行的机会 */
	} 
} 

看不懂没关系,我一步一步说:

前面的宏定义和声明就不看了,一直到第一个for循环,其实都是一些赋值语句,你只需要明白下面的这个for循环要创建child_proc_number个子进程。

	for (i=0; i<child_proc_number; i++)
	{ 
		/* 建立child_proc_number个子进程*/
		child_pid = fork() ;
		if(child_pid == 0){
			proc_number = i ;
			do_something() ;
			break ;
		}
		* 父进程把子进程的id保存到pid[i] */
		pid[i] = child_pid ;
	} 

当执行到for循环时,我们用一个child_pid接受返回值,若其值为0则子进程执行if条件里的语句(我在语句结束时加了个break,一会再说),那么它会执行下面的两句代码:

proc_number = i ;
do_something() ;

我们知道proc_number是一个全局变量,我们将i,也就是创建的第几个子进程的进程号赋值给了proc_number,下一句是一个函数do_something():

void do_something()
{ 
	for(;;)
	{ 
		/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
		* 比如号码是1,就打印" 1" */
		printf("This is process No.%*d\n",
		proc_number+3, 
		proc_number); 
		sleep(2); /* 主动阻塞两秒钟,也给其他进程执行的机会 */
	} 
} 

可以发现,这是一个死循环,子进程不断的打印它的进程号,而一共有child_proc_number个子进程被创建而且它们都执行这段代码,只是其中的proc_number(进程号)不一样。看完子进程我们回到第一个for循环:

	for (i=0; i<child_proc_number; i++)
	{ 
		/* 建立child_proc_number个子进程*/
		child_pid = fork() ;
		if(child_pid == 0){
			proc_number = i ;
			do_something() ;
			break ;
		}
		* 父进程把子进程的id保存到pid[i] */
		pid[i] = child_pid ;
	} 

可以发现父进程每创建一个子进程,会将其child_pid(系统内部标识--pid)保存在数组pid[]内,当创建了child_proc_number个子进程后循环结束,父进程执行如下代码:

	/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
	* 输入q退出 */
	while ((ch = getchar()) != 'q')
	{ 
		if (isdigit(ch)) 
		{ 
			/* 向pid[ch-'0']发信号SIGTERM,杀死该子进程 */ 
			kill(pid[ch-'0'], SIGTERM) ;
		}
	} 
	/* 杀死本组的所有进程 */ 
	kill(0, SIGTERM) ;
	return;

接下来的代码比较好理解,键盘接受用户的一个输入,杀死对应的一个子进程,若输入1则杀死1号进程,若输入q则退出系统,kill()函数是杀死子进程的函数,接收参数为子进程pid时杀死对应子进程,接收0时杀死本组的所有子进程,若输入q,则会传0杀死本组所有子进程,以防多余的资源占用。

程序分析到这其实我们已经可以预测这个程序的功能了:

child_proc_number个子进程每隔两秒打印打印一次自己的进程号,而父进程等待用户杀死进程的输入,若按1则杀死1号进程,则一号进程不会再打印自己的pid,若按q则退出系统,程序结束。

我们来看实际运行结果:

从图中我们发现基本与我们预想的一致,但还可以发现进程们的运行顺序是无序的。

讲完以上部分,我再来说说那个if语句里的break是什么作用?

部分人问,这个break根本没有用为啥还要加在程序里?

我们接着看:

	for (i=0; i<child_proc_number; i++)
	{ 
		/* 建立child_proc_number个子进程*/
		child_pid = fork() ;
		if(child_pid == 0){
			proc_number = i ;
			do_something() ;
			break ;
		}
		* 父进程把子进程的id保存到pid[i] */
		pid[i] = child_pid ;
	} 

我们设想,若子进程执行的do_someing()函数是这样的:

void do_something(){
    printf("I'm child process No.%d\n", proc_number);
}

程序会变成什么样?

我们来分析:

主进程创建了第一个子进程后,这个子进程会执行do_something,do_something只打印一个进程号后结束返回主函数,若是没有break此时的子进程会执行和父进程相同的代码,它同样会创建子进程,所以为了避免创建过多的子进程(这些是删不掉的),我加了一个break跳出循环,这就是break的作用。

最后有一个小问题,如果不加break,会创建多少个子进程?(滑稽)

这个留给大家来分析0.0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!