Linux进程间通信学习2

文章目录

  • 共享内存
  • 信号
    • 信号概述以及种类
    • 信号的处理
    • 信号相关函数(简单)
    • 运用小demo
      • 实现ctrl+c无法终止进程
      • 使用kill函数在程序内部实现一个进程杀死另外一个进程
    • 信号相关函数高级版
    • 运用函数小demo
  • 信号量
    • 信号量相关函数
    • 运用小demo:

共享内存

相比于前三个IPC方式,共享内存有什么不同?
我们可以假设两个人要进行交流,管道和FIFO就好像两人中间有一个水管,一方往里面放,另一方就只能拿;消息队列就好像一个人往箱子里面放纸条,另一个人从箱子里拿出纸条,读取完后再把纸条放回去(消息读完后不会消失,不同于管道);而共享内存就像两人中间有一张桌子,一个人往桌子上写东西,另一个人可以直接看到它写的(桌子对于两个人来说是共用的)。

由名字可知,两个进程可以挂载同一个内存空间,这个内存空间是共享的。
相关函数:

#include <sys/shm.h>
//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);//连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);//断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr);//控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

这个参数Key之间我是直接初始化为一个32位的整数,现在我们用一下其他方法,顺便学习一下一个函数——ftok函数。

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

参数:
1.函数的第一个参数是一个路径名,通常是一个存在的文件路径,待会的代码示例中会传入".",这意味着 ftok 函数会使用当前进程的工作目录(即程序运行时所在的目录)作为路径来生成键值,我们只需要知道怎么使用就行了。
2.第二个参数proj_id,它用于进一步区分同一路径下不同对象(如消息队列、信号量、共享内存)的键值。在使用ftok函数时,传入的proj_id值应当是一个非零的整数。简单理解就是这个数字可以被视为一个简单的标识符,用于区分同一路径下不同的对象。
运用函数小demo:
write:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main()
{int shmid;char *shmaddr;key_t key;key=ftok(".",1);//获取键值shmid=shmget(key,1024*4,IPC_CREAT|0666);//创建一个共享内存,权限为可读可写,大小为4兆if(shmid==-1)//创建/获取共享内存失败{printf("shmget failed\n");exit(-1);}	shmaddr=shmat(shmid,0,0);//挂载共享内存,获取地址strcpy(shmaddr,"hello!\n");//将字符串复制到共享内存里sleep(5);/眠五秒shmdt(shmaddr);//取消挂载/卸载共享内存shmctl(shmid,IPC_RMID,0);//删除共享内存return 0;
}

注意:
1.创建共享内存时,空间大小必须以兆为单位,即1024字节,shmget函数的第二个参数一般传入IPC_CREAT(创建),还需要|上创建的权限(0666表示可读可写,0777表示可读可写可执行)。
2.挂载共享内存shmat的第二和第三个参数通常写0即可,第二个参数写0表示让Linux内核为我们自动安排共享内存,第三个表示挂载/映射的共享内存为可读可写。
3.删除共享内存的第三个参数通常写0,表示不接收删除共享内存的信息等。

read:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main()
{int shmid;char *shmaddr;key_t key;key=ftok(".",1);//获取键值shmid=shmget(key,1024*4,IPC_CREAT|0666);//创建一个共享内存,权限为可读可写,大小为4兆if(shmid==-1)//创建/获取共享内存失败{printf("shmget failed\n");exit(-1);}	shmaddr=shmat(shmid,0,0);//挂载共享内存,获取地址strcpy(shmaddr,"hello!\n");//将字符串复制到共享内存里sleep(5);/眠五秒shmdt(shmaddr);//取消挂载/卸载共享内存shmctl(shmid,IPC_RMID,0);//删除共享内存return 0;
}

运行结果:
在这里插入图片描述
容易误解事项:
将write函数改写为这样后,就是将sleep函数改迟一点点:
在这里插入图片描述
运行结果还是一样的,只要不删除;另外一个进程就还可以读出共享内容的信息,而不是一定要两个进程一起挂起才可以!


共享内存补充
在终端输入指令 ipcs -m 来查看系统中有哪些共享内存

输入 ipcrm -m shmid shmid为共享内存的ID,用于删除共享内存

共享内存有个小缺陷,就是两个人不能同时写,不然数据会混在一起,所以共享内存一般都结合信号量来使用,一个人写的时候另一个人只能看。
————————————————————————————————————

信号

信号概述以及种类

1.信号的名字和编号
每个信号都有一个名字和编号,这些名字都以“SIG"开头,例如"SIGIO","SIGCHLD"等等。

信号定义在 signal.h 头文件中,信号名都定义为正整数。

具体的信号名称可以使用 kill -l 来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kil对于信号0有特殊的应用。
在这里插入图片描述
(使用 kill -l 指令查看信号的名字以及序号)

假设我们写了一个无限循环的程序,想要让它停止运行,我们会按 Ctrl+C 键来终止进程,其实就是向进程发送了第2个信号SIGINT。

还有一种杀死进程的方式,另外打开一个终端,输入 ps -aux |grep 可执行文件名 来查找正在运行的进程ID,再输入 kill -9 进程ID 即可杀死进程,并且在终端打印Killed,kill是发送信号的指令,-9表示发送第9个信号,也就是发送SIGKILL信号给对应ID的进程。以上说的这些指令都需要我们掌握,因为后面的代码示例都是根据这些指令来写的。
运用一下:
在这里插入图片描述

信号的处理

信号的处理有三种方法,分别是:忽略捕捉默认动作

·忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是== SIGKILL ==和 ==SIGSTOP ==)。因为他们向内核和超级用户提供了进程终止和停止的可靠方法,如果忽略了,那么这个进程就变成了没人能管理的的进程,显然是内核设计者不希望看到的场景。

·捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。

·系统默认动作,对于每个信号来说,系统都对应有默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。

信号相关函数(简单)

信息处理函数的注册:

#include <signal.h>//它定义了一个名为sighandler_t的新类型(typedef),该类型是一个指向函数的指针,这个函数接受一个整型参数 int,并且没有返回值(void)
typedef void (*sighandler_t)(int)sighandler_t signal(int signum, sighandler_t handler);
//handler就是这个新定义的类型

参数:
signum:就是上面说的用kill -l查看的信号的名字
handler:我们要传入的函数
**
————————————————————————————————————**
信号处理发送函数:

#include <signal.h>
#include <sys/types.h>int kill(pid_t pid, int siq);//第一个参数传入进程ID,第二个参数传入信号的编号

运用小demo

实现ctrl+c无法终止进程

平时我们想要认为的去终止进程,我们都是直接CTRL+C就可以了,假设我们写了一个无限循环的程序,想要让它停止运行,我们会按 Ctrl+C 键来终止进程,其实就是向进程发送了第2个信号SIGINT。
代码:

#include <stdio.h>
#include <signal.h>void handler(int signum)//自定义函数,参数为整形,无返回值
{printf("get signum=%d\n",signum);//打印信号编码printf("never quit\n");
}int main()
{signal(SIGINT,handler);//检测SIGINT信号(Ctrl+C),检测到了进入handler函数while(1);return 0;
}

本来信号的处理就是系统默认动作,现在相当于我将它改写了,就是捕获这个信号SIGINT,一旦捕获到就执行我们的函数handler,这里就是简单打印了,就没有让进程退出来。

使用kill函数在程序内部实现一个进程杀死另外一个进程

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>int main(int argc, char **argv)
{int signum;int pid;signum = atoi(argv[1]);//将字符串转换为整数类型pid = atoi(argv[2]);kill(pid,signum);printf("send signal ok\n");return 0;
}

运行结果:
在这里插入图片描述
就相当于前面用的kill -9 +pid

信号相关函数高级版

信息处理函数的注册:

#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数:
1.信号编号,要接收哪个信号?
2.是一个指向 struct sigaction 类型的结构体指针,结构体原型为:

struct sigaction {void (*sa_handler)(int);           // 指定信号处理函数void (*sa_sigaction)(int, siginfo_t *, void *); // 详细信号处理函数sigset_t sa_mask;                  // 额外的要阻塞的信号集合int sa_flags;                      // 特殊标志void (*sa_restorer)(void);         // 未使用,以备将来扩展
};

!!!通常配置结构体的第二个和第四个参数即可,第四个参数设置成 SA_SIGINFO ,这表示在使用sigaction函数设置信号处理时,希望使用详细的信号处理函数 sa_sigaction 而不是简单的处理函数 sa_handler ,因为我们只设置结构体的第二个和第四个参数,所以自然选择这个配套在一起,第二个参数传入自定义的函数名即可。
3.传入NULL即可

第二个参数我们慢慢刨开来看,第二个参数是一个结构体指针,里面我们一般配置第2,4个即可,第4个已经说了,那么我们可以看出来第2个这个函数原型为:

void handler(int signum, siginfo_t *info, void *context);

参数:
1.信号的编号,由操作系统传入。
2.siginfo_t是一个结构体,它定义在 <signal.h> 头文件中,这个结构体包含了关于信号更详细的信息,使得信号处理函数能够获取有关信号发生背景的更多信息。
在这里插入图片描述
可以看到siginfo结构体包含了很多参数,代码示例我们只需要接受这两个参数,它会将接受到的整型数放在这里,而== si_value== 又是一个结构体,整型数据会存放在 si_value.sival_int 里面。
3.第三个参数用来判断空或者非空(有无收到数据)
**
————————————————————————————————————**
信号处理发送函数:

#include <signal.h>int sigqueue(pid_t pid, int siq, const union sigval value);

参数:
1.传入要接收信号的进程的进程ID
2.要发送的信号的编号
3.第三个参数是个联合体(二选一),原型如下:

union sigval {int sival_int;      // 整数值作为附加数据void *sival_ptr;    // 指针作为附加数据
};

之所以叫高级版,就是可以实现进程间收发数据,入门版的函数更像是发送某些指令,而高级版可以发送信号的同时发送数据,要么发送整形数,要么发送字符串等。
**
————————————————————————————————————**

运用函数小demo

就简单一个写读数据即可
read:

#include <stdio.h>
#include <signal.h>void handler(int signum, siginfo_t *info, void *context)//用户自定义函数
{printf("get signum %d\n",signum);//打印信号编号if(context != NULL)//如果内容非空{printf("get data = %d\n",info->si_int);//打印收到的数据printf("get data = %d\n",info->si_value.sival_int);//打印收到的数据}
}int main()
{struct sigaction act;//定义struct sigaction类型的结构体/** 只需要配置结构体的两个参数,实现最基础的功能*/act.sa_sigaction = handler;//传入自定义函数名act.sa_flags = SA_SIGINFO;sigaction(SIGUSR1,&act,NULL);//检测SIGUSR1信号,参数二传入结构体指针,参数三通常写NULLprintf("%d\n",getpid());//打印一下进程的ID,方便write函数发送信号和数据while(1);//不让程序退出return 0;
}

write:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>int main(int argc, char **argv)
{int signum;pid_t pid;/** 将main函数传入的字符串转换为整形数*/signum = atoi(argv[1]);pid = atoi(argv[2]);union sigval value;//定义联合体value.sival_int = 100;//发送整形数100sigqueue(pid,signum,value);//第三个参数直接传入value即可return 0;
}

运行结果:
在这里插入图片描述
左边先运行read函数,此时会打印进程的ID号,右边再运行write函数,第一个参数传入信号的编号,SIGUSR1的编号是10,再传入进程ID,这时右边就会打印到收到的数据和信号编号,因为发送的是整型数,所以他会存在两个地方(见proread.c用户自定义函数部分)
————————————————————————————————————

信号量

这个Linux的信号量和FreeRTOS的信号量差不多的,只不过是函数的不同罢了!
可以看一下这个FreeRTOS的信号量一起理解一下:FreeRTOS信号量

信号量相关函数

#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>//创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int nsems, int semflg);//对信号量数组进行操作,改变信号量的值:成功返回0,失败返回-1
int semop(int semid, struct sembuf *sops, size_t nsops);//控制信号量的相关信息
int semctl(int semid, int semnum, int cmd, ...);

参数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运用小demo:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>      
#include <sys/sem.h>
#include <unistd.h>union semun//联合体的原型
{int val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO */
};void p(int semid)//拿钥匙
{struct sembuf set;//定义结构体set.sem_num = 0;set.sem_op = -1;//拿出去-1set.sem_flg = SEM_UNDO;semop(semid,&set,1);printf("get key\n");
}void v(int semid)//放钥匙
{struct sembuf set;set.sem_num = 0;set.sem_op = 1;//放进去+1set.sem_flg = SEM_UNDO;semop(semid,&set,1);printf("put back key\n");
}int main(int argc, char **argv)
{key_t key;key = ftok(".",2);int semid;semid = semget(key,1,IPC_CREAT|0666);union semun initsem;initsem.val = 0;//设置联合体的val为0,就是盒子里面没有信号量,需要“放钥匙”semctl(semid,0,SETVAL,initsem);//传入定义的联合体变量int pid=fork();if(pid>0)//父进程{/*如果是父进程先运行,里面初始化是没有信号量的,会阻塞在这里*/p(semid);//拿钥匙(刚开始没有,需要别人放进去才可以拿,没有拿不了)printf("father\n");v(semid);//放钥匙}else if(pid == 0)//子进程{printf("child\n");v(semid);//放钥匙(给父进程有的拿)}return 0;
}

运行结果:
在这里插入图片描述

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

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

相关文章

快速排序(下)

快速排序&#xff08;下&#xff09; 前言 在上一篇文章中我们了解了快速排序算法&#xff0c;但那是Hoare的版本&#xff0c;其实还有别的版本&#xff1a;一种是挖坑法&#xff0c;它们的区别主要在于如何找基准值。霍尔的版本思路难理解但代码好理解&#xff0c;挖坑法则是…

【Qt开发】调试log日志QDebug重定向输出到textEdit等控件(qInstallMessageHandler回调函数)

【Qt开发】调试log日志QDebug重定向输出到textEdit等控件&#xff08;qInstallMessageHandler回调函数&#xff09; 文章目录 Log输出方式qInstallMessageHandler回调函数线程安全textEdit控件附录&#xff1a;C语言到C的入门知识点&#xff08;主要适用于C语言精通到Qt的C开发…

MySQL:数据类型表的基础操作

目录 1、数据类型 1.1 数值类型 1.2 字符串类型 1.3 日期类型 2、表的基础操作 2.1 选择数据库 2.2 建表 2.3 查看库中所有表 2.4 查看某一表结构 2.5 删表 3、可视化编辑工具 3.1 运行 1、数据类型 1.1 数值类型 bit类型可指定长度&#xff08;如果不写&#xff0c;…

实验八: 彩色图像处理

目录 一、实验目的 二、实验原理 1. 常见彩色图像格式 2. 伪彩色图像 3. 彩色图像滤波 三、实验内容 四、源程序和结果 (1) 主程序(matlab (2) 函数FalseRgbTransf (3) 函数hsi2rgb (4) 函数rgb2hsi (5) 函数GrayscaleFilter (6) 函数RgbFilter 五、结果分析 1. …

清华和字节联合推出的视频理解大模型video-SALMONN(ICML 2024)

video-SALMONN: Speech-Enhanced Audio-Visual Large Language Models 论文信息 paper&#xff1a;https://arxiv.org/abs/2406.15704 code&#xff1a;https://github.com/bytedance/SALMONN/ AI也会「刷抖音」&#xff01;清华领衔发布短视频全模态理解新模型 | ICML 2024 …

基于单片机的防火防盗报警系统设计

摘要&#xff1a; 该多功能防火防盗系统既具有根据环境温度和烟雾浓度进行火灾检测的功能&#xff0c;也有能对人体检测实现防盗的功能。多功能智能防火防盗控制系统的主控制器是 STC89C52 单片机&#xff0c;环境温度的检测采用 DS18B20 &#xff0c; MQ2 检测烟雾浓度&…

[Meachines] [Easy] Mirai Raspberry树莓派默认用户登录+USB挂载文件读取

信息收集 IP AddressOpening Ports10.10.10.48TCP:22,53,80,1276,32400,32469 $ nmap -p- 10.10.10.48 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.7p1 Debian 5deb8u3 (protocol 2.0) | ssh-hostkey: | 1024 aa:ef:5c:…

@change事件传参

change事件传参 change"(value)>handleChange(value, item,index)" 这样可以接收index参数区分是哪一个组件事件&#xff0c;又可以接收子组件传的value值 <div class"boxItem" v-for"(item, index) in checkPeopleList" :key"inde…

【avue+vue2+elementui】删除、rules、页面跳转、列表数据过长、日期dayjs

这里写目录标题 一、删除二、rules三、页面跳转四、列表数据过长截断五、日期 dayjs一、删除 🍃API/*** 删除.* @param {*} data * @returns 返参*/ export const deleteOrder = (data) => {return request({url: /api/Order/deleteOrder,method: post,data}) }HTML🍃左…

5.2-软件工程基础知识-软件过程模型

软件过程模型 瀑布模型瀑布模型变种-V模型演化模型-原型模型增量模型演化模型-螺旋模型喷泉模型基于构件的开发模型形式化方法模型统一过程模型敏捷方法极限编程其他方法 软件过程模型概述练习题 瀑布模型 瀑布模型(SDLC):瀑布模型是一个经典的生命周期模型&#xff0c;一般将软…

声音和数据之间的调制解调 —— 电报机和电传打字机如何影响计算机的演变

注&#xff1a;机翻&#xff0c;未校对。 The Squeal of Data The through line between the telegraph and the computer is more direct than you might realize. Its influence can be seen in common technologies, like the modem. 电报和计算机之间的直通线比你想象的要…

Redis RDB AOF持久化 主从集群同步原理

RDB RDB Redis数据备份文件 也被叫做Redis数据快照 简单来说就是 把内存中的所有数据记录到磁盘中 当Redis实例故障实例重启后从磁盘读取快照文件恢复数据 快照文件称为RDB文件 默认时保存在当前运行目录执行时机 执行save命令 127.0.0.1:6379> save OK 127.0.0.1:6379&g…

opencascade AIS_TrihedronOwner源码学习对象的实体所有者用于选择管理

opencascade AIS_TrihedronOwner 前言 AIS_Trihedron对象的实体所有者用于选择管理。 在OpenCascade的AIS&#xff08;交互对象框架&#xff09;中&#xff0c;管理类似AIS_Trihedron的对象的选择涉及理解如何处理实体&#xff08;或所有者&#xff09;以进行选择。 方法 1…

正则表达式 空格匹配

目录 一. 前提二. 半角空格 匹配半角空格三. ^ 匹配半角空格开头的半角空格四. ^ $ 匹配整行都是半角空格五. ^[ \t]$ 匹配整行都是半角或Tab空格六. \s 匹配所有空格七. [^\s]匹配除了空格之外的所有内容 一. 前提 &#x1f447;&#x1f447;&#x1f447;有如下所示的内容…

程序员面试 “八股文”在实际工作中是助力、阻力还是空谈?

“八股文”在实际工作中是助力、阻力还是空谈&#xff1f; 作为现在各类大中小企业面试程序员时的必问内容&#xff0c;“八股文”似乎是很重要的存在。但“八股文”是否能在实际工作中发挥它“敲门砖”应有的作用呢&#xff1f;有IT人士不禁发出疑问&#xff1a;程序员面试考…

CTF web bibibi题型

CTF web bibibi题型 1.进入网站 在kali中使用Dirsearch对地址进行目录扫描&#xff0c;发现robots.txt 网址内加入 /robots.txt 进入网址 /fl4gi5Here.php 找到flag

Uni-APP页面跳转问题(十六)

【背景】最近在做公司一个PAD端,谁被点检功能,主要时为了移动端点检设备和打印标签,需求比较简单就是扫描设备二维码,问题在于扫描后要能够重复进行多设备的扫描;早期开发的设备点检能够满足需求但是当连续扫描五六十个设备后,APP卡死,必须重启才能使用。 界面原图: 输…

安全基础学习-keil调试汇编代码

初始目的是为了通过汇编编写CRC功能。 但是基础为0,所以目前从搭建工程开始记录。 大佬绕路。 (一)创建项目 1. 新建项目 打开 Keil uVision。选择 Project -> New uVision Project 创建一个新项目。选择你的目标设备(如 ARM Cortex-M 系列处理器),我这里一开始选择…

buu做题(12)

[CISCN 2019 初赛]Love Math <?php error_reporting(0); //听说你很喜欢数学&#xff0c;不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];if (strlen($content) > 80) {die("太长了不会算");…

Creomagic 推出认知通信功能以应对电子战 (EW) 威胁

新时代的软件定义无线电 (SDR) 技术可以在电子战和竞争频谱环境中自主维护可靠的网络。 最近的全球冲突凸显了现代战场上战术通信面临的严峻挑战。随着自主部队的日益普及&#xff0c;战场感知变得比以往任何时候都更加先进&#xff0c;需要大量信息传输和同步。在战场上传输关…