第10章 线程控制(5)_多线程下的fork
6. 线程和fork
6.1 多线程下的fork
(1)历史包袱
①fork与多线程的协作性很差,这是POSIX系统操作系统的历史包袱。
②长期以来程序都是单线程的,fork运行正常,但引入线程这后,fork的适用范围大大缩小。
(2)多线程下的fork
①在多线程执行的情况下调用fork函数,仅会将发起调用的线程复制到子进程中去(线程ID与父进程发起fork调用的线程ID一样)。也就是说,不能同时创建出与父进程一样多的子线程。
②其他线程均在子进程中立即停止并消失,并且不会为这些线程调用清理函数以及针对线程局部存储变量的析构函数,这可能会造成子进程的内存泄漏。
③虽然只有fork调用线程被复制到子进程,但子进程继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁和条件变量的状态。这意味着,如果某个线程锁定了某个互斥锁,那么在子进程中这个互斥锁可能因得不到释放而造成死锁现象的发生。
(3)多线程中fork的使用方法
①方法1:调用fork以后,立即调用exec()函数执行另一个程序,彻底隔断子进程与父进程的关系。由新的进程覆盖掉原有的内存,使用子进程中所有的线程相关的对象消失。
②方法2:使用pthread_atfork() + fork来创建子进程。
【编程实验】“死锁”现象的产生
//pthread_fork.c
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //线程函数
void* th_func(void* arg)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; printf("process(%d)_thread(%x) lock mutex\n", getpid(), pthread_self());
pthread_mutex_lock(&mutex); struct timespec ts = {, };
nanosleep(&ts, ); //休眠10秒 pthread_mutex_unlock(&mutex);
printf("process(%d)_thread(%x) unlock mutex\n", getpid(), pthread_self());
} int main(void)
{ pthread_t th;
int err = ; //创建子线程(注意,这个子线程只会存在于父进程中,子进程没有!)
if((err = pthread_create(&th, NULL, th_func, NULL)) !=){
perror("pthread_create error");
} sleep(); pid_t pid;
pid = fork(); //刚fork出来的子进程只能是单线程的,它只保留了调用fork
//的线程,而不保留父进程的其它线程。) if(pid <){
perror("fork error");
exit();
}else if(pid > ){ //parent process
printf("parent process id = %d thread id = %x\n", getpid(), pthread_self());
pthread_join(th, ); //等待子线程结束
}else{ //child process
printf("child process id = %d thread id = %x\n", getpid(), pthread_self());
th_func();
} wait(pid); //等待子进程结束 return ;
}
/*输出结果
process(1710)_thread(b77cab70) lock mutex //父进程中的子线程获得mutex,此时mutex为被占用状态
parent process id = 1711 thread id = b77cb6c0 //父进程调用fork后,mutex的状态会被子进程继承!
child process id = 1713 thread id = b77cb6c0 //创建子进程
process(1713)_thread(b77cb6c0) lock mutex //子进程的主线程试图获得mutex,因复制了父进程的mutex锁
//的状态。所以子进程的主线程(也是唯一的线程)会一直被阻塞!
process(1711)_thread(b77cab70) unlock mutex //父进程释放mutex
^C //子进程会因其主线程被阻塞而无法退出!
*/
6.2 pthread_atfork函数
(1)pthread_atfork()函数
头文件 |
#include <pthread.h> |
函数 |
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); |
功能 |
安装fork处理函数 |
返回值 |
成功返回0,否则返回错误编号 |
备注 |
①prepare处理函数数在父进程调用fork之后,创建子进程前调用。 ②parent处理函数在创建子进程之后,但fork返回之前在父进程环境中执行。 ③child处理函数创建子进程之后,fork返回之前被调用。在子进程环境中调用。 ④可以多次调用pthread_atfork来安装多个fork处理函数。其中parent和child的处理程序是以它们注册的顺序调用的。而prepare处理程序的调用是与其注册时的顺序相反。 |
【编程实验】子进程中清理互斥锁
//pthread_atfork.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> /*清理mutex锁
1、定义了两个互斥锁。
2、prepare处理函数中获取这两个锁
3、child处理函数在子进程环境中释放锁
4、parent处理函数在父进程环境中释放锁
*/
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER; //在fork出子进程之前调用,因此只会在父进程中执行!
void prepare(void)
{
printf("process(%d) preparing locks...\n", getpid());
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* th_func(void* arg)
{
printf("thread started...\n");
sleep();
return ;
} int main(void)
{
int err = ;
pid_t pid;
pthread_t tid; //可以多次调用pthread_atfork来安装多个fork处理函数.
//为简单起见,这里只调用一次。
if((err = pthread_atfork(prepare, parent, child)) != ){
perror("can't install fork handlers");
} //创建子线程
if((err = pthread_create(&tid, NULL, th_func, NULL)) != ){
perror("can't create thread");
} sleep();//让出CPU,让子线程去运行! printf("parent about to fork...\n"); //创建子进程
pid = fork(); //子进程只继承父进程中调用fork的线程(即主线程)
if(pid < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("child returned from fork\n");
}else{
printf("parent returned from fork\n");
} wait(pid); //等待子进程结束
return ;
}
/* 输出结果
thread started...
parent about to fork...
process(1786) preparing locks... //父进程获得两个锁
parent unlocking locks...
parent returned from fork
child unlocking locks... //子进程复制父进程的两个锁状态,这里成功释放锁
child returned from fork
*/
7. 线程和I/O
(1)进程中的所有线程共享相同的文件描述符。
(2)文件的定位(lseek)和读写(read/write)是两个操作,如果两个线程同时对同一个文件进行并发读写操作时,可能会造成不安全。
(3)而pread/pwrite将定位和读/写操作变成一个原子操作。这极大方便多线程下对文件的操作。如以下读取文件是线程安全的。
①pread(fd, buf1, 100, 300); //确保线程A读取偏移量为300处的记录
②pread(fd, buf2, 100, 700); //确保线程B读取偏移量为700处的记录
第10章 线程控制(5)_多线程下的fork的更多相关文章
- 《UNIX环境高级编程》(APUE) 笔记第十二章 - 线程控制
12 - 线程控制 GitHub 地址 1. 线程限制 下图为与 线程操作 有关的一些 限制: 可以通过 sysconf 函数进行查询 . 2. 线程属性 可使用 pthread_attr_t 结构修 ...
- 《APUE》读书笔记第十二章-线程控制
本章中,主要是介绍控制线程行为方面的内容,同时介绍了在同一进程中的多个线程之间如何保持数据的私有性以及基于进程的系统调用如何与线程进行交互. 一.线程属性 我们在创建线程的时候可以通过修改pthrea ...
- apue学习笔记(第十二章 线程控制)
本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...
- 第10章 系统级I/O(下)
10.7 I/O重定向 Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输出输入联系起来. 例如:unix>ls>foo.txt,使得外壳加载和执行ls程序,将标准输出重定 ...
- 测试AtomicInteger与普通int值在多线程下的递增操作
日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下: java.util.concurrent.atomic.AtomicB ...
- 測试AtomicInteger与普通int值在多线程下的递增操作
日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,当中一部分例如以下: java.util.concurrent.atomic.Atomi ...
- Linux——多线程下解决生产消费者模型
我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...
- C# 语言规范_版本5.0 (第10章 类)
1. 类 类是一种数据结构,它可以包含数据成员(常量和字段).函数成员(方法.属性.事件.索引器.运算符.实例构造函数.静态构造函数和析构函数)以及嵌套类型.类类型支持继承,继承是一种机制,它使派生类 ...
- Java多线程系列--“基础篇”10之 线程优先级和守护线程
概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...
随机推荐
- Linux C 数据结构->双向链表(阴阳在六,何以言九~)
0. 弄完了单链表,在看双向链表.怎么整?多写,多想,想不通画出来在想,再写,再模仿~ 1. 没啥说的,敲代码~ 说点啥呢,注意自己的代码风格哦,要符合"潮流",不要独树一帜 ...
- PTA 大炮打蚊子 (15分)
现在,我们用大炮来打蚊子:蚊子分布在一个M×NM\times NM×N格的二维平面上,每只蚊子占据一格.向该平面的任意位置发射炮弹,炮弹的杀伤范围如下示意: O OXO O 其中,X为炮弹落点中心,O ...
- FlytestingToolkit工具派送,懒人的测试思考
工欲善其事必先利其器,在IT路上摸爬这些年,去年我们分享了<Fiddler录制jmeter脚本,干货分享>,今天我们有另外的思考,我懒,故我思考. 下载解压后是这样的: 双击 Flytes ...
- Java第八次作业--数据库编程
Deadline: 2017-5-18 23:00 一.学习要点 认真看书并查阅相关资料,掌握以下内容: 掌握应用JDBC访问数据库的基本步骤 掌握DriverManager类.Connection接 ...
- requests中获取请求到文本编码格式
1.使用requests模块: import requests 2.通过网络请求,并获取到数据 url = "http://www.stat-nba.com/award/item14.htm ...
- mos如何工作参考地址
https://wenku.baidu.com/view/c118c3fb360cba1aa811da9d.html?qq-pf-to=pcqq.c2c
- HPU 1476: 括号括号
1476: 括号括号 时间限制: 3 Sec 内存限制: 128 MB 提交: 305 解决: 61 统计 题目描述 小明今年上大学,在大学里发现有很多同学都女朋友,两人整天都在一起腻歪,小明看到后感 ...
- 第三周作业3——Bug Report
作业要求来自:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/957 要求1: 准备工作:利用老师提供的git 命令,批量pull所有 ...
- java设计模——反射的应用 (利用反射来去除if判断语句)
利用反射来去除if判断语句 我的以前写的一个查分系统,就是部长让我写的那个,使用一个分发器(函数),他会根据传递进来的字符串参数调用不同的方. If(“add”.equalsIgnoreCase(fu ...
- 关于递归函数中的return位置
1.对于求是否有解的问题一般使用bool dfs() 其中return 可以放在递归式后面 2.对于需要更新解的问题一般使用int dfs() 其中return 不能放在递归式后面,必须放在函数最 ...