进程通信(IPC-Inter Process Communication)

进程之间的通信通过内核空间实现

IPC技术

管道(匿名管道/命名管道-FIFO队列) ②System V IPC(消息队列、信号量和共享内存)  ③套接字(UNIX套接字&Internet套接字)

※信号

软中断,信号提供了一种处理异步事件的方法,作为进程通信的一种机制,由一个进程发送给另一个进程。<signal.h>

信号的产生情况

①用户在终端按下一个组合键;

②硬件异常; //①②硬件问题

③进程调用 kill 函数发送信号;

④当检测到某软件产生异常时产生信号;      //③④软件问题

信号处理

阻塞信号/捕获信号/忽略信号/执行默认动作

【SIGKILL&SIGSTOP 信号是无法捕捉和忽略的】

查看信号 kill -l / trap -l;

信号操作的函数

(1) int kill (pid_t pid,int sig);     //向指定进程发送信号

头文件:<sys/types.h>   <sysnal.h>

pid : ①>0; 发送指定pid的进程 

②=0;信号发送给和目前进程在同一个进程组的所有进程

③=-1;广播到系统内所有进程 

④<0;  发送信号给PID为pid绝对值的进程

(2) int alarm(int second);    //定时器发送信号SIGALRM,默认处理是终止当前进程;

头文件:<unistd.h>

函数的返回值是0/设定闹钟还余下的秒数

(3) int raise(int sig);  //发送信号给当前的进程

头文件:<signal.h>

sig参数主要是信号参数

执行成功返回0,失败返回-1;

等价于 kill (getpid(),sig);

(4) void signal(*signal(it signum,void(*handler)(int)))(int);

头文件:#include <signal.h>

signal()会按照signum指定的信号编号来设置信号的处理函数。当指定的信号到达就会处理*handler指定的函数执行;若该函数不在,则需要是以下的两个常数之一:

SIG_IGN 忽略参数signum指定的信号

SIG_DFL 将参数signum指定的信号重设为 核心预设的信号处理方式 

(5) 信号集操作函数--#include <signal.h>

int sigemptyset(sigset_t *set);             //清空信号集 成功返回0 错误返回-1

int sigfillset(sigset_t *set);                  //初始化信号集  成功返回0 失败返回-1

int sigaddset(sigset_t *set,int signo);   //将signo信号加入到信号集set 成功返回0 失败返回-1

int sigdelset(sigset_t *set,int signo);       //将指定信号从信号集中添加或者删除

int sigismember(const sigset_t *set,int signo); 

                                          //判断指定信号是否包含在信号集中 (不)包含返回1(0) 失败返回-1

int sigprocmask(int how,const sigset_t *set,sigset_t *old) //查询/设置信号掩码

   //how参数:   --成功返回0,失败返回-1

 //SIG_BLOCK: 新的信号掩码由目前的信号掩码和set指定的信号掩码的并集

 //SIG_UNBLOCK:将目前的信号掩码删除set指定的信号掩码

 //SIG_SETMASK: 目前的信号掩码设置成set指定的信号掩码

信号发送例:设计一个程序,要求用户进程创建一个子进程,父进程向子进程发出SIGKILL信号,子进程收到此信号,结束子进程的运行;

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{pid_t sonpid;int ret;sonpid=fork();  //fork()函数执行一次,返回两次;在父进程中,fork返回新建进程ID;在子 //进程中fork返回0,错误返回负值;int newret;if(sonpid<0){perror("创建进程失败!");exit(1);  //异常结束}else if(sonpid==0){raise(SIGSTOP);   //如果是子进程,发送一个不能被阻塞、处理或阻塞的暂停信号;exit(0);  //正常结束}else{printf("子进程的进程号是%d\n",sonpid);if((waitpid(sonpid,NULL,WNOHANG))==0){if(ret=kill(sonpid,SIGKILL)==0){printf("用kill函数返回值是:%d,发出的SIGKILL信号结>束的进程进程号:%d\n",ret,sonpid);}else{perror("kill函数结束子进程失败");}}}
}

 信号处理例:要求程序运行后进入无限循环,当用户按下中断键(Ctrl+C)时,进入程序的自定义信号处理函数,当用户再次按下中断键(Ctrl+C)后,结束程序运行;

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
void fun_ctrl_c();    //自定义信号函数
int main()
{(void)signal(SIGINT,fun_ctrl_c);printf("主程序:主程序进入一个循环...\n");while(1){printf("这是一个无限的循环(退出请按‘Ctrl+C’)\n");sleep(3);}exit(0);
}void fun_ctrl_c()
{printf("\t你按了Ctrl+C!\n");printf("\t此例不处理,重新恢复SIGINT信号的系统默认处理\n");(void) signal(SIGINT,SIG_DFL);   //重新恢复SIGINT的系统默认处理
}

 

信号阻塞例1:

要求主程序运行时,即使按下Ctrl+C也不影响正在运行的程序,即让信号处于阻塞状态,当主体程序运行完毕后才进入自定义信号处理函数;

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
void fun_ctrl_c();//自定义信号函数
int main()
{int i;sigset_t set,pendset;  //定义了两个信号集struct sigaction action;(void) signal(SIGINT,fun_ctrl_c);if(sigemptyset(&set)<0)   //初始化set信号集{perror("初始化集合错误!\n");}if(sigaddset(&set,SIGINT)<0)   //将SIGINT信号加入到set信号集{perror("加入信号集错误\n");}if(sigprocmask(SIG_BLOCK,&set,NULL)<0)  //将当前的信号集合加入到当前进程的//阻塞集合中{perror("往信号阻塞集中增加一个信号集合错误");}else    //将当前信号集加入到阻塞集合中{for(int i=0;i<5;i++){printf("此文字表示程序在阻塞状态\n");sleep(2);}}if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)  //将当前的阻塞集中删除一个信号集{perror("从信号阻塞集删除一个信号集合错误");}
}
void fun_ctrl_c()  //自定义信号函数
{printf("\t你按了Ctrl+C但是系统未处理a... ");//要求中断键不影响当前程序运行printf("\t信号处理函数:要处理的东西在处理函数中编程!\n");printf("\t这个案例不处理,直接退出!\n");(void) signal(SIGINT,SIG_DFL);         //恢复默认SIGINT信号的系统默认处理
}

过程:①初始化set信号集; ②将SIGINT信号加入到set信号集;  ③将set信号集加入到阻塞集;

...   ④将set信号集从阻塞集删除 ->5次循环 程序结束 SIGINT执行默认系统处理 直接退出系统;

信号阻塞例2:

信号SIGINT(Ctrl+C)和SIGTSTP(Ctrl+Z)是可以阻塞的,信号SIGQUIT(Ctrl+\)是不可以阻塞;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void fun_ctrl_c();
void fun_ctrl_z();
void fun_ctrl_d();
int main()
{int i;sigset_t set,pendset;struct sigaction action;(void) signal (SIGINT,fun_ctrl_c);(void) signal (SIGTSTP,fun_ctrl_z);(void) signal (SIGQUIT,fun_ctrl_d);if(sigemptyset(&set)<0) //初始化set信号集{perror("初始化信号集错误\n");}if(sigaddset(&set,SIGTSTP)<0)  //将SIGTSTP信号加入到set{perror("Ctrl+Z加入信号集错误\n");}if(sigaddset(&set,SIGINT)<0)  //将SIGINT信号加入到set{perror("Ctrl+C加入信号集错误\n");}if(sigprocmask(SIG_BLOCK,&set,NULL)<0)   //信号集加入到当前进程的阻塞集合{perror("加入阻塞集合失败\n");}else{printf("加入到阻塞集合成功\n");for(i=0;i<10;i++){printf("Ctrl+C和Ctrl+Z信号处于阻塞,Ctrl+'\'信号未被阻塞\n");sleep(3);}}if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)  //将当前信号集从阻塞信号集中删除{perror("从阻塞信号集中删除当前信号集失败\n");}
}void fun_ctrl_c()  //自定义信号
{int n;printf("\t你已经按了Ctrl+C 系统未处理...");for(n=0;n<4;n++){printf("\t正在处理Ctrl+C信号处理函数");}
}
void fun_ctrl_z()  //自定义信号
{int n;printf("\t你已经按了Ctrl+Z 系统未处理...");for(n=0;n<6;n++){printf("\t正在处理Ctrl+Z信号处理函数");}
}
void fun_ctrl_d()
{int n ;printf("\t你已经按了Ctrl+'\' 系统处理了该信号!!\n");for(n=0;n<2;n++){printf("\t正在处理Ctrl+'\'信号处理函数");}
}

※管道

 无名管道pipe  &  FIFO管道(命名管道),都是通过内核缓冲区实现数据的传输;

pipe用于父进程和子进程之间的通信,通过pipe()系统调用创建并打开;

FIFO在磁盘上有对应的结点,但是有数据块,通过mknod()系统调用或mkfifo()函数来建立;一旦建立,任何进程都可以通过文件名将其打开进行读写;

管道实质是一个内核缓冲区,以先进先出的方式从缓冲区写读数据;

无名管道

建立管道用pipe函数,管道操作:

①父进程用pipe开辟管道,得到的两个文件描述符指向管道的两端;

②父进程用fork创建子进程,子进程也有两个文件描述符指向管道两端;、

③父(子)进程关闭读(写) 端,就可以进行写(读)操作;--读read函数 /  写write函数

(1)pipe函数--#include <unistd.h>

int pipe(int filedes[2]);

filedes[0]管道读取端;  filedes[1]管道写入端;  成功执行返回0,错误返回-1;

(2)memset函数--#include<string.h>

void *memset(void *s,int c,size_t n); 

s指向的内存区域内前n个字节以参数c填入,返回指向s的指针;c虽然声明是int,但是必须是unsigned char,范围在0-255; 

例:要求创建一个管道,复制进程【创建子进程】,父进程往管道中写入字符串,子进程从管道中读取前字符串;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main()
{pid_t result;  //子进程返回的进程号int r_num;int pipe_fd[2]; //两个文件描述符char buf_r[100],buf_w[100];     //读写字符数组memset(buf_r,0,sizeof(buf_r));   //初始化数组设置为0//if(pipe(pipe_fd)<0){perror("创建管道失败\n");return -1;}result = fork();//创建子进程,复制进程if(result<0){perror("创建子进程失败\n");exit(-1);}else if(result==0)  //子进程{close(pipe_fd[1]);  //关闭写if((r_num=read(pipe_fd[0],buf_r,100))>0)   //进行读{printf("子进程从管道中读取%d个字符,读取的字符内容是:%s\n",r_num,buf_r);}close(pipe_fd[0]);   //关闭读exit(0);//正常退出}else   //父进程{close(pipe_fd[0]);   //关闭读printf("请从键盘输入要写入管道的字符串\n");scanf("%s",buf_w);if(write(pipe_fd[1],buf_w,strlen(buf_w))!=-1)  //进行写{printf("父进程向管道写入:%s\n",buf_w);}close(pipe_fd[1]);     //关闭写waitpid(result,NULL,0);  //waitpid,阻塞父进程,等待子进程退出;exit(0);}
}

注意:空字符不读取;

命名管道

命名管道的名字对应磁盘的索引节点,用该文件名,任何进程都有相应的权限对其进行访问。

创建命名管道的方式:mkfifo()和mknode()函数

例:设计两个程,要求用命名管道FIFO实现简单的聊天功能。

高级管道设计

:设计一个程序,要求用popen创建管道,实现“ls -l|grep 7-9c”的功能;

//高级管道设计
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
int main()
{FILE *fp; //文件指针int num;char buf[5000];  //字符缓冲区memset(buf,0,sizeof(buf));   //将buf所指向的内存区域的前sizeof(buf)得到>的字节//设置为0,初始化清空的操作printf("建立管道...\n");fp=popen("ls -l","r");   //调用popen函数,建立读管道if(fp!=NULL){num=fread(buf,sizeof(char),5000,fp);/*if(num>0){printf("第一个命令是'ls-l',执行结果如下:\n");printf("%s\n",buf);}*/if(num<0){perror("读命令失败!\n");exit(-1);}pclose(fp);}else{printf("用popen创建管道失败!\n");return 1;}fp=popen("grep insert.c","w");  //建立写管道printf("第二个命令是grep insert.c,运行结果是:\n");fprintf(fp,"%s\n",buf);pclose(fp);return 0;
}

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

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

相关文章

LeetCode714买卖股票的最佳时机含手续费

题目描述 给定一个整数数组 prices&#xff0c;其中 prices[i]表示第 i 天的股票价格 &#xff1b;整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易&#xff0c;但是你每笔交易都需要付手续费。如果你已经购买了一个股票&#xff0c;在卖出它之前你就不能再继续购买…

责任链模式(行为型)

目录 一、前言 二、责任链模式 三、总结 一、前言 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;也叫职责链模式&#xff0c;是一种行为型设计模式&#xff0c;职责链模式使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦…

第二十一讲:编译和链接

第二十一讲&#xff1a;编译和链接 1.翻译环境和运行环境1.1翻译环境1.2编译1.2.1预编译&#xff08;预处理&#xff09;1.2.2编译1.2.2.1词法分析1.2.2.2语法分析1.2.2.3语义分析 1.2.3汇编 1.3链接1.4运行环境1.5#define符号1.5.1#define的使用和原理1.5.2#define使用的陷阱注…

Python学习从0开始——Kaggle机器学习004总结2

Python学习从0开始——Kaggle机器学习004总结2 一、缺失值二、分类变量2.1介绍2.2实现1.获取训练数据中所有分类变量的列表。2.比较每种方法方法1(删除分类变量)方法2(序数编码)方法3独热编码 三、管道3.1介绍3.2实现步骤1:定义预处理步骤步骤2:定义模型步骤3:创建和评估管道 四…

【JAVA】javadoc,如何生成标准的JAVA API文档

目录 1.什么是JAVA DOC 2.标签 3.命令 1.什么是JAVA DOC 当我们写完JAVA代码&#xff0c;别人要调用我们的代码的时候要是没有API文档是很痛苦的&#xff0c;只能跟进源码去一个个的看&#xff0c;一个个方法的猜&#xff0c;并且JAVA本来就不是一个重复造轮子的游戏&#…

探索LLM 在金融领域有哪些潜在应用——通过使用 GPT-4 测试金融工程、市场预测和风险管理等 11 项任务

概述 近年来&#xff0c;用于自然语言理解和生成的人工智能技术在自然语言处理领域取得了突破性进展&#xff0c;OpenAI 的 GPT 和其他大规模语言模型在该领域取得了显著进步。这些模型通过先进的计算能力和算法&#xff0c;展示了处理复杂任务的能力&#xff0c;如理解复杂语…

vue2组件封装实战系列之tag组件

作为本系列的第一篇文章&#xff0c;不会过于的繁杂&#xff0c;并且前期的组件都会是比较简单的基础组件&#xff01;但是不要忽视这些基础组件&#xff0c;因为纵观elementui、elementplus还是其他的流行组件库&#xff0c;组件库的封装都是套娃式的&#xff0c;很多复杂组件…

关于python中的关键字参数

在python语言中存在两种传参方式&#xff1a; 第一种是按照先后顺序来传参&#xff0c;这种传参风格&#xff0c;称为“位置参数”这是各个编程语言中最普遍的方式。 关键字传参~按照形参的名字来进行传参&#xff01; 如上图所示&#xff0c;在函数中使用关键字传参的最大作…

计算机网络 ——网络层(IPv4地址)

计算机网络 ——网络层&#xff08;IPv4地址&#xff09; 什么是IPv4地址IP地址的分类特殊的IP地址 查看自己的IPv4地址 我们今天来看IPv4地址&#xff1a; 什么是IPv4地址 IPv4&#xff08;Internet Protocol version 4&#xff09;是第四版互联网协议&#xff0c;是第一个被…

使用CodeGen进行程序综合推理

Program Synthesis with CodeGen — ROCm Blogs (amd.com) CodeGen是基于标准Transformer的自回归语言模型家族&#xff0c;用于程序合成&#xff0c;正如作者所定义的&#xff0c;它是一种利用输入-输出示例或自然语言描述生成解决指定问题的计算机程序的方法。 我们将测试的…

mqtt-emqx:paho.mqttv5的简单例子

# 安装emqx 请参考【https://blog.csdn.net/chenhz2284/article/details/139551293?spm1001.2014.3001.5502】 # 下面是示例代码 【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</…

spark-3.5.1+Hadoop 3.4.0+Hive4.0 分布式集群 安装配置

Hadoop安装参考: Hadoop 3.4.0HBase2.5.8ZooKeeper3.8.4Hive4.0Sqoop 分布式高可用集群部署安装 大数据系列二-CSDN博客 一 下载:Downloads | Apache Spark 1 下载Maven – Welcome to Apache Maven # maven安装及配置教程 wget https://dlcdn.apache.org/maven/maven-3/3.8…

mqtt-emqx:简单安装emqx

安装依赖 yum install -y epel-release libatomic下载 cd /chz/install/emqx wget https://www.emqx.com/en/downloads/broker/5.7.0/emqx-5.7.0-el7-amd64.tar.gz解压 mkdir -p emqx && tar -zxvf emqx-5.7.0-el7-amd64.tar.gz -C emqx后台运行 cd /chz/install/e…

分布式事务Seata中XA和AT模式介绍

Seata中XA和AT模式介绍 分布式事务介绍分布式解决方案解决分布式事务的思路Seata的架构Seata中的XA模式Seata的XA模型流程XA模式优缺点实现XA模式 Seata中的AT模式Seata中的AT模式流程实现AT模式AT模式优缺点 AT模式与XA模式的区别 分布式事务介绍 分布式事务&#xff0c;就是…

代码随想录算法训练营第36期DAY50

DAY50 如果写累了就去写套磁信吧。 198打家劫舍 class Solution {public: int rob(vector<int>& nums) { vector<int> dp(nums.size()); dp[0]nums[0]; if(nums.size()1) return nums[0]; dp[1]max(nums[0],nums[1]); …

【中颖】SH79F9202 串口通信

头文件 uart.h #ifndef UART_H #define UART_H#include "SH79F9202.h" #include "LCD.h" #include "timer2.h" #include "timer5.h" #include "cpu.h" #include "key.h" #include "io.h" #include &qu…

Meta Llama 3 RMSNorm(Root Mean Square Layer Normalization)

Meta Llama 3 RMSNorm&#xff08;Root Mean Square Layer Normalization&#xff09; flyfish 目录 Meta Llama 3 RMSNorm&#xff08;Root Mean Square Layer Normalization&#xff09;先看LayerNorm和BatchNorm举个例子计算 LayerNormRMSNorm 的整个计算过程实际代码实现结…

Linux内核epoll

Linux网络IO模型 同步和异步&#xff0c;阻塞和非阻塞 Linux下的五种IO模型 同步和异步&#xff0c;阻塞和非阻塞 Linux 下的五种I/O模型&#xff1a; 阻塞IO&#xff08;Blocking IO&#xff09; BIO 非阻塞IO&#xff08;No Blocking IO&#xff09; IO复用&#xff08;se…

手把手教你实现条纹结构光三维重建(1)——多频条纹生成

关于条纹结构光三维重建的多频相移、格雷码、格雷码相移、互补格雷码等等编码方法&#xff0c;我们在大多数平台上&#xff0c;包括现在使用语言大模型提问&#xff0c;都可以搜到相关的理论&#xff0c;本人重点是想教会你怎么快速用代码实现。 首先说下硬件要求&#xff0c;…

从0到1:企业办公审批小程序开发笔记

可行性分析 企业办公审批小程序&#xff0c;适合各大公司&#xff0c;企业&#xff0c;机关部门办公审批流程&#xff0c;适用于请假审批&#xff0c;报销审批&#xff0c;外出审批&#xff0c;合同审批&#xff0c;采购审批&#xff0c;入职审批&#xff0c;其他审批等规划化…