Chinaunix首页 | 论坛 | 博客
  • 博客访问: 116896
  • 博文数量: 16
  • 博客积分: 2044
  • 博客等级: 中士
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-15 22:15
文章分类

全部博文(16)

文章存档

2012年(1)

2011年(9)

2010年(6)

分类:

2012-10-17 18:29:51

++++++APUE读书笔记-12线程控制-09线程和fork++++++
 
9、线程和fork
================================================
 当线程调用fork的时候,会为这个子进程创建整个进程地址空间的拷贝。根据我们之前所说的copy-on-write,子进程和父进程是完全不同的两个进程,只要内存中的内容不发生变化,那么这部分内存就会在父子进程之间共享(这样尽可能地减少了额外的拷贝)。
 通过继承拷贝过来的地址空间,子进程也会从父进程那里继承每个互斥量,读写锁,条件变量。如果父进程包含不止一个线程,并且子进程fork返回之后不立即调用exec,那么子进程需要清除锁的状态。
 在子进程中只有一个线程,这个线程就是父进程中调用fork的那个线程的拷贝。如果父进程中的线程持有锁那么子进程也将会持有这个锁.问题是子进程不包含持有锁的线程的拷贝,所以子进程无法知道哪些锁被持有,那些锁需要被释放。
 如果子进程在调用fork之后直接调用exec函数那么这个问题就会被避免,这个情况下旧的地址空间会被丢弃,所以锁的状态就不用在意了.这个不总是发生的,所以如果子进程想要在fork之后继续处理,那么我们需要采用另外一种策略。
 我们可以调用pthread_atfork来创建fork处理函数来清除锁的状态。
 #include
 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
 如果成功返回0,如果失败返回错误号码。
 使用这个函数我们安装了三个fork处理函数来帮助我们清除锁的状态。prepare在父进程中,创建子进程之前被调用,它的作用是请求所有父进程定义的锁;parent在父进程中,fork创建子进程之后但是fork返回之前被调用,这个函数用来解锁prepare请求的所有锁;child函数在子进程中fork返回之前被调用,和parent函数一样,用来释放prepare请求的所有的锁。
 注意,这里并不是加锁一次却释放两次这个错误的操作。因为子进程空间创建的时候会拷贝父进程的所有锁,因为prepare请求了所有的锁,所以父子进程中的内容都是一个样子的(就是锁上的状态)。当parent和child解锁它那份拷贝的时候,由于copy-on-write,就会实际分贝并拷贝子进程的空间了。所以在我们看起来,父进程锁住锁的拷贝并在子进程中释放这些锁住的锁的拷贝。child和parent函数以释放它们自己内存区域中的锁的拷贝为结束,过程等价如下:
 a.父进程请求所有的锁。
 b.子进程请求所有的锁。
 c.父进程释放它的锁。
 d.子进程释放它的锁。
 这里,前两个请求所有的锁的操作实际都是prepare函数做的。
 我们可以多次调用pthread_atfork来安装不止一个fork处理函数的集合,如果我们不需要其中的一个函数,我们可以为相应的函数参数的地方传递一个空指针。当使用了多个fork处理函数的集合的时候,它们的调用次序也是不一样的,parent和child处理函数的调用次序和它们的安装次序是一样的,而prepare的调用次序却和它们安装的次序相反。这样能够保证多个模块注册它们自己的fork处理函数而且还满足锁的使用规则。
 例如,模块A调用模块B中的函数,并且每个模块都有它自己的锁的集合。如果上锁的过程是A在B之前,那么模块B必须在模块A之前注册它的fork处理函数。当父进程调用fork的时候,会发生如下过程(假设子进程在父进程之前运行):
 a.模块A的prepare处理函数被调用,请求A的锁。
 b.模块B的prepare处理函数被调用,请求B的锁。
 c.创建子进程。
 d.模块B的child处理函数被调用,释放子进程中B模块的所有锁。
 e.模块A的child处理函数被调用,释放子进程中A模块的所有锁。
 f.fork函数返回到子进程。
 g.模块B的parent处理函数被调用,释放父进程中模块B的所有锁。
 h.模块A的parent处理函数被调用,释放父进程中模块A的所有锁。
 i.fork函数返回到父进程。
 如果fork处理函数用来清除锁的状态,那么谁来清除条件变量的状态呢?在有一些实现中,条件变量并不需要被清除;然而实现如果使用锁来做为条件变量实现的一部分,那么就需要清除了;问题是,没有相应的接口来让我们做这件事情.如果锁嵌入到了条件变量的数据结构中,那么我们就不能在调用fork之后使用条件变量了,因为没有一个可移植的方法来清除条件变量的状态。另一方面,如果实现使用一个全局变量来保护一个进程中的条件变量,那么在fork库中,实现本身可以清除锁的状态;尽管如此,应用程序也不应当依赖这样的实现。
 例子:
 这里给出了使用pthread_atfork的例子代码。
 #include "apue.h"
 #include
 
 pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
 pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
 
 void prepare(void)
 {
     printf("preparing locks...\n");
     pthread_mutex_lock(&lock1);
     pthread_mutex_lock(&lock2);
 }
 void parent(void)
 {
     printf("parent unlocking locks...\n");
     pthread_mutex_unlock(&lock1);
     pthread_mutex_unlock(&lock2);
 }
 
 void child(void)
 {
     printf("child unlocking locks...\n");
     pthread_mutex_unlock(&lock1);
     pthread_mutex_unlock(&lock2);
 }
 
 void * thr_fn(void *arg)
 {
     printf("thread started...\n");
     pause();
     return(0);
 }
 
 int main(void)
 {
     int         err;
     pid_t       pid;
     pthread_t   tid;
 
 #if defined(BSD) || defined(MACOS)
     printf("pthread_atfork is unsupported\n");
 #else
     if ((err = pthread_atfork(prepare, parent, child)) != 0)
         err_exit(err, "can't install fork handlers");
     err = pthread_create(&tid, NULL, thr_fn, 0);
     if (err != 0)
         err_exit(err, "can't create thread");
     sleep(2);
     printf("parent about to fork...\n");
     if ((pid = fork()) < 0)
         err_quit("fork failed");
     else if (pid == 0) /* child */
         printf("child returned from fork\n");
     else        /* parent */
         printf("parent returned from fork\n");
 #endif
     exit(0);
 }
 在这个例子中,我们定义了两个互斥量,lock1和lock2。prepare处理函数给它们两个上锁,child处理函数在子进程上下文中释放锁,parent处理函数在父进程上下文中释放锁。
 运行的结果大致如下:
 $ ./a.out
 thread started...
 parent about to fork...
 preparing locks...
 child unlocking locks...
 child returned from fork
 parent unlocking locks...
 parent returned from fork
 从这个例子的运行结果我们可以看出:prepare处理函数在调用fork之后被调用(但是在创建的进程运行之前被调用),child处理函数在子进程的fork返回之前被调用;parent处理函数在父进程的fork返回之前被调用。
参考:
 
 
阅读(1417) | 评论(0) | 转发(0) |
0

上一篇:select, poll和epoll的区别

下一篇:没有了

给主人留下些什么吧!~~