原文章
一、实验内容
实验一
编写代码,实现以下功能:
打印当前所有环境变量的值;
添加新的环境变量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”。
#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系统中却能正常运行。