【Linux】实验四 进程信号通信

news2024/9/23 21:18:44

文章目录

  • 一、实验目的
  • 二、 实验内容
  • 三、 实验原理
    • 1、信号
      • 3.1.1 信号的基本概念
      • 3.1.2、信号的发送
    • 2、所涉及的系统函数调用
      • 3.2.1 fork()
      • 3.2.2 kill( )
        • This is my question.
      • 3.2.3 signal( )
        • 代码例子
      • 3.2.4 getpid()
      • 3.2.5 wait()
  • 四、 实验内容
  • 五、程序代码
    • 运行结果
  • 六、实验总结及心得体会:
    • 心得:
    • 思考题:
  • 每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

一、实验目的

1、了解什么是信号
2、了解和熟悉 LINUX 支持的信号量机制
3、熟悉 LINUX 系统中进程之间软中断通信的基本原理

二、 实验内容

1、根据 4.1 程序流程图,设计程序。用 fork( )创建两个子进程,再用系统调用 signal( )让父进程捕捉键盘上来的中断信号(即按^c 键);
捕捉到中断信号后,父进程用系统调用 kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:
Child process1 is killed by parent!
Child process2 is killed by parent!

父进程等待两个子进程终止后,输出如下的信息后终止:
Parent process is killed!
2、分析利用软中断通信实现进程同步的机理

三、 实验原理

1、信号

3.1.1 信号的基本概念

每个信号都对应一个正整数常量(称为 signal number,即信号编号。

  1. 定义在系统头文件<signal.h>中),代表同一用户的各个进程之间传送事先约定的信息的类型,用于通知某进程发生了某异常事件

  2. 每个进程在运行时,都要通过信号机制来检查是否有信号到达。
    – 若有,便中断正在执行的程序,转向与该信号相对应的处理程序,以完成对该事件的处理;
    – 处理结束后再返回到原来的断点继续执行。

  3. 实质上,信号机制是对中断机制的一种模拟,故在早期的 UNIX 版本中又把它称为软中断

信号与中断的相似点

  1. 采用了相同的异步通信方式;
  2. 当检测出有信号或中断请求时,都暂停正在执行的程序而转去执行相应的处理程序;
  3. 都在处理完毕后返回到原来的断点;
  4. 对信号或中断都可进行屏蔽。

信号与中断的区别

  1. 中断有优先级,而信号没有优先级,所有的信号都是平等的;
  2. 信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行;
  3. 中断响应是及时的,而信号响应通常都有较大的时间延迟。

信号机制具有以下三方面的功能

  1. 发送信号。发送信号的程序用系统调用 kill( )实现;
  2. 预置对信号的处理方式。接收信号的程序用 signal( )来实现对处理方式的预置;
  3. 收受信号的进程按事先的规定完成对相应事件的处理。

3.1.2、信号的发送

信号是操作系统用于通知进程的特殊事件。信号可以由系统内核发送,也可以由其他进程或用户发送。

  • 当系统内核发送信号时,通常是用于报告某些错误或异常情况,例如内存访问越界、无效指令等。
  • 当其他进程或用户发送信号时,通常是用于控制其他进程的行为。例如,使用kill命令可以向其他进程发送信号,控制其终止、暂停或继续执行。
  • 在 C/C++ 中,可以使用 kill 函数来发送信号。

进程用 kill( )向一个进程或一组进程发送一个信号。


2、所涉及的系统函数调用

3.2.1 fork()

系统调用格式: pid_t fork( void); 
#include <sys/types.h>
#include <unistd.h>

pid_t 是一个宏定义,其实质是 int 被定义在#include<sys/types.h>中

功能:创建子进程
返回值: 若成功调用一次则返回两个值
子进程返回 0
父进程返回 子进程 ID
否则,出错返回 -1.

函数说明:

  • 一个现有进程可以调用 fork 函数创建一个新进程。由 fork 创建的新进程被称为子进程(child process)。
  • fork 函数被调用一次,返回两次。两次返回的唯一区别是子进程中返回 0 值而父进程中返回子进程 ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

注意,

  • 子进程持有的是上述存储空间的 “副本”,这意味着父子进程间不共享这些存储空间。
  • linux 将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。

3.2.2 kill( )

系统调用格式: int kill(pid,sig)
#include <sys/types.h>
#include <signal.h>

参数定义 int pid,sig;
其中,pid 是一个或一组进程的标识符,参数 sig 是要发送的软中断信号。
(1)pid>0 时,核心将信号发送给进程 pid
(2)pid=0 时,核心将信号发送给与发送进程同组的所有进程
(3)pid=-1 时,核心将信号发送给所有用户标识符真正等于发送进程的有效用户标识号的进程。

This is my question.

当 pid 参数的值等于 -1 时,核心将信号发送给所有用户标识符真正等于发送进程的有效用户标识号的进程。这句话什么意思?

  • 核心将信号发送给具有与发送进程相同的有效用户标识符的所有进程。
  • 有效用户标识符是指系统分配给用户的唯一标识符,用于标识用户身份。

例如,如果你的有效用户标识符是 1000,那么当你使用 kill(-1, sig) 发送信号时,核心会将信号发送给所有具有有效用户标识符 1000 的进程。


3.2.3 signal( )

预置对信号的处理方式,允许调用进程控制软中断信号。

系统调用格式: signal(sig,function)
 #include <signal.h>

参数定义
signal(sig,function)

  • int sig;
  • void (*func) ( )

其中 sig 用于指定信号的类型,sig 为 0 则表示没有收到任何信号,

余者如下表:
在这里插入图片描述
在这里插入图片描述
function:在该进程中的一个函数地址,在核心返回用户态时,它以软中断信号的序号作为参数调用该函数,对除了信号 SIGKILL、SIGTRAP 和SIGPWR 以外的信号,核心自动地重新设置软中断信号处理程序的值为 SIG_DFL,一个进程不能捕获 SIGKILL 信号。
function 的解释如下:

(1)function=1 时,进程对 sig 类信号不予理睬,亦即屏蔽了该类信号;
(2)function=0 时,缺省值,进程在收到 sig 信号后应终止自己;
(3)function 为非 0,非 1 类整数时,function 的值即作为信号处理程序的指针。

代码例子

#include <stdio.h>
#include <signal.h>
// 信号处理程序
void sigint_handler(int signum) 
{
	printf("收到 SIGINT 信号!\n");
}
int main(int argc, char*argv[]) 
{
// 设置 SIGINT 信号的处理程序
	signal(SIGINT, sigint_handler);
	return 0;
}

在上面的程序中,当收到 SIGINT 信号时,程序会调用 sigint_handler() 函数。你可以在这个函数内部执行任何你希望在收到信号时执行的操作。


3.2.4 getpid()

pid_t getpid(void)
#include <sys/types.h>
#include <unistd.h>

功能:获取自己的进程 ID 号
参数:无
返回值:本进程的 ID 号


3.2.5 wait()

pid_t wait(int *wstatus);
#include <sys/wait.h>

功能:等待进程状态变化


四、 实验内容

在这里插入图片描述
程序所需头文件如下:

#include <stdio.h> //stdin, stdout, stderr,scanf(),printf()
#include <stdlib.h> //exit()
#include <signal.h> //signal()
#include <unistd.h> //getpid()
#include <sys/wait.h> //wait()

五、程序代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

#define SIG17 17
#define SIG16 17
#define SIG2  17

//定义全局变量
pid_t child_pid1,child_pid2;

/*信号中断函数*/
void sigint_handler(int signum)
{
  // 向两个子进程发送信号
  kill(child_pid1, SIGINT);
  kill(child_pid2, SIGINT);
}

/*子程序中断函数*/
void child_handler(int signum) 
{
  printf("child process %d is killed by parent!\n", getpid());
  exit(0); 
}

/*主函数*/
int main(int argc, char *argv[]) 
{
  // 创建两个子进程
  child_pid1 = fork();
  if (child_pid1 == 0) 
  {
    // 这是第一个子进程
    signal(SIGINT, child_handler);
    while (1) 
    {
      sleep(1);
    }
  }

  child_pid2 = fork();
  if (child_pid2 == 0) 
  {
    // 这是第二个子进程
    signal(SIGINT, child_handler);
    while (1) 
    {
      sleep(1);
    }
  }

  // 输出父进程和子进程的 PID
  printf("parent PID:%d\n", getpid());
  printf("child1 PID:%d\n", child_pid1);
  printf("child2 PID:%d\n", child_pid2);

 //使用信号类型 2 暂停父进程
  printf("stop PID:%d by signal 2\n", getpid());
  kill(getpid(), SIG2);

  // 使用信号类型 17 暂停子进程 2
  printf("stop PID:%d by signal 17\n", child_pid2);
  kill(child_pid2, SIG16);

  // 使用信号类型 16 暂停子进程 1
  printf("stop PID:%d by signal 16\n", child_pid1);
  kill(child_pid1, SIG17);

  // 使用信号类型 2 暂停两个子进程
  printf("stop PID:%d by signal 2\n", child_pid1);
  printf("stop PID:%d by signal 2\n", child_pid2);
  kill(child_pid2, SIG2);
  kill(child_pid1, SIG2);

  // 为父进程设置信号处理程序
  signal(SIGINT, sigint_handler);

  // 等待两个子进程终止
  waitpid(child_pid1, NULL, 0);
  waitpid(child_pid2, NULL, 0);

  printf("Parent peocess is killed!\n");

  return 0;
}


运行结果

在这里插入图片描述

六、实验总结及心得体会:

心得:

  1. 在这个实验中,我使用了 fork()函数来创建子进程,并使用 signal()函数来处理信号,使用 kill()函数来发送信号。还使用了 wait()函数来等待子进程的终止,并使用 exit()函数来退出进程。
  2. 通过这个实验,我更好地了解进程的工作原理,包括进程的创建、信号的发送和接收以及进程的终止。这对于我们编写多进程程序是非常有帮助的

思考题:

  1. 当首次调用新创建进程 fork()时,其入口在哪里?

答:当首次调用 fork() 函数创建新进程时,该进程的入口在函数调用处。也就是说,新进程将从 fork() 函数调用的位置开始执行,而不是从函数的开头开始执行。


  1. 程序段中调用 wait(0)起什么作用?

答:wait() 函数是一个系统调用,用于让当前进程等待其子进程的终止。它的第一个参数可以是一个指向某个整型变量的指针,用于存储子进程的终止状态。如果不希望父进程获取子进程的终止状态,可以将参数设置为 0。
当调用 wait(0) 时,当前进程会等待其任意子进程的终止,然后返回该子进程的终止状态。


  1. 程序段中每个进程退出时都用了语句 exit(0),为什么?

答: exit(0) 函数用于终止当前进程,并返回一个状态码。这里的状态码是 0,表示进程正常结束。


每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

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

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

相关文章

来玩,前端性能优化(+面试必问:宏任务和微任务)

前端性能优化相关的“技能点”笔者之前也写过几篇&#xff0c;但是大多都是小打小闹。我重新整理了曾经使用过的性能优化手段。本文介绍三种方案&#xff1a;页面资源预加载、服务请求优化和非首屏视图延迟加载。 页面资源预加载 页面是不可能真正预加载的&#xff0c;但是有…

Linux | PCIe Hotplug | 概念及工作原理的不完全总结

本文对 PCIe 热插拔的概念及工作原理进行不完全总结。 更新&#xff1a; 2022 / 12 / 31 Linux | PCIe Hotplug | 概念及工作原理的不完全总结热插拔组成部件实现代码通知式热插拔线程中断功耗管理意外移除错误处理整合移除BAR参考链接参考这里 1’ 2’ 3‘ 4’ 5 1992年初始…

postgresql数据库安装,备份还原

一.postgresql数据库安装 1.下载软件包 地址&#xff1a;PostgreSQL: File Browser 2.解压安装 [rootpostgresql u01]# tar -zxf postgresql-14.2.tar.gz安装环境 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel op…

(深度学习快速入门)第一章:深度学习概述、应用、学习路线和框架选择

文章目录一&#xff1a;基本概念&#xff08;1&#xff09;神经网络&#xff08;2&#xff09;感知器&#xff08;3&#xff09;深度学习&#xff08;4&#xff09;前向运算和反向传播二&#xff1a;DeepLearning学习路线三&#xff1a;深度学习应用&#xff08;1&#xff09;生…

基于Stream的Redis消息队列

目录一、消息队列二、基于List结构模拟消息队列基于List的消息队列的优点&#xff1a;基于List的消息队列的缺点&#xff1a;三、基于PubSub的消息队列基于PubSub的消息队列的优点&#xff1a;基于PubSub的消息队列的缺点&#xff1a;四、基于Stream的消息队列1、XADD语法2、XR…

C++ Primer笔记——allocator、unique_ptr和release、智能指针与动态数组、阻止拷贝

目录 一.P418 unique_ptr和release 二.P426 智能指针与动态数组 &#xff08;一&#xff09;.unique_ptr &#xff08;二&#xff09;.shared_ptr 三.P428 allocator &#xff08;一&#xff09;.申请空间 &#xff08;二&#xff09;.初始化构造 &#xff08;三&#…

活动星投票少儿模特大赛网络评选制作一次图文分组投票怎么制作

关于微信投票&#xff0c;我们现在用的最多的就是小程序投票&#xff0c;今天的网络投票&#xff0c;在这里会教大家如何用“活动星投票”小程序来进行投票。我们现在要以“国际车展少儿模特大赛”为主题进行一次投票活动&#xff0c;我们可以在在微信小程序搜索&#xff0c;“…

aws cloudformation 使用模板配置 ecs 蓝绿部署

参考资料 Perform ECS blue/green deployments through CodeDeploy using AWS CloudFormationAWS::CodeDeployBlueGreen 在之前的文章中&#xff0c;使用codepipeline中通过控制台的方式创建了ecs蓝绿部署的demo。实际上可以单独通过codedeploy完成ecs服务的蓝绿部署 参考官…

【ARMv8 SIMD和浮点指令编程】Libyuv I420 转 ARGB 流程分析

Libyuv 可以说是做图形图像相关从业者绕不开的一个常用库&#xff0c;它使用了单指令多数据流提升性能。以 ARM 处理为主线&#xff0c;通过 I420 转 ARGB 流程来分析它是如何流转的。 Libyuv 是一个开源项目&#xff0c;包括 YUV 的缩放和转换功能。 使用邻近、双线性或 box…

QThread、moveToThread用法详述

1.吐槽 QThread类提供了一种平台无关的方法对线程进行管理。但对于QThread类的熟练使用&#xff0c;即使是从事Qt开发多年的程序猿们&#xff0c;往往也会踩雷、入坑。总之&#xff1a;QThread类不好用、如果对该类理解不透&#xff0c;很容易导致程序崩溃。本人强烈建议&#…

(函数介绍)puts()函数

功能介绍 1. puts()函数用来向标准输出设备屏幕输出字符串并换行。 2. 函数的参数就是一个起始的地址&#xff0c;然后就从这个地址开始一直输出字符串&#xff0c;直到碰到\0就停止&#xff0c;然后这个\0是不进行输出的&#xff0c;是不能够算在里面的。与此同时&#xff…

十、字节缓冲流、字符流、转换流、对象操作流、对象序列化流

字节缓冲流 构造方法 字节缓冲流介绍 BufferedOutputStream&#xff1a;该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用BufferedInputStream&#xff1a;创建BufferedInputStream将创建一个内部缓冲区数…

2022年为什么要学习C语言?

为什么学习c语言 为什么学C语言逻辑&#xff1f; 为什么要学习C语言&#xff1f; 学习C语言的主要理由有以下几点&#xff1a; C语言可以作为学习计算机程序设计语言的入门语言&#xff1b; C语言是编写操作系统的首选语言&#xff0c;与计算机硬件打交道时灵巧且高效&…

labelImag安装与使用及构造数据集

在做目标检测任务时&#xff0c;需要进行标注&#xff0c;选择了LabelImg作为标注工具&#xff0c;下面是安装及使用过程。 我们使用Anconda的虚拟环境进行安装&#xff0c;激活环境后&#xff0c;执行&#xff1a; pip install labelimg -i https://pypi.tuna.tsinghua.edu.c…

代码随想录算法训练营第四天 java : 24. 两两交换链表中的节点 ,19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交,142环形链表II

文章目录Leetcode 24. 两两交换链表中的节点题目链接本题思路需要注意的点AC 代码Leetcode 19.删除链表的倒数第N个节点题目链接需要注意的点AC代码Leetcode面试题 02.07. 链表相交题目链接这个略了Leetcode 142环形链表II题目链接难点:AC代码今日收获**一朵玫瑰正马不停蹄地成…

【Linux】Linux下基本指令(三)

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《Linux》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1. Linux基本指令&#xff1a;&#xff08;续&#xff09; 1.1zip指令和u…

极智编程 | C++模板函数

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多笔记分享 大家好&#xff0c;我是极智视界&#xff0c;本文介绍一下 C模板函数。 模板函数是 C 中一种特殊的函数&#xff0c;它的类型参数列表用尖括号 <> 括起来&#xff0c;放在函数名的后面。使用模板函数&a…

Go 并发

来自 《Go 语言从入门到实战》 的并发章节学习笔记&#xff0c;欢迎阅读斧正&#xff0c;感觉该专栏整体来说对有些后端编程经验的来说比无后端编程经验的人更友好。。 Thread VS Groutine 创建时默认 Stack 大小&#xff1a;前者默认 1M&#xff0c;Groutint 的 Stack 初始化…

C语言可变参数与内存管理

有时&#xff0c;您可能会碰到这样的情况&#xff0c;您希望函数带有可变数量的参数&#xff0c;而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案&#xff0c;它允许您定义一个函数&#xff0c;能根据具体的需求接受可变数量的参数。下面的实例演示了这种函数的定…

LeetCode题解 二叉树(八):404 左叶子之和;513 找树左下角的值;112 路径总和;113 路径总和II

二叉树 404 左叶子之和 easy 左叶子结点也好判断&#xff0c;若某结点属于左结点&#xff0c;且无子树&#xff0c;就是左叶子结点 即也如此&#xff0c;所以如果要判断&#xff0c;必然要从父结点下手&#xff0c;涉及到三层结点的处理 如果要使用递归法&#xff0c;要使用…