Linux 多线程中执行fork的情况

一、普通多线程中执行fork的情况

1.多线程中没有执行fork的情况

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>void*fun(void* arg)
{for(int i=0;i<5;i++){printf("fun线程(%d)在运行\n",getpid());//输出fun线程及其pidsleep(1);//每输出一次睡眠1秒}    
}int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<5;i++){printf("main线程(%d)在运行\n",getpid());//输出main线程及其pidsleep(1);//每输出一次睡眠1秒}pthread_join(id,NULL);}

运行结果:

在这里插入图片描述
通过结果可以看出,两个线程同时执行,并且两个线程的pid是相同的。

2.多线程中在主线程中执行fork的情况

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>void*fun(void* arg)
{for(int i=0;i<5;i++){printf("fun线程(%d)在运行\n",getpid());//输出fun线程及其pidsleep(1);//每输出一次睡眠1秒}    
}int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);fork();//产生一个子进程for(int i=0;i<5;i++){printf("main线程(%d)在运行\n",getpid());//输出main线程及其pidsleep(1);//每输出一次睡眠1秒}pthread_join(id,NULL);}

运行结果:

在这里插入图片描述

根据结果可以看出,在主线程中fork之后,父进程产生的子进程只有一条执行路径,子进程并没有产生新线程。所以产生一个结论,在多线程程序中,无论有多少个线程,fork之后产生的子进程中只有一条执行路径。

3.多线程中在子线程中执行fork的情况

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>void*fun(void* arg)
{fork();//产生一个子进程for(int i=0;i<5;i++){printf("fun线程(%d)在运行\n",getpid());//输出fun线程及其pidsleep(1);//每输出一次睡眠1秒}    
}int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<5;i++){printf("main线程(%d)在运行\n",getpid());//输出main线程及其pidsleep(1);//每输出一次睡眠1秒}pthread_join(id,NULL);}

运行结果:

在这里插入图片描述

从结果可以看出,无论fork在哪个线程被执行,fork最终产生的子进程就在哪个线程中执行。fork是复制进程的函数,从资源的角度来讲,父进程用的资源在fork之后都复制会给子进程。如果父进程运行了多个线程,那么在子进程只执行其中一个线程,这个线程就是fork所在的那个线程。无论是单线程还是多线程,fork之后产生的子进程只执行fork所在的那个线程。

4.总结

多线程程序fork之后产生的子进程只有一条执行路径,就是子进程所在的执行路径。

二、加锁的多线程执行fork的情况

1.创建一个互斥锁,在父进程中加锁,fork之后,子进程的情况

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>
#include<sys/wait.h>pthread_mutex_t mutex;//创建一个互斥锁变量void*fun(void* arg)
{pthread_mutex_lock(&mutex);//加锁sleep(5);pthread_mutex_unlock(&mutex);//5秒后解锁printf("线程fun释放这个锁\n");}int main()
{pthread_mutex_init(&mutex,NULL);//初始化互斥锁pthread_t id;pthread_create(&id,NULL,fun,NULL);sleep(1);//睡眠1秒,等fun线程启动,并且已经加锁pid_t pid=fork();if(pid==0){printf("子进程想要加锁\n");pthread_mutex_lock(&mutex);//子进程加锁printf("子进程加锁成功\n");pthread_mutex_unlock(&mutex);//子进程解锁}else{wait(NULL);//父进程等待子进程结束}printf("main程序结束\n");
}

代码思路:首先创建一个全局的互斥锁,然后在主线程中创建了一个线程fun,在fun中先执行加锁的操作,然后等待5秒,执行解锁的操作,然后线程fun就结束了,在当主线程中创建完新线程fun之后,等待1秒,等待1秒是为了在1秒钟以后在线程fun中已经加锁了,然后执行fork产生一个子进程,在子进程中执行加锁,如果可以加锁成功就会输出加锁成功,父进程在等待子进程结束,如果子进程可以成功执行加锁解锁操作顺利结束,wait就可以返回了,否则wait就会阻塞,等待子进程结束。

运行结果:

在这里插入图片描述

根据结果可以看先输出子进程想要加锁,但是没有打印出子进程加锁成功,此时线程fun已经释放锁了,但是子进程依然没有加锁成功,说明子进程在加锁的地方阻塞住了,又由于主线程在wait等待子进程结束,因为子进程没有结束,所以主线程在wait的地方也阻塞住了,导致当前的程序一直没有执行完成。这说明子进程加锁没有成功。而一般加锁不成功的原因是因为已经加锁了,这种情况下才会在加锁的时候发生阻塞。所以,有可能是子进程已经加锁了,所以再次加锁的时候被阻塞住了。

子进程加锁没有成功的详细原因:

父进程加锁之后,执行fork产生子进程,这个锁也会被复制,所以父进程和子进程各自又都锁,而fork的时候锁的状态是加锁状态,所以fork产生的子进程也是处于加锁状态。但是要注意父进程和子进程的锁各自是各自的,是不同的两个锁,相互之间不影响,所以当父进程的线程fun释放锁之后,子进程中锁的状态不会改变,还是加锁状态。所以如果父进程中有互斥锁,那么父进程中锁的状态是什么,在fork之后产生的子进程中的锁的状态就是什么。

2.pthread_atfork()

在这里插入图片描述

参数解释:
3个参数都是函数指针
第1个参数:指向的函数是在fork之前执行
第2个参数:指向的函数是在fork之后父进程中执行
第2个参数:指向的函数是在fork之后子进程中执行
返回值为0表示函数执行成功。值得注意的是,无论函数定义在哪里,只有在下一个fork()执行前,该函数才被执行。

在第一个参数指向的函数中加锁,在第二个参数指向的函数中解锁,并释放锁,在第三个参数指向的函数中也释放锁。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<semaphore.h>
#include<sys/wait.h>pthread_mutex_t mutex;//创建一个互斥锁变量void parent_fun(void)//该函数在fork之前执行
{pthread_mutex_lock(&mutex);//加锁
}void child_fun(void)//该函数在fork之后执行
{pthread_mutex_unlock(&mutex);//解锁
}void*fun(void* arg)
{pthread_mutex_lock(&mutex);//加锁sleep(5);pthread_mutex_unlock(&mutex);//5秒后解锁printf("线程fun释放这个锁\n");}int main()
{pthread_mutex_init(&mutex,NULL);//初始化互斥锁//在fork之前执行parent_fun,//fork之后在父进程中执行第二个参数child_fun,在子进程中执行第三个参数child_funpthread_atfork(parent_fun,child_fun,child_fun);pthread_t id;pthread_create(&id,NULL,fun,NULL);sleep(1);//睡眠1秒,等fun线程启动,并且已经加锁pid_t pid=fork();if(pid==0){printf("子进程想要加锁\n");pthread_mutex_lock(&mutex);printf("子进程加锁成功\n");pthread_mutex_unlock(&mutex);}else{wait(NULL);}printf("main程序结束\n");
}

运行结果:

在这里插入图片描述

3.总结

父进程中有互斥锁,那么父进程中锁的状态是什么,在fork之后产生的子进程中的锁的状态就是什么。如果在多线程程序中父进程中加了锁,在fork之后,子进程中要使用这个锁,那么就需要用到pthread_atfork(),pthread_atfork()的实现思路就是看没有进程用锁的时候,在进行fork去产生子进程,以确保父子进程中锁的状态是清晰的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/47930.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.物联网LWIP之C/S编程,实现服务器大小写转换

LWIP配置 服务器端实现 客户端实现 错误分析 一。LWIP配置&#xff08;FREERTOS配置&#xff0c;ETH配置&#xff0c;LWIP配置&#xff09; 1.FREERTOS配置 为什么要修改定时源为Tim1&#xff1f;不用systick&#xff1f; 原因&#xff1a;HAL库与FREERTOS都需要使用systi…

信号处理--基于EEG脑电信号的眼睛状态的分析

本实验为生物信息学专题设计小项目。项目目的是通过提供的14导联EEG 脑电信号&#xff0c;实现对于人体睁眼和闭眼两个状态的数据分类分析。每个脑电信号的时长大约为117秒。 目录 加载相关的库函数 读取脑电信号数据并查看数据的属性 绘制脑电多通道连接矩阵 绘制两类数据…

Nacos

Nacos介绍 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的⾸字⺟简称&#xff0c;⼀个更易于构 建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。 在这个介绍中&#xff0c;可以看出Nacos⾄少有三个核⼼功能&#xff1a; 1. 动态服务发现 2. 配…

神经网络为什么可以学习

本资料转载于B站up主&#xff1a;大模型成长之路,仅用于学习和讨论&#xff0c;如有侵权请联系 动画解析神经网络为什么可以学习_哔哩哔哩_bilibilis 1、一个神经网络是由很多神经元形成的 1.1 也可以是一层&#xff0c;也可以是多层 2 层和层之间的连接就跟一张网一样 2.1 每…

【ppt密码】为什么PPT幻灯片不能编辑?

PPT打开之后&#xff0c;发现幻灯片内不能编辑&#xff0c;出现这种情况的原因大概有两个。 原因一&#xff1a;幻灯片母版 当幻灯片中出现有些固定的对象无法修改、无法编辑的时候&#xff0c;很有可能就是因为在母版视图中进行了设置。我们只需要再打开幻灯片母版&#xff…

适用于Android™的Windows子系统Windows Subsystem fo r Android™Win11安装指南

文章目录 一、需求二、Windows Subsystem for Android™Win11简介三、安装教程1.查看BIOS是否开启虚拟化2.安装Hyper-V、虚拟机平台3.启动虚拟机管理程序(可选)4.安装适用于Android™的Windows子系统5.相关设置 一、需求 需要在电脑上进行网课APP&#xff08;无客户端只有App&…

Java入门级基础教学(史上最详细的整合)

目录 一&#xff1a;基础语法 1.“Hello word” 2.Java的运行机制 3. Java基本语法 1.注释、标识符、关键字 2.数据类型&#xff08;四类八种&#xff09; 4.类型转换 1.自动转换 2.强制转换 5.常量和变量 1.常量 2.变量 3.变量的作用域 6.运算符 1.算数运算符 …

2023/8/16 华为云OCR识别驾驶证、行驶证

目录 一、 注册华为云账号开通识别驾驶证、行驶证服务 二、编写配置文件 2.1、配置秘钥 2.2、 编写配置工具类 三、接口测试 3.1、测试接口 3.2、结果 四、实际工作中遇到的问题 4.1、前端传值问题 4.2、后端获取数据问题 4.3、使用openfeign调用接口报错 4.3、前端显示问题…

电力虚拟仿真 | 高压电气试验VR教学系统

在科技进步的推动下&#xff0c;我们的教育方式也在发生着翻天覆地的变化。其中&#xff0c;虚拟现实&#xff08;VR&#xff09;技术的出现&#xff0c;为我们提供了一种全新的、富有沉浸感的学习和培训方式。特别是在电力行业领域&#xff0c;例如&#xff0c;电力系统的维护…

ssm+vue绿色农产品推广应用网站源码和论文PPT

ssmvue绿色农产品推广应用网站041 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高…

第3步---MySQL的DDL和DML操作

第3步---MySQL的DDL和DML操作 1.DDL操作 Data Defination Language 数据定义语言。创建数据库和表的不涉及到数据的操作。 1.1DDL基本操作 1.1.1数据库相关操作 ddl&#xff1a;创建数据库&#xff0c;创建和修改表 对数据库常见的操作&#xff1a; 操作数据库 -- 展示数据…

PSP - 基于开源框架 OpenFold Multimer 蛋白质复合物的结构预测与BugFix

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132410296 AlphaFold2-Multimer 是一个基于 AlphaFold2 的神经网络模型&#xff0c;可以预测多链蛋白复合物的结构。该模型在训练和推理时都可以处…

微信小程序卡片横向滚动竖图

滚动并不是使用swiper&#xff0c;该方式使用的是scroll-view实现 Swiper局限性太多了&#xff0c;对竖图并不合适 从左往右滚动图片示例 wxml代码&#xff1a; <view class"img-x" style"margin-top: 10px;"><view style"margin: 20rpx;…

【SpringCloud】Gateway使用

文章目录 概述阻塞式处理模型和非阻塞处理模型概念阻塞式处理模型 三大核心概念 工作流程使用POMYML启动类配置路由通过编码进行配置动态路由常用的Route Predicate自定义全局过滤器自定义filter 官网 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1…

Leetcode61 旋转链表

给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3] 示例2&#xff1a; 输入&#xff1a;head [0,1,2], k 4 输出&#xff1a;[2,0,1] …

软考高级系统架构设计师(一)计算机硬件

【原文链接】软考高级系统架构设计师&#xff08;一&#xff09;计算机硬件 1.1 计算机硬件组成 1.1.1 计算机的基本硬件组成 运算器控制器存储器输入设备输出设备 1.1.2 中央处理单元&#xff08;CPU&#xff09; 中央处理单元&#xff08;CPU&#xff09;的组成 运算器…

7.11 Java方法重写

7.11 Java方法重写 这里首先要确定的是重写跟属性没有关系&#xff0c;重写都是方法的重写&#xff0c;与属性无关 带有关键字Static修饰的方法的重写实例 父类实例 package com.baidu.www.oop.demo05;public class B {public static void test(){System.out.println("这…

实时拍照翻译怎么做?几个步骤轻松翻译

现在&#xff0c;随着人们跨越国界的频率不断增加&#xff0c;语言障碍成为了一个越来越普遍的问题。为了解决这个问题&#xff0c;一些应用程序开始提供实时拍照翻译功能&#xff0c;这种功能可以通过手机摄像头拍摄文本&#xff0c;并将其翻译成用户所需的语言。那么&#xf…

Websocket原理和实践

一、概述 1.websocket是什么&#xff1f; WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&…

从一些常见的错误聊聊mysql服务端的关键配置 | 京东云技术团队

背景 每一年都进行大促前压测&#xff0c;每一次都需要再次关注到一些基础资源的使用问题&#xff0c;订单中心这边数据库比较多&#xff0c;最近频繁报数据库异常&#xff0c;所以对数据库一些配置问题也进行了研究&#xff0c;本文给出一些常见的数据库配置&#xff0c;说明…