大家都知道,调用fork后会返回两个值或者一个值。两个值是指在调用成功的情况下,返回0表示子进程在运行,大于0的数表示父进程在运行,错误情况下就返回一个值,一个小于0的值。在创建成功的情况下,子进程执行返回0,是因为一个子进程只有一个父进程,所以无需知道它父进程的id,通过getppid()也就可以获取它的值,而父进程运行时,它需要知道它的至此执行对应的子进程是哪个,因为一个父进程可能会有不止一个的子进程,而且在父进程中也没有可以直接获得其子进程pid的库函数。
下面就介绍fork的两次返回,一个函数的调用怎么会返回两个值呢,这里要强调的是,它不是返回了两个值,而是返回了两次,一次返回一个值,所以它还是符合函数返回值的特性---只能返回一个值。
fork()是一个经过封装的用户态函数,当用户程序调用了fork函数之后,执行系统调用sys_fork(),而在sys_fork()中直接调用了do_fork()函数,在do_fork()函数中有6个参数,关于参数,我暂不详解,因为我还没研究透。也就是说真正的创建进程实在do_fork函数中实现的,其实像vfork,pthread_creat也都是最终调用的do_fork函数,do_fork函数对调用它的函数的区别是通过clone_flags标志来实现的。
long do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr) |
在do_fork函数中又调用了copy_process函数,在这个函数里面,先用位图法的方式给新进程分配一个pid,然后再为新进程分配PCB资源,先将新进程要复制父进程的资源复制到它的PCB中,然后再为其PCB中的其他变量赋值。一般当一个进程的PCB创建好了,这个进程也就存在了,那么此时已经存在两个进程了,一个是父进程,一个是新创建的子进程,子进程若执行则返回0,父进程执行则返回子进程的pid。所以两次返回,指的是子进程和父进程各返回了一个值。
对于父子进程执行顺序的问题:也是在do_fork函数中,它会有一个标志性的变量,根据其不同取值,来决定先让谁执行,比如子进程先执行然后再把父进程插入到队列中,具体位置我也没研究清楚,简单来说就是在内核的实现过程中,父子进程的执行顺序还是由程序本身来决定的,但是到了用户态看起来就是随机的了。