当线程调用fork时,就为子进程创建了整个进程地址空间的副本。回忆http://www.cnblogs.com/nufangrensheng/p/3509492.html中讨论的写时复制,子进程与父进程是完全不同的进程,只要两者都没有对内存作出改动,父进程和子进程之间还可以共享内存页的副本。

子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态如果父进程包含多个线程,子进程在fork返回以后,如果紧接着不是马上调用exec的话,就需要清理锁状态。

在子进程内部只存在一个线程,它是由父进程中调用fork的线程的副本构成的。如果父进程中的线程占有锁,子进程同样占有这些锁。问题是子进程并不包含占有锁的线程的副本,所以子进程没有办法知道它占有了哪些锁并且需要释放哪些锁。

如果子进程从fork返回以后马上调用某个exec函数,就可以避免这样的问题。这种情况下,老的地址空间被丢弃,所以锁的状态无关紧要。但如果子进程需要继续做处理工作的话,这种方法就行不通,还需要使用其他的策略。

要清除锁状态,可以通过调用pthread_atfork函数建立fork处理程序。

#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
返回值:若成功则返回0,否则返回错误编号

用pthread_atfork函数最多可以安装三个帮助清理锁的函数。prepare fork处理程序由父进程在fork创建子进程前调用,这个fork处理程序的任务是获取父进程定义的所有锁。parent fork处理程序是在fork创建了子进程以后,但在fork返回之前在父进程环境中调用的,这个fork处理程序的任务是对prepare fork处理程序获得的所有锁进行解锁。child fork处理程序在fork返回之前在子进程环境中调用,与parent fork处理程序一样,child fork处理程序也必须释放prepare fork处理程序获得的所有锁。

注意不会出现加锁一次解锁两次的情况,虽然看起来也许会出现。当子进程地址空间创建的时候,它得到了父进程定义的所有锁的副本。因为prepare fork处理程序获取所有的锁,父进程中的内存和子进程中的内存内容在开始的时候是相同的。当父进程和子进程对他们的锁的副本进行解锁的时候,新的内存是分配给子进程的,父进程的内存内容被复制到子进程的内存中(写时复制),所以就会陷入这样的假象,看起来父进程对它所有的副本进行了加锁,子进程对它所有的副本进行了加锁。父进程和子进程对在不同内存位置的重复的锁都进行了解锁操作,就好像出现了下列的时间序列:

(1)父进程获得所有的锁。

(2)子进程获得所有的锁。

(3)父进程释放它的锁。

(4)子进程释放它的锁。

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

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

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

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

(3)创建子进程。

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

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

(6)fork函数返回到子进程。

(7)调用模块B中的parent fork处理程序释放父进程中模块B的所有锁。

(8)调用模块A中的parent fork处理程序释放父进程中模块A的所有锁。

(9)fork函数返回到父进程。

如果fork处理程序是为了清理锁状态,那么又由谁来负责清理条件变量的状态呢?在有些操作系统的实现中,条件变量可能并不需要做任何清理。但是有些操作系统实现把锁作为条件变量实现的一部分,这种情况下的条件变量就需要清理。问题是目前不存在这样的接口,如果锁是嵌入到条件变量的数据结构中的,那么在调用fork之后就不能使用条件变量,因为还没有可移植的方法对其进行状态清理。另外,如果操作系统的实现是使用全局锁保护进程中所有的条件变量数据结构,那么操作系统实现本身可以在fork库例程中做清理锁的工作,但是应用程序不应该依赖操作系统实现中这样的细节。

实例

程序清单12-7中的程序描述了如何使用pthread_atfork和fork处理程序。

程序清单12-7 pthread_atfork实例

#include "apue.h"
#include <pthread.h> 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 fork处理程序获取这两把锁,child fork处理程序在子进程环境中释放锁,parent fork处理程序在父进程中释放锁。

运行该程序,得到如下输出:

可以看出,prepare fork处理程序在调用fork以后运行,child fork处理程序在fork调用返回到子进程之前运行,parent fork处理程序在fork调用返回给父进程前运行。

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

线程控制之线程和fork的更多相关文章

  1. 线程锁的本质:线程控制、线程状态控制 while if:根据线程的关系(模式)协调线程的执行

    线程锁的本质:线程控制.线程状态控制 while if https://www.cnblogs.com/feng9exe/p/8319000.html https://www.cnblogs.com/ ...

  2. 0039 Java学习笔记-多线程-线程控制、线程组

    join线程 假如A线程要B线程去完成一项任务,在B线程完成返回之前,不进行下一步执行,那么就可以调用B线程的join()方法 join()方法的重载: join():等待不限时间 join(long ...

  3. Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信

    1.Java使用Thread类代表线程.     所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...

  4. 线程控制之线程和I/O

    http://www.cnblogs.com/nufangrensheng/p/3498723.html中介绍了pread和pwrite函数,这些函数在多线程环境下是非常有帮助的,因为进程中的所有线程 ...

  5. linux线程控制-2(线程控制函数)

    记录肖堃老师讲解的linux线程 1. 创建线程 int pthread_create( (pthread_t *thread, pthread_attr_t *attr, void *(*start ...

  6. 第10章 线程控制(5)_多线程下的fork

    6. 线程和fork 6.1 多线程下的fork (1)历史包袱 ①fork与多线程的协作性很差,这是POSIX系统操作系统的历史包袱. ②长期以来程序都是单线程的,fork运行正常,但引入线程这后, ...

  7. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  8. Unix环境高级编程(十二)线程控制

    本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询 ...

  9. apue学习笔记(第十二章 线程控制)

    本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...

随机推荐

  1. java ant 命令大全

    ANT命令总结 1 Ant是什么? Apache Ant 是一个基于 Java的生成工具.生成工具在软件开发中用来将源代码和其他输入文件转换为可执行文件的形式(也有可能转换为可安装的产品映像形式).随 ...

  2. MFC字体与文本输出

    字体 成员函数 1.CFont( ); 构造一个CFont对象.此对象在使用之前应该先使用CreateFont.CreateFontIndirect.CreatePointFont或CreatePoi ...

  3. 利用HTML 5中的Menu和Menuitem元素快速创建菜单

    原文:Introducing the HTML5 “Menu” and “Menuitem” Elements 译文:HTML 5中Menu和Menuitem的元素介绍 译者:dwqs 今天向你介绍H ...

  4. Hadoop MapReduce程序中解决第三方jar包问题方案

    hadoop怎样提交多个第三方jar包? 方案1:把所有的第三方jar和自己的class打成一个大的jar包,这种方案显然笨拙,而且更新升级比较繁琐. 方案2: 在你的project里面建立一个lib ...

  5. Directory.GetCurrentDirectory

    1.一个应用程序中,Directory.GetCurrentDirectory获得的当前工作目录是C:\Windows\System32,这是为什么呢?是如何设置的? 2.在WinXP下:System ...

  6. ffmpeg 研究

    ffmpeg -codecs | grep mp3 可以查看ffmpeg是否把mp3编解码模块编译进去了. libmp3lame is the mp3 encoder for ffmpeg. It n ...

  7. ffmpeg内置aac编码器正式发布

    https://www.ffmpeg.org/#aac_encoder_stable February 15th, 2016, FFmpeg 3.0 "Einstein" FFmp ...

  8. 单节点伪分布集群(weekend110)的Hive子项目启动顺序

    因为,我的mysql是用root用户,在/home/hadoop/app/目录下,创建的. 第一步:开启mysql服务 第二步:启动hive [hadoop@weekend110 app]$ su r ...

  9. codeforces 617BChocolate

    B. Chocolate time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  10. Educational Codeforces Round 5 - C. The Labyrinth (dfs联通块操作)

    题目链接:http://codeforces.com/contest/616/problem/C 题意就是 给你一个n行m列的图,让你求’*‘这个元素上下左右相连的连续的’.‘有多少(本身也算一个), ...