我在一个二本类院校,对于linux是不在课程范围的,属于个人爱好吧。自己学习了一些,操作系统上学期开了,老师很水,就期末最后两天学了整本书。
上学期去图书馆借了一本《linux内核设计与实现》当时看的是第二版,但是没有看完,其实收获还是挺多的。
说回正题,前两天有个好朋友给我讲他们操作系统还有实验课,当时我就懵了,确实差距大。于是让他把实验课题发给我,我想看看。很多只有做过实验,才能深刻体会。
编制一段程序,使用系统调用fork( )创建两个子进程,这样在此程序运行时,在系统中就有一个父进程和两个子进程在活动。每一个进程在屏幕上显示一个字符,其中父进程显示字符A,子进程分别显示字符 B和字符C。试观察、记录并分析屏幕上进程调度的情况。
设计思路:
进程的出生:
随着一句fork,一个新进程呱呱落地,但它这时只是老进程的一个克隆。
调用fork创建的子进程,将共享父进程的代码空间,复制父进程数据空间,此时子进程会获得父进程的所有变量的一份拷贝。
fork()返回值意义如下:
=0:在子进程中,表示当前进程是子进程。
>0:在父进程中,返回值为子进程的id值(唯一标识号)。
-1:创建失败。
再在返回的father进程里面调用fork()创建一个新的进程son2.
然后分别在进程打印A,B,C,然后实验一下。
看看运行结果:
孤儿进程
父进程在子进程结束之前死亡(return或者exit)
在一定时间内,当系统发现孤儿进程时,init进程就收养孤儿进程,成为它的father,child进程exit后资源回收都又init进程完成。
僵死进程
子进程在父进程之前结束了,但是父进程没有用wait或waitpid回收子进程
为了避免child成为僵死进程,可以人为的杀死父进程,可以让其成为孤儿进程被init接管,但是如果父进程是服务器进程或者一些重要的进程不能立刻被杀死的话,僵死进程就一直存在,影响系统性能,所以为了避免这种情况,要调用两次fork(),让孙子线程成为工作线程,如果它成为僵死线程的话,可以杀死它的父进程。
在实验中,我还设置了一个数count,是为了实验fork创建的子进程,每个进程是父进程的一份复制,但是又有自己的空间,每个进程都有自己的变量。
son1创建时,会复制变量count=0,但是count是存在son1的数据段中,所以son1的count和father以及son2拥有不同的地址,每个count是独立的,不是共用的。
(但是这个程序存在一个问题,就是有两个子进程但是只wait( )了一次·,如果有一个子进程返回,wait调用就结束。父进程可能不会回收第二个返回的子进程,所以还是存在僵死进程的问题。可以使用waitpid()函数来指定回收子进程)
#include <stdlib.h>
#include <stdio.h>
int main()
{
int count=1;
int pid1;
printf("Recently pid is %d \n",getpid());
pid1 = fork();
if(pid1<0)
{
printf("error in fork!");
}
else if(pid1==0)
{
count++;
printf("My father'pid is %d I'm son1,my pid is %d B\n",getppid(),getpid());
}
else
{
int pid2=fork();
if(pid2<0)
{
printf("error in fork!");
}
if(pid2==0)
{
count++;
printf("My father'pid is %d I'm son2,my pid is %d C \n",getppid(),getpid());
}
else
{
waitpid(pid1,NULL);
waitpid(pid2,NULL);
count++;
printf("I'm father,my pid is %d, A \n",getpid());
}
}
printf("%d \n",count);
return 0;
}
最后,fork与vfork的区别
1. fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
2. fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制