1.进程的创建
fork()函数是创建子进程的函数,在主函数中调用fork会产生一个子进程
打印出来的结果是:
if语句是条件语句但却两个都打印了,两个条件都满足,也就是说两个都执行,但却不是一条执行流,那么可以得出肯定还有一个进程在打印另一个。都记得fork()之后有两个进程,一个父进程,一个子进程,父进程返回子进程的pid,子进程返回0;也就是说调用一次返回两次。
有没有发现 我在父进程执行时加了wait()函数,因为fork函数在创建子进程时,并不能确定是子进程先运行还是父进程先运行,防止子进程成为僵死进程(进程主体结束,但其pcb没有释放),所以在父进程中调用wait函数,让其阻塞,等待子进程运行完毕后并获得子进程的退出状态,处理完子进程后再运行父进程,也就是同步执行。关于wait函数以及僵死进程、阻塞在后续解释。
那么fork()这个函数是怎么创建进程的呢?
其实是系统调用clone()实现fork()然后clone()再去调用do_fork(),do_fork()还不是最后的执行者最后的执行者是copy_process()这个函数,它具体做如下事:
-
调用dup_task_struct为新进程创建一个内核栈、thread_info结构和task_struct,这些值与当前进程的值相同。此时,子进程和父进程的描述符完全相同。
-
检查创建完毕后,当前用户所拥有的进程数没有超出给他分配的资源的限制。
-
将子进程的状态设置为不可中断状态,保证它不会投入运行.
-
copy_process()函数调用copy_flags()更新task_struct的flags成员。表明进程是否有超级用户权限的标志被清零,表明还没有调用exec()函数的标志被设置。
-
调用get_pid()为新进程获取有效的pid。
-
根据clone()传递的参数标志,copy_procsee()拷贝或共享打开的文件、文件系统信息、信号处理函数进程地址空间和命名空间等,一般来说,这些资源会被进程中的所有线程所共享,否则这些资源对每个进程来说是不共享的,因此拷贝到这里。
-
让父进程和子进程平分剩余的时间片。
-
copy_process()返回一个指向子进程的指针。
当完成后,再返回到do_fork()函数,如果copy_process()函数成功返回,新创建的子进程被唤醒并投入到运行。内核一般会让子进程先执行,因为子进程一般会调用exec函数,这样可以避免写时拷贝的开销,如果时父进程先执行的话,有可能会开始向地址空间写入。