线程控制之线程和fork
当线程调用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处理程序时,处理程序的调用顺序并不相同。parent和child 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的更多相关文章
- 线程锁的本质:线程控制、线程状态控制 while if:根据线程的关系(模式)协调线程的执行
线程锁的本质:线程控制.线程状态控制 while if https://www.cnblogs.com/feng9exe/p/8319000.html https://www.cnblogs.com/ ...
- 0039 Java学习笔记-多线程-线程控制、线程组
join线程 假如A线程要B线程去完成一项任务,在B线程完成返回之前,不进行下一步执行,那么就可以调用B线程的join()方法 join()方法的重载: join():等待不限时间 join(long ...
- Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信
1.Java使用Thread类代表线程. 所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...
- 线程控制之线程和I/O
http://www.cnblogs.com/nufangrensheng/p/3498723.html中介绍了pread和pwrite函数,这些函数在多线程环境下是非常有帮助的,因为进程中的所有线程 ...
- linux线程控制-2(线程控制函数)
记录肖堃老师讲解的linux线程 1. 创建线程 int pthread_create( (pthread_t *thread, pthread_attr_t *attr, void *(*start ...
- 第10章 线程控制(5)_多线程下的fork
6. 线程和fork 6.1 多线程下的fork (1)历史包袱 ①fork与多线程的协作性很差,这是POSIX系统操作系统的历史包袱. ②长期以来程序都是单线程的,fork运行正常,但引入线程这后, ...
- (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- Unix环境高级编程(十二)线程控制
本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询 ...
- apue学习笔记(第十二章 线程控制)
本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...
随机推荐
- Shell获取Aix/linux/unix机器上db2和os的信息并上传到指定服务器
(之前写过一篇类似的文章,当时传输文件用的是ftp,因为项目觉得ftp不够安全所以这次换成了scp,同时对脚本的一些地方也做了一些调整) 其实做这个东西还是因为项目的需求,需要获取某些机器(目前主要是 ...
- 学习笔记之Linux内核编译过程
准备工作 物理主机:win8(32位) 虚拟机工具:VirtualBox_4.3.16_Win32 虚拟主机:xubuntu-12.04.4 安装virtualBox功能增强包 设置好虚拟机与主机的共 ...
- apache环境下配置服务器支持https
SSL加密的意义在于保护服务器到客户端的信息或者是客户端到服务器的信息不被监听和篡改. 现在一些主流的网站都已经是通过 https访问了,搜索引擎对此类网站的收录也不存在问题了. 具体的配置流程大概是 ...
- MFC使用ShowWindow(SW_MAXIMIZE)任务栏消失的处理
ShowWindow(SW_SHOWMAXIMIZED);//窗口最大化 问题:在写程序时,如果包含了标题栏,但是没有包含最大化按钮或者最小话按钮. 那么人工用ShowWindow(SW_MAXIMI ...
- [Hive - Tutorial] Built In Operators and Functions 内置操作符与内置函数
Built-in Operators Relational Operators The following operators compare the passed operands and gene ...
- Trail: JDBC(TM) Database Access(1)
package com.oracle.tutorial.jdbc; import java.sql.BatchUpdateException; import java.sql.Connection; ...
- 转】Maven学习总结(四)——Maven核心概念
原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4051819.html 感谢! 一.Maven坐标 1.1.什么是坐标? 在平面几何中坐标(x,y)可以标识平面中 ...
- quartz 时间配置规则
quartz 时间配置规则 格式: [秒] [分] [小时] [日] [月] [周] [年] 序号 说明 是否必填 允许填写的值 允许的通配符 1 秒 是 0-59 , - * / ...
- thymeleaf中的th:with用法
局部变量,th:with能定义局部变量: <div th:with="firstPer=${persons[0]}"> <p> The name of th ...
- 框架学习笔记:Unity3D的MVC框架——StrangeIoC
作为从AS3页游走过来的人,看见StrangeIoC会额外亲切,因为StrangeIoC的设计和RobotLegs几乎一致,作为一款依赖注入/控制反转(IoC)的MVC框架,StrangeIoC除了使 ...