Linux学习笔记之六(进程之间的管道通信和信号处理)

目录

  • 1、管道通信
    • 1.1、无名管道
    • 1.1、有名管道
  • 2、信号处理
    • 2.1、信号的种类和发送
    • 2.2、信号的接受和处理

1、管道通信

管道通信是一个设备中进程与进程之间通信的一种方式,分为无名管道和有名管道两种。前者只能用于有亲缘关系的进程之间的通信,如父子进程等,后者则没有亲缘限制。此外,管道通信从根本上还是通过内核的一种半双工的通信方式。

1.1、无名管道

无名管道又称匿名管道,即通信双方无需知道对方的pid号,仅通过一些管道提供的描述符便可往管道缓冲区读写数据。在具体使用上,管道应比子进程更先被创建,因为而后被创建的子进程会拷贝父进程的地址空间,从而保证父子进程使用的是同一条管道。当然,管道通信并不限制进程数量。
pipe( )是创建无名管道的一个重要函数,我们可以查看其函数说明:

man 2 pipe

在这里插入图片描述

  • int pipefd[2]:该数组是用于存放从pipe()取出的管道描述符。其中,pipefd[0]是读取数据的描述符,pipefd[1]是写入数据的描述符。
  • int flags:不常用,不说。
  • 返回值:成功返回0,反之返回-1。

再看个例子便知道怎么用了:
该例子完成的任务:父进程从终端中读取用户输入的字符并写入管道,然后子进程从管道中读取数据之后将字符一个个打印到终端中来。
注意的点:管道读取是一种阻塞式读取,即如果某一进程想从本没有数据的管道中读取数据,就会一直阻塞在那里,等到有数据进入管道缓冲区。并且这种阻塞只发生在最开始的时候,如果中途管道没有数据了,它还是可以正常读取,只不过返回值是0.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int pipe_fd[2];char buf;if(argc != 2){printf("Usage :%s <string>\n", argv[0]);return -1}    pipe(pipe_fd);			//create a pipepid_t cpid = fork();	//create a child process	if(cpid == 0)           //enter the child process{close(pipe_fd[1]);while(read(pipe_fd[0], &buf, 1)) write(1, &buf, 1);printf("\n");close(pipe_fd[0]);_exit(0);       //recommend to use the _exit() to exit in the child process}else                    //enter the parent process{close(pipe_fd[0]);write(pipe_fd[1], argv[1], strlen(argv[1]));close(pipe_fd[1]);wait(NULL);     //waiting for his child to  exitexit(0);}return 0;
}

1.1、有名管道

有名管道相较于无名管道,可以用于无亲缘关系进程之间的通信。并且它的逻辑也更为清晰,使用的时候,我们可以在文件夹中创造一个管道文件,然后其他进程便可往这个管道文件读写数据,最终实现进程通信的目的。
下面看看具体操作步骤:
第一步,在文件夹中创建一个管道文件(以test_pipe为例)。

mkfifo test_pipe

在这里插入图片描述
除了在终端中创建一个FIFO文件夹,我们还可以在程序中创建文件夹。同样是用mkfifo函数,参考

man 3 mkfifo

第二步,创建一个进程往test_pipe写入数据。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int fd = open("./test_pipe", O_WRONLY);		//open the pipe filewrite(fd, "hello", 5);						//write something into the fileclose(fd);									//close the file, don't forget!return 0;
}

第三步,创建一个新的进程往test_pipe写入数据。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{  int fd = open("./test_pipe", O_WRONLY); if(fd == -1){printf("an error occured opening the pipe file\n");return -1;}while(1){write(fd, "hello!", 6);printf("writing successfully!\n");sleep(1);}close(fd);return 0;
}

第四步,创建一个新的进程从test_pipe读出数据。注意在读取操作的时候要考虑读取延时问题,y因为如果读的速度比写的速度还慢,那可能会读到的不仅仅是这一次写入的数据,还可以能拼接一些上一次的数据。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{int fd = open("./test_pipe", O_RDONLY);char buf[6];while(1){//sleep(1);read(fd, buf, 6);printf("what i read is: %s\n", buf);memset(buf, 0 ,sizeof(buf));			//clear the buf       }close(fd);return 0;
}

通过以上步骤便可以实现两个没有亲缘关系之间的通信了。但在我学习过程中,还遇到很多问题,这也一一列举出来吧。
第一,有名管道的通信是读写阻塞的,但它阻塞不是阻塞在read和write函数上,而是阻塞在open函数上。这似乎意味着,有名管道的通信始于两个进程同时访问该管道。
第二,当写进程终止之后,读进程还是可以继续运行,只是读出的数据是“空”而已。但如果读进程终止,写进程也会跟着终止。个人感觉问题出现在write函数上,即write函数检测到没有进程读数据,它就会停止,我写了程序验证了一下也大抵如此。

2、信号处理

2.1、信号的种类和发送

信号的发送最常用的是kill()函数,kill即可以在终端直接使用,也可以在代码里以函数的形式使用。我们先看看发送的信号的种类吧。
在这里插入图片描述
总共有64种信号,一般常用的就几个,其他需要的时候再去网上查就行了。如果我们想在终端中使用命令kill某一个进程,可以按以下操作来。

kill -<number> <pid>

比如我这里kill了一个叫test的进程。
在这里插入图片描述
接下来看看kill怎么在程序中使用,在此之前可以看一下它的官方文档(man 2 kill),在这里我就直接提取它的函数原型出来。

int kill(pid_t pid, int sig);

pid_t pid:指目标进程的pid号,其实就是你要把信号发给哪个进程。
int sig:你要发的是几号信号。
返回值:成功返回0,失败返回-1。

在函数中的使用方法也十分简单,下面以杀死pid号为3234的进程为例。

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>int main()
{//if you wanna kill yourself, you can use kill(getpid(), 9);int flag = kill(3234, 9);if(flag < 0){printf("kill error!");}return 0;
}

除了kill()函数可以用来发送信号,alarm(), raise()等函数也可以。
其中:

raise(sig);  <==> kill(getpid(), sig);

而alarm函数的原型是:

unsigned int alarm(unsigned int seconds);

使用方法也很简单,该函数的输入参数是N秒。当一个进程调用这个函数N秒之后,该进程就会收一个SIGALAM的信号,该信号可以终止进程。

2.2、信号的接受和处理

进程信号接受的根本要求肯定是该进程还存在,一般而言保证进程存在有三种方式:

  1. 采用sleep()函数让进程进入睡眠,但睡眠状态并不是永远的,它有一个输入参数来限制睡眠的时间。比如睡眠三秒:sleep(3);
  2. 采用pause()函数让进程进入睡眠状态,进入睡眠状态之后,无论你对进程进行任何操作,进程都会退出睡眠状态。
  3. 利用while循环让进程一直处于运行状态。

在进程存在的前提下便可以考虑进程对于信号的处理方式了,处理方式也是有三种,分别是忽略(SIG_IGN),默认(SIG-DFL)和捕获。而信号的处理一般用signal()函数,下面我们来看看它的函数原型:

sighandler_t signal(int signum, sighandler_t handler);

int signum:处理的信号的编码/种类,除了该信号,其他信号一概忽略。
sighandler_t handler:当接受到该信号的时候,处理的方式是什么(默认、忽略还是捕获)。
返回值:成功则返回被处理信号的编号,失败则返回SIG_ERR。

看个例子吧。

#include <stdio.h>
#include <signal.h>void t_function(int sig);int main()
{signal(SIGINT, t_function);while(1);                //keep this process running.return 0;
}void t_function(int sig)
{printf("capture successfully!\n");
}

将以上代码写进一个叫test.c的文件,然后再编译成名为a.out的可执行文件。接着执行a.out,并在另一个终端输入kill -2 5961给a.out这个进程发送SIGINT的信号,可以看到捕获成功。
在这里插入图片描述

这里有几个注意的点:
第一,SIGKILL和SIGSTOP不能被捕获和忽略。
第二,捕获的函数,必须有且一个属于参数,这个输入参数其实是signal的标号,比如SIGINT的标号就是2。
第三,SIGINT其实就是键入的Ctrl+C的组合效果。(在Linux系统中Ctrl+C表示中断)

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

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

相关文章

Proteus仿真--用DS1302与12864LCD设计的可调式中文电子日历

本文主要介绍用DS1302和12864 LCD的可调式中文电子日历&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中12864LCD上面显示中文年月日信息时间信息&#xff0c;按键K1-K4&#xff0c;K1用于年月日时分选择&#xff0c;K2用于加功能&#xff0c;K3用于…

CSS水平居中与垂直居中的方法

当我们页面布局的时候&#xff0c;通常需要把某一个元素居中&#xff0c;这一篇文章为大家介绍一下居中的几种方法&#xff0c;本人文笔有限&#xff0c;请见谅&#xff01; 一.水平居中 行内元素水平居中的方法&#xff0c;我们使用text-align:center; <!DOCTYPE html&g…

夜神模拟器 burp抓包 ADB 微信小程序

夜神模拟器 burp抓包 ADB 微信小程序 初始环境准备应用连接证书转换设置夜神模拟器环境ADB配置测试burp抓包 初始环境准备 既然想了解如何抓包&#xff0c;我想大多数是已经安装好 夜神模拟器 和 Burp 了&#xff0c;这里就不在赘述&#xff0c;直接开始操作。 openssl 的下载…

module java.base does not “opens java.io“ to unnamed module

环境 如上图所示&#xff0c; Runtime version的版本是JAVA 17 项目所需要JDK版本为JAVA 8 解决

Alibaba Cloud Linux 3安装Docker

进行docker安装&#xff08;以社区版为例&#xff09; 添加docker-ce的dnf源 dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo2.安装Alibaba Cloud Linux 3专用的dnf源兼容插件 dnf -y install dnf-plugin-releasever-adap…

⑦【Redis GEO 】Redis常用数据类型:GEO [使用手册]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Redis GEO ⑦Redis GEO 基本操作命令1.geoadd …

Ubuntu系统安装docker

1.检查是否安装老版本 检查卸载老版本docker ubuntu下自带了docker的库&#xff0c;不需要添加新的源。 但是ubuntu自带的docker版本太低&#xff0c;需要先卸载旧的再安装新的。 apt-get remove docker docker-engine docker.io containerd runc 如果不能正常卸载&#x…

如何通过 Al 的能力提升编程的效率?

通过人工智能&#xff08;AI&#xff09;的技术&#xff0c;可以提升编程效率和能力。以下是一些建议和方法&#xff1a; 代码自动生成&#xff1a;使用AI技术&#xff0c;可以根据程序员的需求和输入&#xff0c;自动生成代码。这可以提高编程效率&#xff0c;减少编写代码所需…

AI换脸教程

方法一、MJ换脸大法 1.点击这个网站添加一个机器人到自己的服务器 https://discord.com/oauth2/authorize?client_id1090660574196674713&permissions274877945856&scopebot 2. /saveid 回车选择你自己的照片&#xff0c;并且在名字框命名身份&#xff0c;回车 3.…

微信小程序文件预览和下载-文件系统

文件预览和下载 在下载之前&#xff0c;我们得先调用接口获取文件下载的url 然后通过wx.downloadFile将下载文件资源到本地 wx.downloadFile({url: res.data.url,success: function (res) {console.log(数据,res);} })tempFilePath就是临时临时文件路径。 通过wx.openDocume…

Python爬虫实战-批量爬取豆瓣电影排行信息

大家好&#xff0c;我是python222小锋老师。 近日锋哥又卷了一波Python实战课程-批量爬取豆瓣电影排行信息&#xff0c;主要是巩固下Python爬虫基础 视频版教程&#xff1a; Python爬虫实战-批量爬取豆瓣电影排行信息 视频教程_哔哩哔哩_bilibiliPython爬虫实战-批量爬取豆瓣…

每日一题(LeetCode)----链表--链表中的下一个更大节点

每日一题(LeetCode)----链表–链表中的下一个更大节点 1.题目&#xff08;1019. 链表中的下一个更大节点&#xff09; 给定一个长度为 n 的链表 head 对于列表中的每个节点&#xff0c;查找下一个 更大节点 的值。也就是说&#xff0c;对于每个节点&#xff0c;找到它旁边的第…

停车管理系统

1 用户信息管理 2 车位信息管理 3 车位费用设置 4 停泊车辆查询 5 车辆进出管理 6 用户个人中心 7 预定停车位 8 缴费信息 9 业务逻辑详解 1 用户停车&#xff1a;user用户登录&#xff0c;在预定停车位菜单&#xff0c;选择一个车位点击预定即可 2 车辆驶出&#xff1a;admin…

【ArcGIS Pro微课1000例】0038:基于ArcGIS Pro的人口密度分析与制图

文章目录 一、人口密度二、人口密度分析1. 点密度分析2. 核密度分析三、结果比对一、人口密度 人口密度是指单位土地面积上居住的人口数,通常以每平方千米或每公顷内的常住人口为单位计算。人口密度同资源、经济密切结合,因此,科学准确地分析人口密度的分布情况,对合理制定…

kubernetes使用nfs创建pvc部署mysql stateful的方法

kubernetes创建的pod默认都是无状态的&#xff0c;换句话说删除以后不会保留任何数据。 所以对于mysql这种有状态的应用&#xff0c;必须使用持久化存储作为支撑&#xff0c;才能部署成有状态的stateful. 最简单的方法就是使用nfs作为网络存储&#xff0c;因为nfs存储很容易被…

无需公网IP,使用MCSM面板一键搭建我的世界Minecraft服务器联机游戏

文章目录 前言1.Mcsmanager安装2.创建Minecraft服务器3.本地测试联机4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射内网端口 5.远程联机测试6. 配置固定远程联机端口地址6.1 保留一个固定TCP地址6.2 配置固定TCP地址 7. 使用固定公网地址远程联机 前言 MCSManager是一个…

基于springboot网上超市管理系统

基于springboot网上超市管理系统 摘要 随着互联网的快速发展&#xff0c;电子商务行业迎来了蓬勃的发展&#xff0c;网上超市作为电子商务的一种形式&#xff0c;为消费者提供了便利的购物体验。本文基于Spring Boot框架&#xff0c;设计和实现了一个网上超市管理系统&#xff…

图的邻接矩阵,邻接表的C语言实现(408真题)

图的邻接矩阵 数据结构定义 #define MAXV 50;//顶点数目的最大值 typedef struct{int vex[MAX]; //顶点表 int edge[MAXV][MAXV]; //邻接矩阵 int edgeNum,vexNum; //图中实际的边数和顶点数 }MGraph;初始化 void Matrix_Init(MGraph *Mgraph) {int v1, v2;//存储有边的…

Redis key的类型以及命令

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…

[AutoSAR 存储] 汽车智能座舱的存储需求

公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《AutoSAR 存储》 <<<< 返回总目录 <<<< 1 智能座舱的发展&#xff1a; 1.1 发展历史 车辆信息娱乐系统的发展可以分为三个阶段。 机械化阶段 在上世纪90年代&#xff0c;车辆仪表盘…