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的更多相关文章

  1. 《UNIX环境高级编程》(APUE) 笔记第十二章 - 线程控制

    12 - 线程控制 GitHub 地址 1. 线程限制 下图为与 线程操作 有关的一些 限制: 可以通过 sysconf 函数进行查询 . 2. 线程属性 可使用 pthread_attr_t 结构修 ...

  2. 《APUE》读书笔记第十二章-线程控制

    本章中,主要是介绍控制线程行为方面的内容,同时介绍了在同一进程中的多个线程之间如何保持数据的私有性以及基于进程的系统调用如何与线程进行交互. 一.线程属性 我们在创建线程的时候可以通过修改pthrea ...

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

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

  4. 第10章 系统级I/O(下)

    10.7  I/O重定向 Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输出输入联系起来. 例如:unix>ls>foo.txt,使得外壳加载和执行ls程序,将标准输出重定 ...

  5. 测试AtomicInteger与普通int值在多线程下的递增操作

    日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下: java.util.concurrent.atomic.AtomicB ...

  6. 測试AtomicInteger与普通int值在多线程下的递增操作

    日期: 2014年6月10日 作者: 铁锚 Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,当中一部分例如以下: java.util.concurrent.atomic.Atomi ...

  7. Linux——多线程下解决生产消费者模型

    我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...

  8. C# 语言规范_版本5.0 (第10章 类)

    1. 类 类是一种数据结构,它可以包含数据成员(常量和字段).函数成员(方法.属性.事件.索引器.运算符.实例构造函数.静态构造函数和析构函数)以及嵌套类型.类类型支持继承,继承是一种机制,它使派生类 ...

  9. Java多线程系列--“基础篇”10之 线程优先级和守护线程

    概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...

随机推荐

  1. RSA签名验证无法通过,检查以下部分

    RSA签名验证无法通过,检查以下部分:1.是否和上游交换公钥,提交给上游的公钥是否配置正确并生效2.检查加密方式是SHA1还是MD5,是否跟上游一致3.上游采用的是否是base64SafeUrl的方式 ...

  2. 简单的C#爬虫

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net ...

  3. jenkins系列(9)--插件之Archive The Artifacts

    点击标题下「蓝色微信名」可快速关注 坚持的是分享,搬运的是知识,图的是大家的进步,没有收费的培训,没有虚度的吹水,喜欢就关注.转发(免费帮助更多伙伴)等来交流,想了解的知识请留言,给你带来更多价值,是 ...

  4. select 从应用层到内核实现解析

    在一个应用中,如果需要读取多个设备文件,这其中有多种实现方式: 1.使用一个进程,并采用同步查询机制,不停的去轮询每一个设备描述符,当设备描述符不可用时,进程睡眠. 2:使用多个进程或者线程分别读取一 ...

  5. 与众不同 制作会唱歌的WinRAR - imsoft.cnblogs

    为了使用方便,我们可能会把RAR压缩包制作成自解压文件.WinRAR自带的自解压模块虽然使用很方便,但千篇一律的外观看起来实在 乏味.其实,只要通过简单改造,你就可以制作出与众不同,声色俱佳的WinR ...

  6. (4)格式化输出(%用法和format用法以及区别)

    %s用法(%s的用法是写多少个,后面就要传多少个) format用法(基本语法是通过{}和:来代替%.format函数可以接受不限个参数,位置可以不按顺序) 形式一(顺序填坑{}) >>& ...

  7. 【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 activation-group& dialect& date-effective

    转载至:https://blog.csdn.net/wo541075754/article/details/75511887 activation-group 该属性将若干个规则划分成一个组,统一命名 ...

  8. CH4302 Interval GCD

    题意 4302 Interval GCD 0x40「数据结构进阶」例题 描述 给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一: &qu ...

  9. oracle 日期时间函数

    ORACLE日期时间函数大全 TO_DATE格式(以时间:2007-11-02   13:45:25为例)           Year:              yy two digits 两位年 ...

  10. 如何构建 MVC&AJax&JSon示例

    背景: 博客中将构建一个小示例,用于演示在ASP.NET MVC4项目中,如何使用JQuery Ajax. 直接查看JSon部分 步骤: 1,添加控制器(HomeController)和动作方法(In ...