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 pipe
        pid_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  exit
                exit(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 file
    write(fd, "hello", 5);						//write something into the file
    close(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表示中断)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二叉树详讲(一)---完全二叉树、满二叉树、堆

1.树的概念及其结构 1.1树的概念 树是一种非线性数据结构&#xff0c;是一种种抽象数据类型&#xff0c;旨在模拟具有树状结构的节点之间的层次关系。一颗树由诺干个点和诺干条边组成。每棵树只有一个根节点&#xff0c;根节点向下延申又有子节点和叶子节点&#xff0c;叶子节…

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法 2023/11/25 23:00 缘起&#xff0c;在拼多多买了2片10TB的7200rpm的日立二手硬盘。 型号&#xff1a;日立 mar-2018 10T硬盘 接上电脑&#xff0c;硬盘感觉在转动了【正常上电了。】 但是X99主板&#xff0c;在WIN10下就…

人工智能面面观

人工智能简介 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机能够模拟和执行人类智能任务的科学和技术领域。它致力于开发能够感知、理解、学习、推理、决策和与人类进行交互的智能系统。人工智能的背景可以追溯到上世纪50…

DeepWalk代码实战-维基百科词条图嵌入可视化

准备工作&#xff1a; 从爬虫网站中爬取维基百科See also关联词条&#xff1a;https://densitydesign.github.io/strumentalia-seealsology/ 维基百科网站&#xff1a;https://www.wikipedia.org/ 爬取过程&#xff1a; 下载 tsv 文件&#xff1a; import networkx as nx # 图…

详解#define

我们要知道&#xff0c;#define后面定义的标识符只进行替换而不进行计算&#xff0c;我们不能根据惯性自动给它计算了&#xff0c;这样可能会出错。 目录 1.关于#define 1.1#define定义标识符 1.2#define定义宏 1.3#define的替换规则 2.#和## 1.# 2.## 3.带副作用的宏参…

信息收集小练习

信息收集小练习 本文章无任何恶意攻击行为&#xff0c;演示内容都合规无攻击性 演示如何绕过cdn获取真实ip 使用多地ping该网站 发现有很多ip地址&#xff0c;证明有cdn 此处使用搜索引擎搜索&#xff0c;得到ip 演示nmap工具的常用参数 此处以testfire.net为例 使用多地p…

Android逆向实战 - MIUI调起三方应用系统拦截弹窗分析

近期&#xff0c;发现在部分Android手机调起其他应用时&#xff0c;会弹出一个系统弹窗拦截调起&#xff0c;需要用户二次确认。经过内部众测&#xff0c;发现绝大多数是小米手机&#xff0c;而且跟Android版本没有直接关系&#xff0c;猜测是MIUI某次升级引入的功能。这篇文章…

OpenCV入门教程

本文旨在让你快速入门opencv。 OpenCV OpenCV是计算机视觉中最受欢迎的库&#xff0c;最初由intel使用C和C 进行开发的&#xff0c;现在也可以在python中使用。该库是一个跨平台的开源库&#xff0c;是免费使用的。OpenCV库是一个高度优化的库&#xff0c;主要关注实时应用程…

Another app is currently holding the yum lock; waiting for it to exit...

今天使用yum进行下载的时候报错 解决办法&#xff1a; 执行 rm -f /var/run/yum.pid 然后重新运行yum指令即可&#xff0c;发现已经可以正常下载啦&#xff01;

VScode

一、VSCode设置中文 1、首先我们打开vscode&#xff0c;进入编辑页面后点击左边栏的图示图标进入“EXTENSIONS”面板 2、进入后&#xff0c;在上方搜索“Chinese”&#xff0c;搜索到中文&#xff08;简体&#xff09;后&#xff0c;点击“install”按钮。 3、等待自动下载安装…

STK Components 基础篇

1.开发包 STK Components 访问AGI官网&#xff0c;注册并登录后&#xff0c;从官网下载开发包&#xff1a;https://support.agi.com/downloads/&#xff0c;下载成功后可以申请许可证&#xff0c;AGI会向你注册的邮箱地址发送有效期半年的使用授权许可文件&#xff08;lic文件…

基于 STM32 的温度测量与控制系统设计

本文介绍了如何基于 STM32 微控制器设计一款温度测量与控制系统。首先&#xff0c;我们将简要介绍 STM32 微控制器的特点和能力。接下来&#xff0c;我们将详细讨论温度传感器的选择与接口。然后&#xff0c;我们将介绍如何使用 STM32 提供的开发工具和相关库来进行温度测量和控…

ubuntu20.04配置OpenCV的C++环境

ubuntu20.04配置OpenCV的C环境 这里以opencv-3.4.16为例 复现https://github.com/raulmur/ORB_SLAM2此项目&#xff0c;需安装opencv及其他依赖&#xff0c;可见README.md详情 1.下载opencv源代码 https://opencv.org/releases/ 2.下载OpenCV的扩展包opencv_contrib&#x…

排序算法-----基数排序

目录 前言 基数排序 算法思想 ​编辑 算法示例 代码实现 1.队列queue.h 头文件 2.队列queue.c 源文件 3.主函数&#xff08;radix_sort实现&#xff09; 算法分析 前言 今天我想把前面未更新完的排序算法补充一下&#xff0c;也就是基数排序的一种&#xff0c;这是跟…

小程序如何禁止指定用户访问?如何设置指定用户才能访问?

​有些商家为了价格保密或者实行严格的会员制等原因&#xff0c;希望小程序能够限制某些人的访问或者设置指定人员才能访问。这种功能在小程序中&#xff0c;怎么支持这些功能呢&#xff1f;下面具体介绍。 一、禁止指定用户访问 禁止指定用户访问&#xff0c;可以通过小程序…

【三极管锯齿波电路】2022-3-23

缘由以晶体管作恒流源的锯齿波电路工作原理? - 24小时必答区

【数据结构】树与二叉树(廿六):树删除指定结点及其子树(算法DS)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点4. 删除结点及其左右子树a. 逻辑删除与物理删除b. 算法DSTc. 算法解析d. 代码实现递归释放树算法DS e. 算法测试 5. 代码整合…

基于C#实现三元组

我们知道矩阵是一个非常强大的数据结构&#xff0c;在动态规划以及各种图论算法上都有广泛的应用&#xff0c;当然矩阵有着不足的地方就是空间和时间复杂度都维持在 N2 上&#xff0c;比如 1w 个数字建立一个矩阵&#xff0c;在内存中会占用 1w*1w1 亿的类型空间&#xff0c;这…

【人工智能】Chatgpt的训练原理

前言 前不久&#xff0c;在学习C语言的我写了一段三子棋的代码&#xff0c;但是与我对抗的电脑是没有任何思考的&#xff0c;你看了这段代码就理解为什么了&#xff1a; void computerMove(char Board[ROW][COL], int row, int col) {while (1){unsigned int i rand() % ROW, …

BC76 [NOIP2008]ISBN号码

#include<stdio.h> int main() {char arr[13]; //存放13位的ISBNint i, j;scanf("%s",arr);int s 0;for(i0, j1; i<11; i){if(arr[i] ! -){s (arr[i]-0)*j; //将字符换成int累加&#xff1a;0162……29158j; //执行if的时候加&#xff0c;不执行不加…