一、学习内容
进程之间的通信(nterprocess communication)
#### 信号通信
1.
>
> ##### 概念
>
> 1\> 信号通信中,多个进程只起到通知作用,没有数据传输的功能
>
> 2\> 所谓信号通信,就是软件模拟的硬件的中断请求
>
> 3\>原理图
>
> ![](https://i-blog.csdnimg.cn/direct/e0fe697140d3401db8c26a49ca91fa53.png)
>
2.
##### 信号处理方式
1.
###### 默认(SIG_DFL)
停下正在执行的任务,去执行信号功能的任务,大多数默认处理是杀死进程
2.
###### 捕获
需要给定一个函数名,表示停下正在执行的任务,去执行给定的函数
3.
###### 忽略(SIG_IGN)
忽略当前信号,继续执行自己的任务
3.
##### 收到信号类型
1.
###### 内核发送过来的信号
如管道破裂时,内核向用户空间发射SIGPIPE信号,指针操作失误时内核向用户空间发射SIGSEGV信号
2.
###### 用户发送过来的信号
如用户键入ctrl+c,表示终端向该进程发送SIGINT信号;用户键入ctrl+z,表示终端向进程发送SIGSTOP信号;
3.
###### 一个进程向另一个进程发送信号
需要使用相关函数完成,kill 函数,表示向某个进程发送信号
4.
##### signal(信号绑定函数)
typedef void (\*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
1.
###### 返回值
成功返回处理方式,失败返回SIG_ERR( void \* -1 ),并置位错误码
2.
###### 参数
> 参数1:要处理的信号号
>
> 参数2:信号处理方式一共有三种
>
> SIG_IGN:表示忽略该信号
>
> SIG_DFL:表示默认处理该信号
>
> 自定义函数名:返回值为void,参数为int
3.
###### 功能
将发送给当前进程的信号与信号处理方式进行绑定
5.
##### alarm(闹钟)
unsigned int alarm(unsigned int seconds);
1.
###### 返回值
如果之前没有启动定时器,那么该函数返回0,如果之前启动了定时器,那么该函数返回上一个定时器剩余的秒数,并更新该定时器时间
2.
###### 参数
要延迟的秒数,如果为0,表示取消该定时器
3.
###### 功能
启动一个定时器,延迟seconds秒后,向该进程发送SIGALRM信号
6.
##### kill(发信号)
int kill(pid_t pid, int sig);
1.
###### 返回值
成功返回0,失败返回-1并置位错误码
2.
###### 参数
> 参数1:进程ID号
>
> \>0:表示向指定进程发送信号
>
> =0:表示向当前进程所在的进程组发送信号
>
> =-1:表示向所有进程发送信号
>
> \<-1:表示向进程号为pid的绝对值那个进程组发送信号
>
> 参数2:要发送的信号号
3.
###### 功能
向指定的进程或进程组发信号
7.
##### raise(发信号)
int raise(int sig);
1.
###### 返回值
成功返回0,失败返回非0错误码
2.
###### 参数
要发送的信号号
3.
###### 功能
向当前进程发送信号(自己给自己发信号 kill(getpid(), sig))
#### System V提供的IPC通信方式
1.
##### 概念
system V提供的通信方式,是将通信容器独立于可执行程序而存在。即使相关程序已经退出,写入容器中的数据如果取出,依然存在
2.
##### 通信方式
> 消息队列
>
> 共享内存
>
> 信号量集
>
> 注意:
>
> 使用该通信方式时,需要先创建一个 IPC 对象,后续只需对该对象进行操作,就可以实现进程间通信
3.
##### 有关System V 提供的IPC对象操作的指令
> 1、ipcs:可以查看当前所有的消息队列、共享内存、信号量集的属性
>
> 2、ipcs -q:只查看消息队列的相关信息
>
> 3、ipcs -m:只查看共享内存的相关信息
>
> 4、ipcs -s:只查看信号量集的相关信号
>
> 5、ipcrm -q/-m/-s ID:表示删除某个消息队列、共享内存、信号量集
#### 消息队列
1.
##### 原理图
![](https://i-blog.csdnimg.cn/direct/393d291b93694fdd991113b0752d88df.png)
2.
##### key(钥匙)值的创建
key_t ftok(const char \*pathname, int proj_id);
1.
###### 返回值
成功返回当前ipc对象key值,失败返回-1并置位错误码
2.
###### 参数
> 参数1:一个必须已经存在的文件路径,占key值的四分之三,其中inode号占2字节,设备号占1字节
>
> 参数2:一个随机值,栈key值的1字节
3.
###### 功能
创建IPC通信的key值
3.
##### API
1.
###### 创建消息队列
int msgget(key_t key, int msgflg);
1.
###### 返回值
成功返回该消息队列的id号,以便于后期使用该消息队列,失败返回-1并置位错误码
2.
###### 参数
> 参数1:key值,可以由ftok创建出来,也可以使用IPC_PRIVATE(适用于亲缘进程间通信)
>
> 参数2:消息队列创建的标识
>
> IPC_CREAT:表示创建一个消息队列,如果消息队列已经存在,则直接打开
>
> IPC_EXEC:表示确保创建一个新的消息队列,如果该消息队列存在,则报错
>
> 注意:
>
> 如果是创建消息队列,那么该参数中也要包含该消息队列的权限
3.
###### 功能
通过给定的key值创建一个消息队列对象,并返回该消息队列的id号
2.
###### 向消息队列中存放数据
int msgsnd(int msqid, const void \*msgp, size_t msgsz, int msgflg);
1.
###### 返回值
成功返回0,失败返回-1并置位错误码
2.
###### 参数
> 参数1:消息队列id号
>
> 参数2:消息的起始地址,一般是如下结构体类型,需要用户自己定义
>
> ```cpp
> struct msgbuf {
> long mtype; /* 消息类型,必须是大于0的数字,必须放在结构体的最上面 */
> char mtext[1]; /* 消息正文 */
> };
> ```
>
> 参数3:当前消息的正文的大小,不包含消息的类型大小
>
> 参数4:是否阻塞
>
> 0:表示阻塞
>
> IPC_NOWAIT:表示非阻塞
3.
###### 功能
向msqid对应的消息队列容器中存放以msgp作为起始地址的msgsz大小的数据
3.
###### 从消息队列中读取消息
ssize_t msgrcv(int msqid, void \*msgp, size_t msgsz, long msgtyp,int msgflg);
1.
###### 返回值
成功返回读取数据正文的大小,失败返回-1并置位错误码
2.
###### 参数
> 参数1:消息队列id
>
> 参数2:取出数据存放的容器起始地址
>
> 参数3:消息正文的大小
>
> 参数4:要取出的消息类型
>
> \>0:表示取得指定类型的消息的第一个
>
> =0:表示取得消息队列的第一个(不限制类型)
>
> \<0: 从消息队列中取出类型为小于等于给定类型的绝对值的第一个消息
>
> 参数5:是否阻塞
>
> 0:表示阻塞
>
> IPC_NOWAIT:表示非阻塞
3.
###### 功能
从消息队列中取出一条消息
4.
###### 消息队列的控制函数
int msgctl(int msqid, int cmd, struct msqid_ds \*buf);
1.
###### 返回值
成功返回0,失败返回-1并置位错误码
2.
###### 参数
> 参数1:消息队列id
>
> 参数2: 该函数要执行的操作指令,该函数会因不同的指令执行不同的操作
>
> IPC_STAT: 获取当前消息队列的属性信息,并将该属性放入参数3中
>
> IPC_SET: 设置当前消息队列的属性信息,需要将新信息当做第三个参数传入
>
> IPC_RMID: 删除当前的消息队列,此时参数3可以省略填NULL即可
>
> 参数3:根据参数2而定
3.
###### 功能
用于控制消息队列的操作
脑图
二、作业
作业1:
使用消息队列实现两个进程的相互通信
代码解答:
方法一:进程法
进程A:
cpp
#include <myhead.h>
struct msgbuf
{
long mtype; // 消息类型,必须是长整型
char mtext[128]; // 消息正文,最多包含128个字符
};
#define MSGSZ (sizeof(struct msgbuf)-sizeof(long)) // 定义消息正文的大小,不包含消息类型
int main(int argc, const char *argv[])
{
key_t key = ftok("/", 'I'); // 使用ftok函数生成唯一的消息队列key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误的详细信息
return -1; // 返回错误
}
printf("key=%#x\n", key); // 打印生成的key
pid_t pid = fork(); // 创建子进程
if (pid > 0) // 父进程执行此部分代码
{
int msqid = msgget(key, IPC_CREAT | 0664); // 创建或获取消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
return -1; // 返回错误
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义发送消息的缓冲区
while (1)
{
printf("请输入消息类型>>>(类型只能有1)");
scanf("%ld", &buf.mtype); // 从用户输入中读取消息类型
getchar(); // 清除输入缓冲区中残留的换行符
if (buf.mtype != 1) // 如果消息类型不为1,提示重新输入
{
printf("输入的类型有误请重新输入\n");
continue;
}
printf("请输入消息正文>>>");
fgets(buf.mtext, MSGSZ, stdin); // 从标准输入中获取消息正文
buf.mtext[strlen(buf.mtext) - 1] = 0; // 去除换行符
msgsnd(msqid, &buf, MSGSZ, 0); // 将消息发送到消息队列
printf("发送成功\n"); // 成功发送提示
if (strcmp(buf.mtext, "quit") == 0) // 如果消息内容为"quit",退出循环
{
break;
}
}
}
else if (pid == 0) // 子进程执行此部分代码
{
int msqid = msgget(key, IPC_CREAT | 0664); // 获取父进程中创建的消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
return -1; // 返回错误
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf rbuf; // 定义接收消息的缓冲区
while (1)
{
msgrcv(msqid, &rbuf, MSGSZ, 2, 0); // 从消息队列中读取类型为2的消息,阻塞方式接收
printf("收到的消息为:%s\n", rbuf.mtext); // 打印接收到的消息内容
if (strcmp(rbuf.mtext, "quit") == 0) // 如果收到的消息内容为"quit",退出循环
{
break;
}
}
// 删除消息队列
if (msgctl(msqid, IPC_RMID, NULL) == -1) // 删除消息队列
{
perror("msgctl error"); // 打印删除消息队列时的错误信息
return -1; // 返回错误
}
exit(EXIT_SUCCESS); // 子进程正常退出
}
else // fork函数调用出错
{
perror("fork error"); // 打印fork调用错误信息
return -1; // 返回错误
}
waitpid(0, NULL, 0); // 父进程等待子进程退出
return 0; // 程序正常结束
}
进程B:
cpp
#include <myhead.h>
struct msgbuf
{
long mtype; // 消息类型,必须是长整型
char mtext[128]; // 消息正文,最多包含128个字符
};
#define MSGSZ (sizeof(struct msgbuf)-sizeof(long)) // 定义消息正文的大小,不包含消息类型
int main(int argc, const char *argv[])
{
key_t key = ftok("/", 'I'); // 使用ftok函数生成唯一的消息队列key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误的详细信息
return -1; // 返回错误
}
printf("key=%#x\n", key); // 打印生成的key
pid_t pid = fork(); // 创建子进程
if (pid == 0) // 子进程执行此部分代码
{
int msqid = msgget(key, IPC_CREAT | 0664); // 创建或获取消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
return -1; // 返回错误
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义发送消息的缓冲区
while (1)
{
printf("请输入消息类型>>>(类型只能有2)");
scanf("%ld", &buf.mtype); // 从用户输入中读取消息类型
getchar(); // 清除输入缓冲区中残留的换行符
if (buf.mtype != 2) // 如果消息类型不为2,提示重新输入
{
printf("输入的类型有误请重新输入\n");
continue;
}
printf("请输入消息正文>>>");
fgets(buf.mtext, MSGSZ, stdin); // 从标准输入中获取消息正文
buf.mtext[strlen(buf.mtext) - 1] = 0; // 去除换行符
msgsnd(msqid, &buf, MSGSZ, 0); // 将消息发送到消息队列
printf("发送成功\n"); // 成功发送提示
if (strcmp(buf.mtext, "quit") == 0) // 如果消息内容为"quit",退出循环
{
break;
}
}
}
else if (pid > 0) // 父进程执行此部分代码
{
int msqid = msgget(key, IPC_CREAT | 0664); // 创建或获取消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
return -1; // 返回错误
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf rbuf; // 定义接收消息的缓冲区
while (1)
{
msgrcv(msqid, &rbuf, MSGSZ, 1, 0); // 从消息队列中读取类型为1的消息
printf("收到的消息为:%s\n", rbuf.mtext); // 打印接收到的消息内容
if (strcmp(rbuf.mtext, "quit") == 0) // 如果收到的消息内容为"quit",退出循环
{
break;
}
}
// 删除消息队列
if (msgctl(msqid, IPC_RMID, NULL) == -1) // 删除消息队列
{
perror("msgctl error"); // 打印删除消息队列时的错误信息
return -1; // 返回错误
}
exit(EXIT_SUCCESS); // 父进程正常退出
}
else // fork函数调用出错
{
perror("fork error"); // 打印fork调用错误信息
return -1; // 返回错误
}
waitpid(0, NULL, 0); // 父进程等待子进程退出
return 0; // 程序正常结束
}
方法二:线程法
进程A:
cpp
#include <myhead.h>
// 定义消息缓冲区结构体,包含消息类型和消息正文
struct msgbuf
{
long mtmype; // 消息类型,必须是长整型
char mtext[128]; // 消息正文,最多128个字符
};
#define MSGSZ (sizeof(struct msgbuf) - sizeof(long)) // 计算消息正文的大小,不包含消息类型
// 发送消息到B进程的函数,作为父线程的执行体
void *send_to_b(void *arg)
{
key_t key = ftok("/", 'I'); // 使用ftok生成唯一的key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("key=%#x\n", key); // 打印生成的key
int msqid = msgget(key, IPC_CREAT | 0664); // 创建或获取消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义发送消息的缓冲区
while (1)
{
printf("请输入消息类型>>>(只能输入1)");
scanf("%ld", &buf.mtmype); // 获取消息类型
getchar(); // 清除输入缓冲区中的换行符
if (buf.mtmype != 1) // 如果消息类型不是1,提示重新输入
{
printf("输入有误,请重新输入\n");
continue; // 重新输入
}
printf("请输入消息正文>>>");
fgets(buf.mtext, MSGSZ, stdin); // 获取消息正文
buf.mtext[strlen(buf.mtext) - 1] = 0; // 去掉最后的换行符
msgsnd(msqid, &buf, MSGSZ, 0); // 将消息发送到消息队列
printf("发送成功\n"); // 提示发送成功
if (strcmp(buf.mtext, "quit") == 0) // 如果消息内容为"quit",退出循环
{
break;
}
}
pthread_exit(NULL); // 线程正常退出
}
// 接收来自B进程消息的函数,作为子线程的执行体
void *receive_from_b(void *arg)
{
key_t key = ftok("/", 'I'); // 生成唯一的key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("key=%#x\n", key); // 打印生成的key
int msqid = msgget(key, IPC_CREAT | 0664); // 获取或创建消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义接收消息的缓冲区
while (1)
{
msgrcv(msqid, &buf, MSGSZ, 2, 0); // 从消息队列中接收类型为2的消息
printf("收到消息为%s\n", buf.mtext); // 打印收到的消息内容
if (strcmp(buf.mtext, "quit") == 0) // 如果收到的消息为"quit",退出循环
{
break;
}
}
if (msgctl(msqid, IPC_RMID, NULL) == -1) // 删除消息队列
{
perror("msgctl error"); // 打印msgctl调用错误信息
pthread_exit(NULL); // 线程退出
}
pthread_exit(NULL); // 线程正常退出
}
// 主函数
int main(int argc, const char *argv[])
{
pthread_t tid1, tid2; // 定义两个线程ID变量
pthread_create(&tid1, NULL, send_to_b, NULL); // 创建发送消息的父线程
pthread_create(&tid2, NULL, receive_from_b, NULL); // 创建接收消息的子线程
pthread_join(tid1, NULL); // 等待父线程执行完毕
pthread_join(tid2, NULL); // 等待子线程执行完毕
return 0; // 程序正常结束
}
进程B:
cpp
#include <myhead.h>
// 定义消息缓冲区结构体,包含消息类型和消息正文
struct msgbuf
{
long mtmype; // 消息类型,必须是长整型
char mtext[128]; // 消息正文,最多128个字符
};
#define MSGSZ (sizeof(struct msgbuf) - sizeof(long)) // 计算消息正文的大小,不包含消息类型
// 发送消息到A进程的函数,作为子线程的执行体
void *send_to_a(void *arg)
{
key_t key = ftok("/", 'I'); // 使用ftok生成唯一的key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("key=%#x\n", key); // 打印生成的key
int msqid = msgget(key, IPC_CREAT | 0664); // 创建或获取消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义发送消息的缓冲区
while (1)
{
printf("请输入消息类型>>>(只能输入2)");
scanf("%ld", &buf.mtmype); // 获取消息类型
getchar(); // 清除输入缓冲区中的换行符
if (buf.mtmype != 2) // 如果消息类型不是2,提示重新输入
{
printf("输入有误,请重新输入\n");
continue; // 重新输入
}
printf("请输入消息正文>>>");
fgets(buf.mtext, MSGSZ, stdin); // 获取消息正文
buf.mtext[strlen(buf.mtext) - 1] = 0; // 去掉最后的换行符
msgsnd(msqid, &buf, MSGSZ, 0); // 将消息发送到消息队列
printf("发送成功\n"); // 提示发送成功
if (strcmp(buf.mtext, "quit") == 0) // 如果消息内容为"quit",退出循环
{
break;
}
}
pthread_exit(NULL); // 线程正常退出
}
// 接收来自A进程消息的函数,作为父线程的执行体
void *receive_from_a(void *arg)
{
key_t key = ftok("/", 'I'); // 生成唯一的key
if (key == -1) // 错误检查
{
perror("ftok error"); // 打印ftok调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("key=%#x\n", key); // 打印生成的key
int msqid = msgget(key, IPC_CREAT | 0664); // 获取或创建消息队列
if (msqid == -1) // 错误检查
{
perror("msgget error"); // 打印msgget调用错误信息
pthread_exit(NULL); // 线程退出
}
printf("msqid=%d\n", msqid); // 打印消息队列ID
struct msgbuf buf; // 定义接收消息的缓冲区
while (1)
{
msgrcv(msqid, &buf, MSGSZ, 1, 0); // 从消息队列中接收类型为1的消息
printf("收到消息为%s\n", buf.mtext); // 打印收到的消息内容
if (strcmp(buf.mtext, "quit") == 0) // 如果收到的消息为"quit",退出循环
{
break;
}
}
if (msgctl(msqid, IPC_RMID, NULL) == -1) // 删除消息队列
{
perror("msgctl error"); // 打印msgctl调用错误信息
pthread_exit(NULL); // 线程退出
}
pthread_exit(NULL); // 线程正常退出
}
// 主函数
int main(int argc, const char *argv[])
{
pthread_t tid1, tid2; // 定义两个线程ID变量
pthread_create(&tid1, NULL, receive_from_a, NULL); // 创建接收消息的父线程
pthread_create(&tid2, NULL, send_to_a, NULL); // 创建发送消息的子线程
pthread_join(tid1, NULL); // 等待父线程执行完毕
pthread_join(tid2, NULL); // 等待子线程执行完毕
return 0; // 程序正常结束
}
成果展示:
方法一:进程法
方法二:线程法
三、总结
学习内容概述
1. 信号通信:
信号处理的三种方式(默认处理、捕获、忽略)、常见信号类型及其处理、如何使用 `signal` 函数绑定信号处理方式、通过 `alarm` 启动定时器,以及使用 `kill` 和 `raise` 发送信号给进程。
2. System V 提供的 IPC 机制:
包括消息队列的创建、发送、接收及控制函数的使用,重点学习了如何通过 `ftok` 函数生成消息队列的 `key`,以及消息队列的存取操作(`msgsnd` 和 `msgrcv`)。
学习难点
1. 信号处理的机制与应用:
1、理解信号的捕获机制和如何使用 `signal` 函数绑定信号处理函数是较为复杂的部分,尤其是不同信号的处理行为,如 SIGPIPE、SIGSEGV、SIGINT 等的触发条件和处理方式。
2、理解信号的实时性及不可排队处理。多个相同信号发送到一个进程时,可能会导致一些信号丢失,这要求处理程序具有足够的容错能力。
2. 消息队列的使用:
1、理解消息队列的结构体 `msgbuf` 的设计,以及消息类型 `mtype` 的特殊要求(必须为大于 0 的值)。
2、学习如何根据消息类型有选择地从消息队列中接收消息(通过 `msgrcv` 的 `msgtyp` 参数指定),并处理消息队列中的并发与顺序问题。
3、控制消息队列的操作,如 `msgctl` 用于删除或查看消息队列的状态,这在多进程协作中是一个重要但复杂的环节。
主要事项
1. 信号通信的使用:
信号的捕获与处理:
信号是操作系统发送给进程的异步通知,通过 `signal` 函数,进程可以指定自定义的信号处理函数来捕获并处理特定的信号。
常见信号处理:
例如 `SIGINT` 用于捕捉用户按下 `Ctrl+C` 产生的中断信号,`SIGPIPE` 用于处理写管道断裂,`SIGALRM` 是通过 `alarm` 函数生成的定时信号。
信号的发送:
通过 `kill` 可以向指定进程发送信号,`raise` 则用于向当前进程发送信号。信号可以用于进程间的简单通信和控制。
2. System V IPC 机制中的消息队列:
创建消息队列:
通过 `msgget` 函数创建消息队列,`ftok` 函数用于生成消息队列的唯一标识 `key`,确保不同进程可以共享同一个队列。
消息的发送与接收:
使用 `msgsnd` 向队列发送消息,使用 `msgrcv` 从队列中读取消息。每条消息都必须指定一个类型 `mtype`,用于标识消息的优先级或类别。
消息队列的控制:
`msgctl` 提供了一些管理消息队列的功能,如删除队列或查看队列状态,确保队列不再使用时能够清理资源。
未来学习的重点:
1. 深入理解信号的并发处理:
1、学习如何处理多个信号的并发情况,避免重要信号被忽略或丢失,特别是在高并发环境下如何避免信号混乱。同时学习如何使用 `sigaction` 替代 `signal` 进行更精细的信号处理控制。
2、学习更多系统信号的具体作用和场景应用,如 `SIGCHLD` 用于处理子进程退出,`SIGKILL` 强制结束进程等。
2. 高级 IPC 机制:
1、继续深入学习其他 System V IPC 机制,如共享内存(`shmget`、`shmat`)和信号量(`semget`、`semop`),这些是进程间通信的重要方式。
2、理解消息队列的并发访问控制,通过适当的同步机制(如信号量或锁)保证多进程之间的顺序操作和数据一致性。
3. 实战应用与优化:
1、将所学的信号处理机制与 IPC 机制结合应用于实际项目中,如创建一个简单的多进程任务管理系统,使用信号和消息队列进行进程间的协调和通信。
2、学习如何优化 IPC 性能,特别是在多进程并发较高的场景中,如何减少 IPC 操作的延迟,提升消息队列的处理效率。