fork()函数

在学习Unix环境编程和Unix网络编程中,都遇到了fork()函数,于是想写一篇文章来说明一下自己的感悟,方便以后查吧。如果有错误的地方,还需要大佬们多多指点。

示例采用Unix环境编程上1-7的例子

#include "apue.h"

#include <sys/wait.h>

int main(void)
{
    char buf[MAXLINE];
    pid_t pid;
    int status;

    while (fgets(buf,MAXLINE,stdin) != NULL)
    {
        if (buf[strlen(buf)-1]=='\n')
        {
            buf[strlen(buf)-1]=0;
        }
        if ((pid=fork())<0)
        {
            /* code */
            err_sys("fork error !");        
        }
        else if (0==pid)
        {
            execlp(buf,buf,(char *)0);
            err_ret("couldn't execute : %s\n",buf);
            exit(127);
        }
        
        if ((pid=waitpid(pid,&status,0))<0)
        {
            printf("%d\n",pid);
            err_sys("waitpid error\n");
        }
    }
    return 0;
}

注意上面的代码不能直接运行,需要下载apue3e的源代码,因为apue.h头文件是他们提供给我们的,包括err_sys()等函数

从while条件开始,fgets从标准输入中获取字符串,存放在buf里面,最大字符数为MAXLINE,apue.h头文件中定义MAXLINE为4096大小。一般来说我们敲命令是以Enter键结尾,在内部进行判断并且替换最后的’\n’,execlp函数的参数需要的是字符串(以‘\0’结尾)。下一个if是一个关键点,pid=fork()这个是文章的重点。

在fork函数中,将创建一个与创建进程完全一样的进程,所有的寄存器的值都是一样的,只有一个是例外的,那就是存返回值的寄存器。需要记住一点是在fork函数执行结束后就有两个进程了,除非没有创建成功。存返回值的那个寄存器为什么不一样?肯定是用来标识创建进程啊,不然我(父进程)怎么知道我创建的进程是哪一个。所以在父进程中返回的是子进程(fork中创建的进程)的pid(进程ID),而在子进程中返回的是0,切记不是-1。如果返回-1,说明fork失败。

既然子进程是父进程的副本,那么就应该包含父进程所有的一切,包括当时的值,在fork函数执行之前的值,以及执行的是从fork函数后面的代码,而不是从头开始运行。所以在父进程中会判断pid是否为0,答案是否定的,现在pid的值是子进程的进程ID。接下来再执行pid=waitpid()函数,作用是等到这个存在这个pid的时候就终结它,已知在我们之前,子进程可能执行成功了,所以父进程等在这里杀死他,也可能子进程自己就结束运行了,这是无关紧要的。

再说说子进程中的情况,子进程创建后。咦,返回pid居然为0,又进行else if 判断;哇,真的这么巧,刚好可以判断成功,这也是本章示例故意这么做的,为的是进行下一步操作,要不要这样判断,还是要看读者当时的需求。这里也间接告诉我们,是在fork函数中创建的子进程,而不是其他地方,并子进程从fork函数返回的地方开始运行

在这里插入图片描述
这是我在一个文本中写入的命令行,利用重定向到标准输入中。
在这里插入图片描述
但是这里还有一个小bug,命令行只能是简单的,也就是一个指令,还没有试用复杂的,还是等测完之后再说吧。

我们都看到了这三个命令确实都运行了,第一次产生子进程,返回到子进程的pid为0,执行else if 中的语句,excelp函数把文本的一行作为参数,效果就像在命令行中执行这一行一样。确实如此,excelp函数执行时会把调用它的进程的所有寄存器内容都清空,像是一个新进程来执行命令,pid是没有变的!!!

父进程在那里等着杀死子进程,也有可能在子进程本身还有没执行结束,就被父进程杀死了。在else if 中也对命令有没有执行成功做出了判断,没有成功会打印信息,并以结束代码127自己终止掉。


下面会改写程序,我们看看fork函数到底做了什么?

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>

#define MAXLINE 4096
int main(void)
{
    char buf[MAXLINE];
    pid_t pid;
    int status;

    while (fgets(buf,MAXLINE,stdin) != NULL)
    {
        if (buf[strlen(buf)-1]=='\n')
        {
            buf[strlen(buf)-1]=0;
        }
        if ((pid=fork())<0)
        {
            /* code */
            printf("fork error !");        
        }
        else if (0==pid)
        {
            //execlp(buf,buf,(char *)0);
            printf("couldn't execute : %s\n",buf);
            //exit(127);
        }
    }
    return 0;
}

改写代码,不让父进程帮我们终止,或者等他自己结束,就看看fork
在这里插入图片描述
刚好 1 2 4,很符合树的结构嘛,开始我也这么认为,但是他有8个进程。

①——>②(ls)
①——>③(who)
②——>④(who)
①——>⑤(ifconfig)
②——>⑥(ifconfig)
③——>⑦(ifconfig)
④——>⑧(ifconfig)

箭头右边都是子进程。这并不是树的结构,他们只有平行关系,即使你是我创建,我们两个地位是相等的,你要来杀我,就来waitpid我吧!!!

记录点点滴滴,提升自己实力。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值