Linux 多线程中执行fork的情况

news2024/9/24 5:30:03

一、普通多线程中执行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线程及其pid
        sleep(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线程及其pid
        sleep(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线程及其pid
        sleep(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线程及其pid
        sleep(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线程及其pid
        sleep(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线程及其pid
        sleep(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_fun
    pthread_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.coloradmin.cn/o/908279.html

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

相关文章

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

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

Lnton羚通算法算力云平台如何在OpenCV-Python中使用cvui库创建复选框

CVUI 之 复选框 Python import numpy as np import cv2 import cvuidef checkbox_test():WINDOW_NAME Checkbox-Testchecked [False]# 创建画布frame np.zeros((300, 400, 3), np.uint8)# 初始化窗口cvui.init(WINDOW_NAME)while True:# 画布填色frame[:] (100, 200, 100…

WEB APIs day5

一、window对象 BOM属于window对象 1.BOM&#xff08;浏览器对象模型&#xff09; bom里面包含着dom,只不过bom我们平时用得比较少&#xff0c;我们经常使用的是dom操作&#xff0c;因为我们页面中的这些标签都是在dom中取的&#xff0c;所以我们操作dom多一点。 window对象…

大数据、人工智能、机器学习、深度学习关系联系前言

1.大数据和人工智能关系 2.机器学习、深度学习、人工智能关系 3.监督学习、无监督学习、半监督学习、强化学习、迁移学习关系 4.机器学习具体内容 一、大数据和人工智能之间存在相促进并相互支持&#xff0c;推动了科技发展 1.数据驱动的人工智能&#xff1a;人工智能系统需要…

【是C++,不是C艹】 手把手带你实现Date类(附源码)

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 恍惚间&#xff0c;已经两个月没更新了 &#xff08;&#xff1b;д&#xff40;&#xff09;ゞ 我忏…

K8s+Docker+KubeSphere+DevOps笔记

K8sDockerKubeSphereDevOps 前言一、阿里云服务器开通二、docker基本概念1.一次构建、到处运行2、docker基础命令操作3、docker进阶操作1.部署redis中间件2.打包docker镜像 三、kubernetes 大规模容器编排系统1、基础概念&#xff1a;1、服务发现和负载均衡2、存储编排3、自动部…

基于springboot+vue的流动人口登记系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

因果推断(五)基于谷歌框架Causal Impact的因果推断

因果推断&#xff08;五&#xff09;基于谷歌框架Causal Impact的因果推断 除了传统的因果推断外&#xff0c;还有一些机器学习框架可以使用&#xff0c;本文介绍来自谷歌框架的Causal Impact。该方法基于合成控制法的原理&#xff0c;利用多个对照组数据来构建贝叶斯结构时间…

javaScript:常用的js字符串方法

目录 一.前言 二.字符串方法 1.charAt(num) 获取字符串指定位置上的字符 解释 示例 注意 2.length属性 获取字符串长度 解释 示例讲解 3.substring()字符串的截取 解释 特点 示例 4.slice()字符串截取 解释 特点 示例 应用 单行文本加省略号 字符串劫…

Dockerfile制作镜像与搭建LAMP环境

1、编写Dockerfile制作Web应用系统nginx镜像&#xff0c;生成镜像nginx:v1.1&#xff0c;并推送其到私有仓库。 具体要求如下&#xff1a; &#xff08;1&#xff09;基于centos基础镜像&#xff1b; &#xff08;2&#xff09;指定作者信息&#xff1b; &#xff08;3&#x…

卷积神经网络——中篇【深度学习】【PyTorch】

文章目录 5、卷积神经网络5.5、经典卷积神经网络&#xff08;LeNet&#xff09;5.5.1、理论部分5.5.2、代码实现 5.6、深度卷积神经网络&#xff08;AlexNet&#xff09;5.6.1、理论部分5.6.2、代码实现 5.7、使用块的网络&#xff08;VGG&#xff09;5.7.1、理论部分5.7.2、代…

从浅到深研究矩阵的特征值、特征向量

本篇特征值、特征向量笔记来源于MIT线性代数课程。 矩阵特征值与特征向量 ✨引言✨什么是特征向量呢&#xff1f;✨表示✨从特例看特征值与特征向量✨如何求解方程▶️ 思路&#xff1a;✨对称矩阵例子&#xff1a;✨对比观察两个矩阵及它们的特征值及特征向量&#xff1a;✨旋…

C语言小白急救 整型与浮点型在内存中的存储(理论知识+代码示例)

文章目录 一、有无符号整型的存储1.整形家族2.整形在内存中的存储3.大小端介绍4.signed 与 unsigned 类型存储例子&#xff1a;1.2.3.4.5. 二、浮点型的存储1.浮点型家族2.浮点型的存储例子&#xff1a; 一、有无符号整型的存储 1.整形家族 字符在内存中存储的是字符的ASCII码…

Django视图-HttpRequest请求对象和HttpResponse响应对象

文章目录 HttpRequestHttpResponse实践request对象的属性和方法响应 def index(request): 这个request其实就是内部已经封装好的Http请求HttpRequest&#xff0c;它是一个请求对象Django中的视图主要用来接受Web请求&#xff0c;并做出响应。 视图的本质就是一个Python中的函数…

解决Windows下的docker desktop无法启动问题

以管理员权限运行cmd 报错&#xff1a; docker: error during connect: Post http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.40/containers/create: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows,…

【image captioning】自用数据集BUTD特征提取流程

自用数据集BUTD特征提取流程 作者:安静到无声 个人主页 目录 自用数据集BUTD特征提取流程源数据生成推荐专栏源数据生成 基于程序bottom-up-attention.pytorch可以提取图片的BUTD特征,具体方法详见:MILVLG/bottom-up-attention.pytorch: A PyTorch reimplementation of bo…

2023年国赛数学建模思路 - 案例:粒子群算法

文章目录 1 什么是粒子群算法&#xff1f;2 举个例子3 还是一个例子算法流程算法实现建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法&#xff1f; 粒子群算法&#xff08;Pa…

航空电子设备中的TSN通讯架构—直升机

前言 以太网正在迅速取代传统网络&#xff0c;成为航空电子设备和任务系统的核心高速网络。本文提出了以太网时间敏感网络(TSN)在航空电子设备上应用的技术优势问题。在实际应用中&#xff0c;TSN已成为一个具有丰富的机制和协议的工具箱&#xff0c;可满足与时间和可靠性相关…

第九课 过去分词、现在分词作非谓语

文章目录 前言一、过去分词的定义1、及物动词的过去分词2、双宾动词的过去分词 二、过去分词和过去分词短语的作用1、过去分词做前置定语过去分词构成的形容词有被动或者完成意义&#xff0c;如果单独的一个过去分词不及物只有完成意义&#xff0c;如果是一个及物动词的过去分词…

索引构造与信息检索:让 ChatGPT 成为 Selenium 问答助手

这是chatgpt为我生成的3个标题&#xff0c;我选了第3个。 利用 Langchain 和 GPT 实现 Selenium 机器人自动问答 向量化存储和检索&#xff1a;如何用相似度搜索匹配 Selenium 知识&#xff1f; 索引构造与信息检索&#xff1a;让 ChatGPT 成为 Selenium 问答助手 之前有很…