子进程PID回收
子进程先于父进程结束,则父进程需要调用wait()或waitpid()来回收子进程的pid资源,否则子进程会变为僵尸进程,直到父进程退出后,有init回收僵尸进程。回收子进程pid的方法:
1) 使用信号量
每个子进程退出时都会发送SIGCHLD信号量,父进程只要该信息量处理函数中调用wait()、waitpid(-1,null,0)函数回收即可。
优点:立即触发,不需要父进程主流程支持什么。
缺点:不能区分子进程pid,只要有SIGCHLD就会触发,wait、waitpid返回即为子进程pid。
2) 循环检测子进程
父进程循环执行waitpid(),检测子进程是否退出(比较傻,父进程什么也干不了,一直等着子进程退出)。
存在多个子进程时,需要循环多次;多个子进程同时退出时,SIGCHLD信号量会覆盖。
比较好的方法,父进程与子进程通过pipe句柄通信,父进程select/poll该句柄,及时得到子进程退出。(该方法没试过)
3) 由init回收子进程资源
父进程设置signal(SIGCHLD, SIG_IGN),屏蔽子进程退出信号;执行两次fork(),儿子进程直接退出,则孙子进程的父进程为init进程。
缺点:子进程、孙进程何时结束,父进程无法得知。
Wait()函数:阻塞直到任一子进程结束,有子进程退出,返回值为子进程pid;不存在子进程则出错,返回-1。
Waitpid()函数:可以传递参数,比如“waitpid(pid, &Status, WNOHANG)”,pid指定子进程pid,Status保存子进程退出返回信息(正常退出、异常退出、进程返回值),WNOHANG表示函数立即返回,不会阻塞;指定pid不存在,返回-1;指定进程没结束,则返回0;进程结束则返回进程pid。
子线程资源回收
子线程退出后同样存在资源回收问题,子线程创建后默认是joinable状态,子线程退出后,显示创建的资源必须显示释放(如内存、句柄等),同一进程的其他线程必须使用pthread_join()回收子线程的资源,包括线程控制块、线程堆栈。pthread_join()函数是阻塞的,必然导致调用者挂起。如果不关心该线程退出时间,又需要安全的释放资源就必须将线程置为detach状态:
方法1:在创建线程前设置线程属性pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED),再使用该属性创建线程。
pthread_t dwThreadId;
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); //设置线程为detached状态
pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM); //设置线程调度范围为系统级
if (stack > 0)
pthread_attr_setstacksize(&thread_attr, stack);
if (0 != pthread_create(&dwThreadId, &thread_attr, (void *(*)(void *))startaddr, (LPVOID)parg)) { //应用属性到具体线程
hthread = 0;
} else {
*dwThread = dwThreadId;
hthread = 1;
}
pthread_attr_destroy(&thread_attr);
方法2:线程启动时,主动调用pthread_detach(id) ,修改线程本身为detached状态。
线程退出包括多种场景,最好主动退出pthread_exit()。如果是其他线程要求该线程退出,可以发送pthread_cancel()停止指定线程,这种方法比较危险,cancel信号会使子线程的很多系统调用报错退出,因此要求被cancel的线程设置安全退出点,即在没有复杂系统调用时执行pthread_testcancel()循环检测cancel信号。