Linux实验三进程的管理

原文章

实验3 进程的管理

一、实验内容
实验一
 编写代码,实现以下功能:
 打印当前所有环境变量的值;
 添加新的环境变量NEWENV=first;
 修改环境变量NEWENV的值为second;
 打印环境变量NEWENV的值。

extern是全局变量声明。
只要声明全局变量就默认 前面加extern(程序员可以不加,但编译器默认加上)

当函数定义里有extern关键字,则表示该函数的定义可能在其它的源文件中。

#include 
#include 
#include 
#include 
extern char **environ;
 
int main()
{
	char **env = environ;
	while(*env)
	{
		printf("The env is: %s\n",*env);
		env++;
	}
 
	putenv("NEWENV=first");
	
	char *str;
	str = getenv("NEWENV");
	printf("The NEWENV is: %s\n",str);
 
	if(setenv("NEWENV","second",1) < 0)
	  perror("setenv");
	
	str = getenv("NEWENV");
	printf("The NEWENV is: %s\n",str);
 
	return 0;
}

实验二
 编写代码实现以下功能:
 1.打印字符串“hello world!”
 2.在打印字符串“hello world!”前调用三次fork,分析打印结果。

编写代码实现以下功能:
打印字符串“hello world!”
在打印字符串“hello world!”前调用三次fork,分析打印结果。

我们在编写程序之前,我们先分析一下,应该会打印出几个“hello world”。

首先我们要了解fork函数的工作机制。首先,调用一次fork函数会返回两个返回值,父进程会返回子进程的pid,子进程会返回0。

父子进程都会执行fork函数下的第一条语句。

父子进程的执行顺序是不确定的,取决于操作系统的调度。

综上所诉,调用三次fork数后,将会打印八个“hello world”。
Linux实验三进程的管理_第1张图片

#include 
#include 
#include 
#include 
int main()
{
 
	fork();
	fork();
	fork();
 
	printf("hello world!!!\n");
	return 0;
}

实验三
 创建子进程
 1.在子进程中打开文件file1,写入自己的“班级_姓名_学号”,
 2.父进程读取file1中的内容,并且打印显示。
 3.在父进程中获取已经结束的子进程的状态信息,打印该信息,并且打印结束的子进程的进程号。

由实验二我们了解到调用fork函数后,父进程会返回子进程的pid,子进程则会返回0。因此,在调用fork函数后,只要加上一个判断语句,就能确定是在父进程还是子进程了。

要想获取到已经结束的子进程的状态信息,需要我们用到wait函数并结合相关宏将子进程的退出状态打印。

wait函数的执行过程如下:

程序如下:

#include 
#include 
#include 
#include 
#include 
int main()
{
	int fd,pid;
	fd = open("file",O_CREAT|O_RDWR,S_IRWXU);
	if(fd< 0)
	  perror("open");
 
	pid = fork();
 
	if(pid  == 0)
	{
		printf("This is the child!\n");
		char str[128] = "yidongyiban_wangguangjie_1715925494";
		
		if(write(fd,str,128) < 0)
		  perror("write");
 
		exit(5);
	}
	else
	{
		printf("This is the father!\n");
		
		char buf[128];
		int n,status;
		if(read(fd,buf,128) < 0)
		  perror("read");
		printf("The buf is: %s\n",buf);
 
		if(wait(&status) < 0)
		  perror("perror");
 
		if(WIFEXITED(status))
		  n = WEXITSTATUS(status);
		else
		  printf("wait error!\n");
 
		printf("The child's pid is: %d\n",pid);
		printf("The child exit status is: %d\n",n);
	}
	return 0;
}

其中,WIFEXITED宏用来检测子进程是否正常结束,成功则返回非零值,否则为0;

WEXITSTATUS宏则会返回子进程的结束状态,例如:子进程通过exit(5)退出,则它的返回值就是5。

实验四
 编写程序实现以下功能:
 1,在父进程中定义变量n,在子进程中对变量n进行++操作;并且打印变量n的值,打印子进程pid;
 2,在父进程中打印变量n的值,并且打印父进程pid。
 3,要求分别用fork和vfork创建子进程。

在进行程序编写之前,我们要明白vfork和fork的区别。

1.vfork产生的子进程和父进程共享代码段和数据段,而fork产生的子进程是拷贝父进程的数据段和代码段。

2.vfork产生的子进程的一定比父进程先运行,而fork产生的子进程和父进程的执行顺序不一定,由操作系统的调度决定。

3.vfork产生的子进程调用exit或exec族函数之后才能对父进程进行调度,如果在此之前子进程的运行依赖父进程的进一步执行结果,则会照成死锁。

程序结果分析:假设变量n的值为1,在vfork函数中,父进程打印的n值是2,而fork函数中的父进程打印的n值是1。

fork.c程序如下:

#include 
#include 
#include 
#include 
 
int main()
{
	int n = 1;
	if(fork() == 0)
	{
		printf("This is child,the pid is%d\n",getpid());
		printf("The n is: %d\n",++n);
	}
	else
	{
		printf("This is father,the pid is%d\n",getpid());
		printf("The n is: %d\n",n);
	}
 
	return 0;
}

vfork.c程序如下:

#include 
#include 
#include 
int main()
{
	int n = 1;
	pid_t pid;
	pid = vfork();
	if(pid < 0)
	  perror("vfork");
	else if(pid == 0)
	{
		printf("This is child,the child's pid is: %d\n",getpid());
		printf("The n is: %d\n",++n);
		exit(0);
	}
	else
	{
		printf("This is father,the father's pid is: %d\n",getpid());
		printf("The n is: %d\n",n);
	}
	return 0;
}

通过两个程序的运行结果我们可以发现,之前的推理是正确的。

注意,在试验中的n的自加必须用++n。因为如果用n++,两个父进程输出的n值都会是1。

实验完成
实验五
 创建子进程一,在子进程中递归打印/home目录中的内容(用exec系列函数调用第二次实验中的代码完成此功能);
 1.子进程结束的时候完成以下功能:
 打印字符串“Child process exited!”
 打印子进程标识符,打印父进程标识符。

在程序编写之前,我们应该对exec族函数的功能进行一定的了解。

exec族函数的作用是在进程中,通过文件名找到可执行文件(二进制文件或脚本文件)并对其进行调用。一个进程一旦调用exec族的函数,它本身就已经结束了。系统会将原来进程的代码段更新,并废弃其数据段和堆栈段。唯一没有被改变的就是该进程的pid,因此,对于操作系统来说,进程并没有结束,只是更换了新的程序。

于此同时,我们要清楚如何在进程结束时将字符串打印。利用atexit登记函数,可以实现题目要求。

atexit的作用是程序可以在进程结束时完成对进程资源的清理工作,通过登记自定义函数来实现清理工作。注意:先登记的后执行,即登记顺序和执行顺序是相反的。同时,_exit的调用不能触发atexit登记的函数。

atexit函数实例如下:

#include 
#include 
#include 
#include 
void test1(void )
{
	printf("This is the test1!\n");
}
 
void test2(void)
{
	printf("This is the test2!\n");
}
 
int main()
{
	if(fork() == 0)
	{
		printf("This is the child!\n");
		atexit(test2);
		atexit(test1);
		exit(1);
	}
	else
	{
		printf("This is father!\n");
	}
	return 0;
}

 2. 创建子进程二, 打印子进程运行环境中环境变量“USER”的值,通过exec系列中的某个函数设置子进程”USER”环境变量值为“zhangsan”,并且让该子进程完成以下命令:“ls –li /home”.

通过程序运行结果我们发现,函数的执行顺序确实是和登记顺序相反的。

要想让子进程二执行“ls -li /home”,则可以利用system函数执行。

程序如下:

#include 
#include 
#include 
 
void fun(void)
{
	printf("~~~~~~~~~~~~~~~~~~~~~~\n")printf("Child process exited!!!\n");
	printf("The child's pid is: %d\n",getpid());
	printf("The father's pid is %d\n",getppid());
	printf("~~~~~~~~~~~~~~~~~~~~~~\n");
}
		
int main()
{
	pid_t pid;
	pid = vfork();
 
	if(pid <0)
	  perror("vfork");
	else if(pid == 0)
	{
		printf("This is the child1 !!!\n");
		atexit(fun);
 
		if((execl("/home/wang/test/file/test6/test","test",NULL)) < 0)
		{
		  perror("execl");
		  exit(0);
		}
	}
	else
	{
		printf("This is the father !!!\n");
		if(vfork() == 0)
		{
			printf("This is the child2 !!!\n");
			printf("The child2's father's pid is: %d\n",getppid());
			char * env[] = {"USER=zhangsan",NULL};
			char *p;
			
			p = getenv("USER");
			if(p)
			{
				printf("The user is: %s\n",p);
			}
 
			system("ls -li /home");
			if((execle("/bin/env","env",NULL,env)) < 0)
			  perror("execle");
			
			exit(1);
		}
	}
	return 0;
}

在我的Fedora中运行程序发现,只用vfork函数创建的子进程中才能成功调用atexit函数,而fork函数创建的子进程下不能实现对atexit函数的调用,但是在我的ubuntu系统中却能正常运行。

你可能感兴趣的:(Linux,实验三,进程的管理)