线程的父子关系
一般利用pthread库让让主线程去创建子线程,从而形成一个线程的父子关系。
主线程调用pthread_join来等待子线程的结束,然后释放子进程占有的栈、id、私有数据等资源。这样设计阻塞的好处是父线程会等到子线程结束后才结束,这样不至于父线程关闭导致子线程没有结束就被关闭。可见父子线程默认情况下是有紧密联系的,父线程需要为子线程处理身后事。
现在,我期望让父子线程没有关系,让子线程在结束的时候自己去释放自己的资源,那么需要调用pthread_detach来解开默认的父子关系。这样,父线程不再会顾忌子线程的结束与否。“不受约束”的子线程很可能因父线程的结束而异常关闭。
#include<iostream> #include<pthread.h> using namespace std; void* fun(void*) { pthread_detach(pthread_self());//父子关系的接触与否依赖于这句,可以观察有和没有的运行结果的区别。 cout<<"fun begins!"<<endl; int i; for(i=0;i<1000000000;i++) ; cout<<"fun is over! i="<<i<<endl; return NULL; } int main() { cout<<"main begins."<<endl; pthread_t tid; int err = pthread_create(&tid, NULL, fun, NULL); void * tret; err = pthread_join(tid, &tret); cout<<"main is over."<<endl; return 0; }
从运行结果看出,在不调用pthread_detach的情况下,父线程会在pthread_join阻塞,等候子线程结束后再唤醒并结束。若调用了pthread_detach,父线程会直接关闭,导致子线程被迫关闭。
进程的父子关系
一般利用系统调用fork在主进程创建子进程(见fork),从而形成一个父子进程。
同样,一般要求父进程wait子进程结束的信号,然后去处理子进程的“身后事”,否则子进程将会变为僵尸进程(见《僵尸进程和孤儿进程》),显然属于父子关系处理不当的情况。如果父进程比子进程先结束,父子的关系会自然脱离,子进程变为孤儿进程,其“身后事”交给init办理。
现在,我期望父子进程结束这种关系,成为两个独立的进程。对于线程来说,父子线程即使再怎么独立,也总是属于同一个进程的,因为共享了太多的资源。对于进程来说,父子进程的关系一旦解除之后,就可以认为进程没有关系了,子进程的身后事交给init来解决。
APUE里提出一个fork两次的方法,通过创建了爷孙三代来使得进程关系彻底脱离,不过借助于第三个进程太麻烦了。利用系统调用setsid(void)就可以直接做到,setsid的作用是将当前进程加入到一个新的会话中。
#include<iostream> #include<unistd.h> using namespace std; int main() { if(fork() != 0) { cout<<"father begins."<<endl; int i=0; for(;i<1000000000;i++) ; for(;i<1000000000;i++) ; for(;i<1000000000;i++) ; for(;i<1000000000;i++) ; for(;i<1000000000;i++) ; for(;i<1000000000;i++) ; cout<<"father is over. i="<<i<<endl; } else { cout<<"son begins."<<endl; pid_t pid = setsid();//有没有这句:父子关系是否脱离的区别 cout<<"son is over."<<endl; } return 0; }
程序运行起来,在另一个终端ps u观察进程的状态,可以看到父子关系是否脱离情况下子进程的状态的不同。从结果看出,不调用setsid的情况下,子进程提前结束会成为Zombie进程;调用setsid的情况下,子线程提前结束会完整释放其资源。