当前位置: 首页 > news >正文

华清远见25072班I/O学习day4

重点内容:

三、进程间通信(IPC)

3.1 进程间通信的引入

1> 多个进程之间能否使用全局变量来进行进程间通信?

答:不能,多个进程之间的用户空间是相互独立的,每个进程的全局变量是各个进程独立拥有的,更改一个进程的全局变量,另一个进程不会受到影响,故而不能使用全局变量来完成通信

2> 有同学说:使用文件来完成进程间通信?

答:可行,但是要求写进程先完成向文件中写入数据,然后读进程才能读取数据,必须要加入进程的同步操作

3> 多个进程之间内核空间是共享的,我们可以通过将数据放入到内核空间,来完成两个进程之间信息的交流

4> 进程间通信方式:

        1、内核提供的原始通信方式

                无名管道

                有名管道

                信号

        2、system V提供三种

                消息队列

                共享内存

                信号量集

        3、套接字通信(网络编程)

3.2 信号通信

1> 实现一个进程向另一个进程发送信号,能够发送的信号是固定,有62个,可以通过 man kill 查看

2> 通过函数 kill 将一个信号从一个进程中发送给另一个进程,但是,不能发送数据(只起到通知作用)

3> 注意:需要信号的发射者进程,直到要被发送信号进程的pid,一般发生在父子进程之间

4> 对于信号的接受进程而言,需要使用signal函数,将指定信号,绑定到指定操作上,后期一旦该进程收到相关信号,直接去执行相关函数

5> 相关信号:一般情况下,一个进程收到一个信号,默认的处理方式是杀死进程

6> 信号绑定函数(signal)

        sighandler_t signal(int signum, sighandler_t handler);

        功能:将信号号跟信号处理方式绑定到一起

        参数1:要处理的信号号

        参数2:处理方式,一共有三种处理方式

                SIG_IGN:表示忽略该信号

                SIG_DFL:执行该信号对应的默认处理方式

                用户自定义功能(捕获):参数为int类型,返回值为void类型的函数名

        返回值:成功返回处理方式的起始地址,失败返回SIG_ERR并置位错误码

        注意:SIGKILL和SIGSTOP信号既不能被捕获,也不能被忽略

7> 向其他进程发送信号的函数(kill)

        int kill(pid_t pid, int sig);

        功能:将指定的信号发送给指定的进程

        参数1:要接受信号的进程pid

        参数2:要被发送的信号

                >0: 该信号发送给指定的进程中

                =0:表示该信号会发送到当前进程所在的进程组中的任意一个进程中

                =-1:表示将该信号发送给除当前进程外的所有进程

                <-1:将该信号发送到一个进程组中的任意一个进程,给定的pid的绝对值进程所在的进程组

        返回值:成功返回0,失败返回-1并置位错误码

8> 以信号的方式回收僵尸进程

1、 当子进程退出时,会自动向其父进程发送一个SIGCHLD信号,告诉父进程自己已经死了

2、 但是父进程收到该信号后,默认的处理方式是忽略

3.3 消息队列

前提指令:有关IPC对象的指令

ipcs:可以查看消息队列、共享内存、信号量集的所有信息

ipcs -q: 只查看消息队列的所有信息

ipcs -m:只查看共享内存的所有信息

ipcs -s:只查看信号量集的所有信息

ipcs -r ID:表示要删除某个ICP对象

System V提供的进程间通信对象,是对立与进程而存在的

1>有关消息队列的函数 API

        key_t ftok(const char *pathname, int proj_id);

        功能:通过给定的文件或目录路径(必须存在)以及一个随机数据来生成一个key值 ftok("\", 'k')

        参数1:已经存在的文件路径,一般我们使用 "\"

        参数2:一个任意给定的随机数,只需要低8位即可

        返回值:成功返回ipc对象的key值,失败返回(key_t)-1并置位错误码

        int msgget(key_t key, int msgflg);

        功能:通过给定的key值创建出一个消息队列,并返回该消息队列的id,以后使用该id就可以操作该消息队列了

        参数1:key值,是用于创建消息队列的key值,该值可以是IPC_PRIVATE(仅仅用于亲缘进程间通信使用),非亲缘进程间通信需要使用 ftok 创建一个key值

        参数2:消息队列的创建模式: IPC_CREAT|IPC_EXCL|0664

                IPC_CREAT:打开一个消息队列,如果消息队列不存在,则创建一个消息队列                 IPC_EXCL:确保本次必须创建出一个新的消息队列,如果消息队列已经存在,则报错,错误码为EEXISTS

                还需要加上创建权限

        返回值:成功返回打开的消息队列的id号 以后使用该id就可以操作该消息队列了

        int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        功能:向msqid记录的消息队列中放入以msgp指针指向的消息,其消息正文大小为msgsz

        参数1:消息队列id号

        参数2:消息的起始地址,需要用户自定义一个结构体

                     结构体的通用格式为 struct msgbuf { long mtype; /* 消息类型 */ char mtext[1024]; /* 消息正文 */ };

        参数3:消息正文的大小

        参数4:是否阻塞发送

                0:表示阻塞形式发送

                IPC_NOWAIT:表示非阻塞形式发送

        返回值: 成功返回0,失败返回-1并置位错误码

        ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

        功能:从msqid消息队列中取出消息,并放到msgp指向的容器中,取出的正文大小为msgsz,取出消息的类型为msgtyp

        参数1:消息队列id号

        参数2:消息的起始地址,需要用户自定义一个结构体

                     结构体的通用格式为 struct msgbuf { long mtype; /* 消息类型 */ char mtext[1]; /* 消息正文 */ };

        参数3:消息正文的大小

        参数4:取出消息的类型

                =0:表示无关类型,直接取第一个放入消息队列中的消息

                >0:表示取出给定类型中的第一个存放到消息队列中的消息

                <0: 表示取得1--该数的绝对值类型中的第一个存放到消息队中的消息

        例如 -5 表示从类型为1---5之间存放进入的第一个消息

        参数5:是否阻塞发送

                0:表示阻塞形式发送

                IPC_NOWAIT:表示非阻塞形式发送


作业:

1>使用消息队列实现两个程序的相互通信

原理图:

程序源码:(s.out)

#include <25072head.h>
//自定义消息类型的结构体
struct msgbuf
{
long mtype;           //消息类型
char mtext[1024];       //消息正文
};

//宏定义一个消息正文大小
#define  SIZE (sizeof(struct msgbuf)-sizeof(long))
int main(int argc, const char *argv[])
{
pid_t pid=-1;
pid=fork();
if(pid>0)
{
//1.创建一个key值
key_t key=ftok("/",'k');
if(key==-1)
{
perror("ftok error\n");
return -1;
}
//2.使用创建出来的key值创建一个消息队列
int msqid=msgget(key,IPC_CREAT|0664);
if(msqid==-1)
{
perror("msgget error");
return -1;
}
//3.不断在消息队列中放入消息
struct msgbuf buf;
while(1)
{
printf("请输入消息类型:");
scanf("%ld",&buf.mtype);
getchar();
printf("请输入消息正文:");
fgets(buf.mtext,SIZE,stdin);
buf.mtext[strlen(buf.mtext)-1]=0;
//表示退出不再往里面放数据
if(strcmp(buf.mtext,"quit")==0)
{
break;
}
//将消息放入到消息队列中
msgsnd(msqid,&buf,SIZE,0);
}
}
else if(pid==0)
{
//1.创建一个key值
key_t key1=ftok("/",'k');
if(key1==-1)
{
perror("ftok error\n");
return -1;
}

        int msqid1=msgget(key1,IPC_CREAT|0664);
if(msqid1==-1)
{
perror("msgget error");
return -1;
}
struct msgbuf buf1;
while(1)
{
msgrcv(msqid1,&buf1,SIZE,2,0);
printf("取出的消息为:%s\n",buf1.mtext);
}
}
return 0;
}

程序源码:(r.out)

#include<25072head.h>
//自定义消息类型的结构体
struct msgbuf
{
long mtype;           //消息类型
char mtext[1024];       //消息正文
};

//宏定义一个消息正文大小
#define  SIZE (sizeof(struct msgbuf)-sizeof(long))
/******************8主程序********************/
int main(int argc, const char *argv[])
{
pid_t pid=-1;
pid=fork();
if(pid>0)
{
//1.创建一个key值
key_t key = ftok("/", 'k');
if(key == -1)
{
perror("ftok error\n");
return -1;
}

        //2.使用创建出来的key值创建一个消息队列
int msqid = msgget(key, IPC_CREAT|0664);
if(msqid == -1)
{
perror("msgget error");
return -1;
}

        //3.不断从消息队列中取出消息
struct msgbuf buf;
while(1)
{
//从消息队列中取出消息
msgrcv(msqid, &buf, SIZE, 1, 0);
printf("取出的消息为:%s\n", buf.mtext);
}
}
else if(pid==0)
{
//1.创建一个key值
key_t key1 = ftok("/", 'k');
if(key1 == -1)
{
perror("ftok error\n");
return -1;
}

        int msqid1 = msgget(key1, IPC_CREAT|0664);
if(msqid1 == -1)
{
perror("msgget error");
return -1;
}
struct msgbuf buf1;
while(1)
{
printf("请输入消息类型:");
scanf("%ld", &buf1.mtype);
getchar();
printf("请输入消息正文:");
fgets(buf1.mtext, SIZE, stdin);  //从终端读取
buf1.mtext[strlen(buf1.mtext)-1] = 0;

            //表示退出不再往里面放数据
if(strcmp(buf1.mtext,"quit")==0)
{
break;
}
//将消息放入到消息队列中
msgsnd(msqid1, &buf1, SIZE, 0);
}
}
return 0;
}

2> 使用信号实现非阻塞方式回收僵尸进程练习

程序源码:

#include<25072head.h>
//自定义信号处理函数
void handler(int signum)
{
//判断信号
if(signum == SIGCHLD)
{
//直到一定没有僵尸进程时,结束循环
while(waitpid(-1, NULL, WNOHANG)>0);
}
}
int main(int argc, const char *argv[])
{
//将子进程退出时的信号SIGCHLD绑定到自定义函数中
if(signal(SIGCHLD, handler) == SIG_ERR)
{
perror("signal error");
return -1;
}
for(int i=0; i<10; i++)
{
if(fork() == 0)
{
exit(EXIT_SUCCESS);
}
}
while(1)
{
if(fork() == 0)
{
exit(EXIT_SUCCESS);
}
}
return 0;
}

3> 思维导图

4> 牛客网

http://www.xdnf.cn/news/1449829.html

相关文章:

  • 从端口耗尽危机到性能提升:一次数据库连接问题的深度剖析与解决
  • Ansible 核心功能:循环、过滤器、判断与错误处理全解析
  • Java学习笔记一(数据类型,运算符,流程控制)
  • GitLens:VS Code下高效解决代码追溯的Git管理实用插件
  • 【串口过滤工具】串口调试助手LTSerialTool v3.12.0发布
  • Redis 发布订阅模式详解:实现高效实时消息通信
  • TDengine TIMEDIFF() 函数用户使用手册
  • 66认知诊断模型发展与NeuralCD框架笔记
  • FPGA笔试面试常考问题及答案汇总
  • 无穿戴动捕如何深度结合AI数据分析,实现精准动作评估?
  • DOM常见的操作有哪些?
  • 还在 @AfterEach 里手动 deleteAll()?你早就该试试这个测试数据清理 Starter 了
  • leetcode110. 平衡二叉树
  • mysql常见面试题
  • [光学原理与应用-376]:ZEMAX - 优化 - 概述
  • 代码随想录算法训练营第四天|链表part02
  • SQLint3 模块如何使用
  • PostgreSQL 技术峰会哈尔滨站活动回顾|深度参与 IvorySQL 开源社区建设的实践与思考
  • 农业XR数字融合工作站,赋能农业专业实践学习
  • 刻意练习实践说明使用手册
  • 正则表达式的使用
  • Java jar 如何防止被反编译?代码写的太烂,害怕被人发现
  • TDD测试驱动开发+Python案例解析
  • 在linux下使用MySQL常用的命令集合
  • 通义实验室发布AgentScope 1.0新一代智能体开发框架
  • 嵌入式第四十二天(数据库,网页设计)
  • Spring Boot集成Kafka常见业务场景最佳实践实战指南
  • Java全栈工程师的面试实战:从基础到复杂问题的完整解析
  • 安卓APP备案的三要素包名,公钥,签名md5值详细获取方法-优雅草卓伊凡
  • 鹧鸪云软件:光伏施工管理一目了然,进度尽在掌握