fork()父子进程变量之间的关系与信号的响应

2023-10-26

1.变量关系:

      根据《unix 高级环境编程》中的一句话:子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如:子进程获取父进程数据空间,堆和栈的副本。注意,这是子进程所拥有的副本。父子进程并不共享这些存储空间部分。父子进程共享正文段(就是代码段.text)。


个人理解:就是说一子进程会复制父进程的数据,但是这些数据是存放在另一个地方的,双方的数据都是对自己可见而已,你不可能通过一个变量来进行双方的通信!!!


下面来做个小测试,关于变量的可见性:

volatile int g_val = 0;
int main()
{
    int val = 0;
	pid_t fpid;
	
	printf("\nprocess\tpid\tppid\tcpid\tval\tval_address\tg_val\tg_val_address \n");
	printf("%s\t%d\t%d\t  \t%d\t%x\t%d\t%x\n","main",getpid(),getppid(),val,&val,g_val,&g_val);
	
	if ((fpid = fork()) < 0)
	   printf("fork error\n");
	else if (0 == fpid)//child
	{
	   val++;
	   g_val++;
	   printf("%s\t%d\t%d\t%d\t%d\t%x\t%d\t%x\n","child",getpid(),getppid(),fpid,val,&val,g_val,&g_val);
	}
	else//father
	{
	   sleep(5);//wait for child exit,and we can sure the variable has changed but maybe the result is not like what we think it is. 
	   //printf("father wakes up\n");
	   printf("%s\t%d\t%d\t%d\t%d\t%x\t%d\t%x \n","father",getpid(),getppid(),fpid,val,&val,g_val,&g_val);
	}
	
	return 0;
}

测试结果:

[root@localhost src]# ./sig_main 

process   pid	    ppid	cpid	 val	 val_address	  g_val	   g_val_address 
main	  2976	   2698	  	         0	   bfcd7ce8	     0	     8049138
child	  2977	   2976	         0	 1	   bfcd7ce8	     1	     8049138
father	  2976	   2698  	2977	 0	   bfcd7ce8	     0	     8049138 
[root@localhost src]# 


从结果可以看出来:子进程修改的值并没有对父进程有影响。但是变量的地址都是一样的,数据却是不一样的。怎么理解。。。。。个人觉得吧,物理地址一定是一样的,但是它们的逻辑地址是不一样的,就是进程的虚拟地址是不一样的,不知道对不对,先这样觉得吧。


2.信号响应

   对于父子进程,都能响应信号,但是,如果我发一个SIGINT信号一次,那么父子进程都响应这个信号(前提是父子进程都注册了这个信号响应)吗?另外信号处理程序只有一个,父子进程是共享的,那么,当父子进程分别响应信号时,是否在各自的进程领域内?


从第一点来看,父子进程正文段是共享的,但数据存储区并不是共享的,那么数据是属于进程领域内的。

那么问题是什么?这里想验证一下,同一个信号响应处理程序是不是对父子的调用是独立的。


测试代码:

<pre name="code" class="cpp">volatile int flag = 0;
void myhandler(int signo)
{
    pid_t fpid;
    if (SIGUSR1 == signo)
       printf("receive SIGUSER1\n");
    else if (SIGUSR2 == signo)
       printf("receive SIGUSER2\n");
	else if ( SIGALRM == signo)
	   {
	      fpid = getpid();
	      printf("pid = %d\n",fpid);
	      alarm(2);
	   }
	else if (SIGINT == signo)
	{
	   printf("SIGINT catch\n");
	   fpid = getpid();
	   printf("in handle pid = %d\n",fpid);   
	}
    else if (SIGCHLD == signo)
	{
	   printf("SIGCHLD catch\n");
	   fpid = getpid();
	   printf("in handle pid = %d\n",fpid);   
       flag = 1; 
	   printf("wait child succeed\n");
	}
	   	   
    else
       printf("received signal %d\n",signo);
	   
	
	//printf("alarm handler\n");
}

int main()
{
	pid_t fpid;
	sigset_t newmask,oldmask,waitmask;
	/*为父子进程同时注册信号*/
       if (signal(SIGUSR2,myhandler) == SIG_ERR)
          printf("can't catch SIGUSER2");
       if (signal(SIGUSR1,myhandler) == SIG_ERR)
          printf("can't catch SIGUSER1");
       if (signal(SIGALRM,myhandler) == SIG_ERR)
          printf("can't catch SIGUSER1");
       if (signal(SIGQUIT,myhandler) == SIG_ERR)
          printf("can't catch SIGQUIT");
       if (signal(SIGINT,myhandler) == SIG_ERR)
          printf("can't catch SIGQUIT");
       if (signal(SIGCHLD,myhandler) == SIG_ERR)
	      printf("can't catch SIGCHLD");
    
	sigemptyset(&waitmask);
     	fpid = fork();
	
	if (0 == fpid)//child
	{
	   printf("start child pid = %d\n",getpid());
	   //sigaddset(&newmask,SIGINT);
	   //sigprocmask(SIG_BLOCK,&newmask,&oldmask);//子进程不接收中断信号
	   sleep(10);
	   printf("child exit\n");
		
	}
	else//father
	{	  
	   printf("wait child,father pid=%d\n",getpid());
	   while (flag == 0)//这样做是因为这期间可能会有很多信号到来,然后sigsuspend会被唤醒,但这并是我想要的信号,所以等。
	     sigsuspend(&waitmask);
	   fpid = wait();
	   flag = 0;
	   printf("father exit,getchild pid:%d \n ",fpid);
	}
	
	
	
    return 0;
}


 

说明:

测试程序流程:子进程先休眠10s,在这个过程任何信号可唤醒子进程。父进程等待子进程结束,而这个过程接收任意信号。



测试结果:

[root@localhost src]# ./sig_main 
wait child,father pid=8065
start child pid = 8066
^CSIGINT catch
in handle pid = 8066         
child exit
SIGCHLD catch
in handle pid = 8065
wait child succeed
SIGINT catch
in handle pid = 8065
father exit,getchild pid:8066 
[root@localhost src]# 

NOTE:

上面的^C 是crtl+c 发送一个SIGINT信号


一个SIGINT信号,父子进程都接收了,并且是分别接收的,从PID就可以看出来。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

fork()父子进程变量之间的关系与信号的响应 的相关文章

随机推荐

  • Arduino基础 — Arduino 字符串

    Arduino 字符串 在Arduino编程中有两位字符串 1 字符数组 与C语言编程使用相同 2 Arduino 字符串 它允许我们在代码中使用字符对象 字符串数组 字符串是一个特殊的数组 在字符串的末尾有一个额外的元素 其值总是为0 零
  • 面试鹅厂,我三面被虐的体无完肤……

    戳蓝字 CSDN云计算 关注我们哦 作者 codegoose 来源 https segmentfault com a 1190000017864721 经过半年的沉淀 加上对MySQL redis和分布式这块的补齐 终于重拾面试信心 再次出
  • SOLO代码阅读解析

    SOLO是一种直接预测instance mask的范式 摒弃了之前top down和bottom up两种主流的实例分割方法 从而pipeline更加简洁直观 这篇文章以官方代码中的demo为例 简单梳理一下SOLO在inference时的
  • etcd编译与简单使用

    etcd是用go语言编写的 首先安装与配置go语言的环境 ubuntu1404安装go开发环境参考 http xhrwang me 2014 12 22 go dev env html http go ctolib com topics 3
  • 队列的C语言实现

    头文件 Queue h typedef int BTDataType typedef struct BindaryTreeNode BTDataType data struct BindaryTreeNode left struct Bin
  • Elasticsearch解决只能查询10000条数据方案

    es官方默认限制索引查询最多只能查询10000条数据 查询第10001条数据开始就会报错 Result window is too large from size must be less than or equal to 但是很多时候10
  • tan和atan--正切和反正切函数

    功能简介 求变量的正切和反正切 语法格式 1 Y tan X 求X中元素的正切值 元素可以为复数 tan函数是周期函数 以pi为一个周期 当元素值等于pi 2的奇数倍时 函数失去定义 在三角函数中 tan x sin x cos x 2 Y
  • P1025 数的划分(dfs/dp)

    题目 https www luogu com cn problem P1025 题目描述 将整数n分成k份 且每份不能为空 任意两个方案不相同 不考虑顺序 例如 n 7 k 3 下面三种分法被认为是相同的 1 1 5 1 5 1 5 1 1
  • 关于vscode头文件出现红色波浪曲线问题

    关于vscode头文件总显示红色波浪曲线解决 方法1 可以在设置中直接关掉 方法2 没有添加编译器的路径 我们可以找到gcc exe所在的路径 或者重新安装官网下载 解压到c盘非中文路径 复制路径 主要是看gcc g 所在的路径
  • 环信iOS使用步骤及使用总结

    环信iOS使用步骤及使用总结 第一步 集成 请参照环信官方文档http docs easemob com im 300iosclientintegration 20iossdkimport 集成 iOSSDK 在您阅读此文档时 我们假定您已
  • OpenAI最新官方ChatGPT聊天插件接口《插件部署上生产》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(五)(附源码)

    Plugins in production 插件部署上生产 前言 Rate limits 速率限制 Updating your plugin 更新您的插件 Plugin terms Domain verification and secur
  • TCP与UDP的区别

    一 前言 TCP IP 中有两个具有代表性的传输层协议 分别是 TCP 和 UDP 二 TCP IP网络模型 计算机与网络设备要相互通信 双方就必须基于相同的方法 比如 如何探测到通信目标 由哪一边先发起通信 使用哪种语言进行通信 怎样结束
  • 因果推断4--Causal ML(个人笔记)

    目录 1 安装教程及官方文档 1 1 pip安装 1 2 API文档 1 3 代码仓库 2 Uplift模型与主要方法介绍 2 1 发放代金券 2 2 多treatment 2 3 实验方法 3 causalml inference tre
  • IDEA中使用单元测试(Junit)Scanner控制台无法输入问题解决 2022.12.13

    步骤 Help Edit Custom VM options 在idea64 exe vmoptions文件中最后一行添加如下内容 Deditable java test console true 操作完成保存后一定要重启IDEA ps 通
  • Vue中读取本地图片实现预览和上传

    先看效果图 上面展示了两张从本地添加的准备上传服务器的预览图片 效果还不错吧 哈哈哈 下面是该页面中紫色框的布局与实现代码 我想有些人也不喜欢HTML中默认的上传文件样式 实在辣眼睛
  • C\C++ 中的绝对值函数:abs()、cabs()、fabs()、labs()

    不同类型的数据使用不同类型的绝对值函数 整型 int abs int i 返回整型参数i的绝对值 复数 double cabs struct complex znum 返回复数znum的绝对值 双精度浮点型 double fabs doub
  • egg-swagger token验证无效解决方案

    先看效果 注意 我这边只讲重点 1 首先 egg swagger 怎么安装看我的这篇egg swagger demo 无数据库版 2 无swagger的 token 验证 我的这篇eggjs token生成和验证demo 3 我这边只讲 重
  • Raft一致性算法

    摘要 Raft 是一种为了管理复制日志的一致性算法 它提供了和 Paxos 算法相同的功能和性能 但是它的算法结构和 Paxos 不同 使得 Raft 算法更加容易理解并且更容易构建实际的系统 为了提升可理解性 Raft 将一致性算法分解成
  • Prometheus详解(四)——Prometheus简单使用

    今天继续给大家介绍Linux运维相关知识 本文主要内容是Prometheus简单使用 在上文Prometheus详解 三 Prometheus安装部署中 我们介绍了Prometheus的安装和部署 今天 我们就来介绍一下Prometheus
  • fork()父子进程变量之间的关系与信号的响应

    1 变量关系 根据 unix 高级环境编程 中的一句话 子进程和父进程继续执行fork调用之后的指令 子进程是父进程的副本 例如 子进程获取父进程数据空间 堆和栈的副本 注意 这是子进程所拥有的副本 父子进程并不共享这些存储空间部分 父子进