当前位置: 首页 > 科技观察

用户态进程-线程创建Fork-vfork-Pthread_Create

时间:2023-03-18 16:00:26 科技观察

forkfork函数创建子进程成功后,父进程返回子进程的pid,子进程返回0。具体说明如下:fork返回值为-1,表示子进程创建失败。fork返回值为0,表示子进程创建成功。这个分支就是子进程的运行逻辑。fork返回值大于0,这个分支就是父进程的运行逻辑,返回值等于子进程的pid我们来看一个通过fork系统调用创建子进程的例子:#include#include#includeintmain(){pid_tpid=fork();if(pid==-1){printf("createchildprocessfailed!\n");return-1;}elseif(pid==0){printf("Thisischildprocess!\n");}else{printf("Thisisparentprocess!\n");printf("parentprocesspid=%d\n",getpid());printf("childprocesspid=%d\n",pid);}getchar();return0;}运行结果:$./a.outThisisparentprocess!parentprocesspid=25483childprocesspid=25484Thisischildprocess!从上面的运行结果来看,子进程的pid=25484,父进程的pid=25483。在介绍内存页面错误异常时,提到了写时复制(COW)是一种延迟或避免复制数据的技术。主要用于fork系统调用。执行fork创建新的子进程时,内核不需要复制父进程的整个进程地址空间给子进程,而是父进程和子进程共享同一份。只有在写的时候,数据才会被复制。让我们用一个简单的例子来描述它:#include#include#includeintpeter=10;intmain(){pid_tpid=fork();if(pid==-1){printf("createchildprocessfailed!\n");return-1;}elseif(pid==0){printf("Thisischildprocess,peter=%d!\n",peter);peter=100;printf("Afterchildprocessmodifypeter=%d\n",peter);}else{printf("Thisisparentprocess=%d!\n",peter);}getchar();return0;}执行结果:$./a.outThisisparentprocess=10!Thisischildprocess,peter=10!Afterchildprocessmodifypeter=100从运行结果可以看出,无论子进程如何修改peter的值,父进程始终看到自己的share。vfork接下来看使用vfork创建子进程:#include#include#include#includeintpeter=10;intmain(){pid_tpid=vfork();if(pid==-1){printf("createchildprocessfailed!\n");return-1;}elseif(pid==0){printf("Thisischildprocess,peter=%d!\n",peter);peter=100;printf("Afterchildprocessmodifypeter=%d\n",peter);exit(0);}else{printf("Thisisparentprocess=%d!\n",peter);}getchar();return0;}运行结果:$./a.outThisischildprocess,peter=10!Afterchildprocessmodifypeter=100Thisisparentprocess=100!从运行结果可以看出,当子进程修改peter=100时,父进程中打印的peter的值也是100。pthread_create现在我们知道创建进程有两种方式:fork,vfork。那么线程创建呢?线程创建接口为pthread_create:#include#include#include#include#includeintpeter=10;staticpid_tgettid(void){returnsyscall(SYS_gettid);}staticvoid*thread_call(void*arg){peter=100;printf("createthreadsuccess!\n");printf("thread_callpid=%d,tid=%d,peter=%d\n",getpid(),gettid(),peter);returnNULL;}intmain(){intret;pthread_tthread;ret=pthread_create(&thread,NULL,thread_call,NULL);if(ret==-1)printf("createthreadfailed!\n");ret=pthread_join(thread,NULL);if(ret==-1)printf("pthreadjoinfailed!\n");printf("processpid=%d,tid=%d,peter=%d\n",getpid(),gettid(),peter);returnret;}运行结果:$./a.outcreatethreadsuccess!thread_callpid=9719,tid=9720,peter=100processpid=9719,tid=9719,peter=100从上面的结果可以看出:进程和线程的pid是一样的。当线程修改peter=100时,父进程中打印的peter的值也是100。上面的进程线程创建总图介绍了用户态创建进程和线程的方法,以及每种方法的特点。关于其底层实现的性质,我们将在后面详细说明。这里先给出三者的关系。可见三者最终都会调用do_fork来实现。但是内核态没有进程线程的概念。内核只承认task_struct结构,只要是task_struct结构就可以参与调度。请参阅下面的内核模式任务创建。