操作系统实验课,老师明确说了考试考fork()和exec族函数,我一直以为自己理解的比较透彻,结果昨天小quiz,考了一道非常简单的题,虽然我做对了,但是我竟然有所犹豫,所以需要巩固一下,刚刚进行了一些实验,发现理解的更进一步了。首先,上个简单的例子:
int main()
{
int p1,p2;
printf("Once or Twice %d\n",getpid()); // 1
p1 = fork();
if(p1 == 0)
{
printf("This is the child process \n");
execl("/bin/ls","ls","-a",NULL);
printf("Will it be displayed????? \n"); //2
}
else
{
wait(p1);
printf("This is the parent \n");
}
printf("The end\n"); //3
return 0;
}
我发现,1,3都只会显示一次,而2永远不显示,但是讲蓝色高亮的那行execl去掉,2就会显示1次了,而3会显示2次了。说明fork()新创建的子进制只执行从fork开始后面的进程内容(注意,是“只执行”,而不是“只复制”,我们后面会看到),而exec族函数调用后会将所有的进程都覆盖,也就是说不保留父进程的任何信息,相当于脱胎换骨了,所以在那个if里面execl后面写任何代码都不会被执行的。同理,3处的代码,子进程也不好去执行了。
然后这里就想到一个好玩的程序,如果是在for循环里面调用fork()呢,如下:
int main()
{
int i,pid1;
for (i=0; i < 3; i++ )
{
pid1 = fork();
if (pid1 == 0) {
printf("This is child process! \n");
//return 0;
} else {
wait(pid1);
printf("This is parent process!\n");
}
}
return 0;
}
如果你执行一下这段程序,你就会发现,咦?不应该是显示3对么?怎么这么多?
于是,为了看得清楚:
int main()
{
int i,pid1;
for (i=0; i < 3; i++ )
{
pid1 = fork();
if (pid1 == 0) {
printf("This is child process! %d \n",i);
//return 0;
} else {
wait(pid1);
printf("This is parent process! %d\n",i);
}
}
return 0;
}
于是,我们得到如下结果:
int main()
{
int p1,p2;
printf("Once or Twice %d\n",getpid()); // 1
p1 = fork();
if(p1 == 0)
{
printf("This is the child process \n");
execl("/bin/ls","ls","-a",NULL);
printf("Will it be displayed????? \n"); //2
}
else
{
wait(p1);
printf("This is the parent \n");
}
printf("The end\n"); //3
return 0;
}
我发现,1,3都只会显示一次,而2永远不显示,但是讲蓝色高亮的那行execl去掉,2就会显示1次了,而3会显示2次了。说明fork()新创建的子进制只执行从fork开始后面的进程内容(注意,是“只执行”,而不是“只复制”,我们后面会看到),而exec族函数调用后会将所有的进程都覆盖,也就是说不保留父进程的任何信息,相当于脱胎换骨了,所以在那个if里面execl后面写任何代码都不会被执行的。同理,3处的代码,子进程也不好去执行了。
然后这里就想到一个好玩的程序,如果是在for循环里面调用fork()呢,如下:
int main()
{
int i,pid1;
for (i=0; i < 3; i++ )
{
pid1 = fork();
if (pid1 == 0) {
printf("This is child process! \n");
//return 0;
} else {
wait(pid1);
printf("This is parent process!\n");
}
}
return 0;
}
如果你执行一下这段程序,你就会发现,咦?不应该是显示3对么?怎么这么多?
于是,为了看得清楚:
int main()
{
int i,pid1;
for (i=0; i < 3; i++ )
{
pid1 = fork();
if (pid1 == 0) {
printf("This is child process! %d \n",i);
//return 0;
} else {
wait(pid1);
printf("This is parent process! %d\n",i);
}
}
return 0;
}
于是,我们得到如下结果:
为什么这么诡异呢?看到0,1,2,2,1...于是我知道了,再第一次循环的时候,子进程并非只复制父进程fork()后面的内容,而且复制了父进程的所有内容,只是它只是从fork()后面执行而已(相当于CS:IP为fork()后一条指令的地址!)
于是这个例子就不难理解了,i = 0时,那个child开始循环,注意,这里child保存了父进程的所有内容,包括i,所以child执行i = 1,接着 i =2,结束后,开始parent 2(注意:虽然child改变了i,但是这里执行的是copy on write,即在子进程修改前,父进程先另外copy一份i保存起来),parent1(还原child1相对的那个i)....整个过程就像是递归调用似的...
这样就把fork剖析地更清楚了:每当父进程fork一个子进程时,其实就是将子进程弄个指针过来,指向父进程的内存块,但是,保留了CS:IP,即当前进程的入口地址,然后安装父进程的内存内容进行执行...当需要改变某一内容时,父进程会另外保存这个内容,即copy on write...
于是这个例子就不难理解了,i = 0时,那个child开始循环,注意,这里child保存了父进程的所有内容,包括i,所以child执行i = 1,接着 i =2,结束后,开始parent 2(注意:虽然child改变了i,但是这里执行的是copy on write,即在子进程修改前,父进程先另外copy一份i保存起来),parent1(还原child1相对的那个i)....整个过程就像是递归调用似的...
这样就把fork剖析地更清楚了:每当父进程fork一个子进程时,其实就是将子进程弄个指针过来,指向父进程的内存块,但是,保留了CS:IP,即当前进程的入口地址,然后安装父进程的内存内容进行执行...当需要改变某一内容时,父进程会另外保存这个内容,即copy on write...