《unix高级环境编程》线程控制——线程和 fork

  多线程的父进程调用 fork 函数创建子进程时,子进程继承了整个地址空间的副本。子进程里面只有一个线程,它是父进程中调用 fork 函数的线程的副本。在子进程中的线程继承了在父进程中相同的状态,即有相同的互斥量、读写锁和条件变量。如果父进程中的线程占用锁,则子进程也同样占有这些锁,只是子进程不包含占有锁的线程的副本,所以并不知道具体占有哪些锁并且需要释放哪些锁。

        如果子进程从 fork 返回之后没有立即调用 exec 函数,则需要调用 fork 处理程序清理锁状态。可以调用 pthread_atfork 函数实现清理锁状态:

[cpp]  view plain copy
  1. /* 线程和 fork */  
  2.   
  3. /* 
  4.  * 函数功能:清理锁状态; 
  5.  * 返回值:若成功则返回0,否则返回错误编码; 
  6.  * 函数原型: 
  7.  */  
  8. #include <pthread.h>  
  9. int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));  
  10. /* 
  11.  * 说明: 
  12.  * 该函数最多可以安装三个帮助清理锁的函数; 
  13.  * prepare fork处理程序由父进程在fork创建子进程前调用,这个fork处理程序的任务是获取父进程定义的所有锁; 
  14.  * 
  15.  * parent fork处理程序是在fork创建子进程以后,但在fork返回之前在父进程环境中调用的,这个fork处理程序的任务是对prepare fork处理程序 
  16.  * 获取的所有锁进行解锁; 
  17.  * 
  18.  * child fork处理程序在fork返回之前在子进程环境中调用,与parent fork处理程序一样,child fork处理程序必须释放prepare fork处理程序获得的所有锁; 
  19.  */  

        可以多次调用  pthread_atfork  函数从而设置多套 fork 处理程序。如果不需要使用其中某个处理程序,可以给特定的处理程序参数传入空指针,这样就不会起任何作用作用。使用多个  fork  处理程序时,处理程序的调用顺序并不相同。 parent  和  child fork  处理程序时与它们注册时的顺序进行调用的。而 prepare fork  处理程序的调用顺序与它们注册的顺序相反,这样可以允许多个模块注册它们自己的  fork  处理函数,并且保持锁的层次。

        例如,模块A调用模块B中的函数,而且每个模块有自己的一套锁。如果所的层次是A在B之间,模块B必须在模块A之前设置fork处理程序,当父进程调用fork时,就会执行以下步骤,假设子进程在父进程之前运行。

1.调用模块A的 prepare 处理程序获取模块A的所有锁。

2.调用模块B的 prepare 处理程序获取模块B的所有锁。

3.创建子进程。

4.调用模块B中的 child 处理程序释放子进程中模块B的所有锁。

5.调用模块A中的 child 处理程序释放子进程中模块A的所有锁。

6.fork 函数返回到子进程。

7.调用模块B中的 parent 处理程序释放子进程中模块B的所有锁。

8.调用模块A中的 parent 处理程序释放子进程中模块A的所有锁。

9.fork 函数返回到父进程。

测试程序:

[cpp]  view plain copy
  1. #include "apue.h"  
  2. #include <pthread.h>  
  3. #include <signal.h>  
  4.   
  5. pthread_mutex_t     lock1 = PTHREAD_MUTEX_INITIALIZER;  
  6. pthread_mutex_t     lock2 = PTHREAD_MUTEX_INITIALIZER;  
  7.   
  8. void prepare(void)  
  9. {  
  10.     printf("preparing locks...\n");  
  11.     pthread_mutex_lock(&lock1);  
  12.     pthread_mutex_lock(&lock2);  
  13. }  
  14. void parent(void)  
  15. {  
  16.     printf("parent unlocking locks...\n");  
  17.     pthread_mutex_unlock(&lock1);  
  18.     pthread_mutex_unlock(&lock2);  
  19. }  
  20. void child(void)  
  21. {  
  22.     printf("child unlocking locks...\n");  
  23.     pthread_mutex_unlock(&lock1);  
  24.     pthread_mutex_unlock(&lock2);  
  25. }  
  26. void* thread_func(void *arg)  
  27. {  
  28.     printf("thread started...\n");  
  29.     pause();  
  30.     return 0;  
  31. }  
  32. int main(void)  
  33. {  
  34.     pid_t       pid;  
  35.     pthread_t   tid;  
  36.     int err;  
  37.   
  38.     err = pthread_atfork(prepare,parent,child);  
  39.     if(err != 0)  
  40.         err_exit(err, "can't install fork handlers");  
  41.     err = pthread_create(&tid,NULL,thread_func,NULL);  
  42.     if(err != 0)  
  43.         err_exit(err, "can't create thread");  
  44.     sleep(2);  
  45.     printf("parent about to fork.\n");  
  46.     pid = fork();  
  47.     if(pid == -1)  
  48.         err_quit("fork failed: %s\n", strerror(err));  
  49.     if(pid == 0)  
  50.         printf("child returned from fork.\n");  
  51.     else  
  52.         printf("parent returned form fork.\n");  
  53.     exit(0);  
  54. }  

输出结果:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. thread started...  
  2. parent about to fork.  
  3. preparing locks...  
  4. parent unlocking locks...  
  5. parent returned form fork.  
  6. child unlocking locks...  
  7. child returned from fork.  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值