Linux软件编程学习笔记(八):进程间通信详解(1)
·
前言
进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间交换数据和信息的技术。由于进程空间是独立的,每个进程独享自己的0-4G虚拟内存空间,因此需要借助特定的机制来实现进程间的数据交换。本文将详细介绍Linux下常用的进程间通信方式。
一、进程间通信概述
1. 进程间通信的必要性
- 进程空间是独立的,无法直接访问其他进程的内存空间
- 多个进程协同工作需要进行数据交换
- 进程间通信是构建复杂应用程序的基础
2. 进程间通信的方法
- 管道(无名管道、有名管道)
- 信号
- 消息队列
- 共享内存
- 信号灯
二、管道通信
1. 无名管道
1.1 概念
无名管道只能用于具有亲缘关系的进程间通信(如父子进程之间)。
1.2 函数接口
pipe函数
int pipe(int pipefd[2]);
- 功能:创建一段用于进程间通信的内核缓存区
- 参数:
pipefd[0]:读文件描述符pipefd[1]:写文件描述符
- 返回值:成功返回0,失败返回-1
1.3 管道特性
关于写端:
- 管道中有写端存在时:
- 管道中有数据则阻塞等待直到数据被读出
- 管道中没有写端时:
- 管道中有数据则直接读出
- 管道中没有数据则直接结束,不需要阻塞等待
关于读端:
- 管道中有读端存在时:
- 如果管道没有写满(64K),直接写入数据
- 如果管道已写满,则需要有数据读出才能继续写入
- 管道中没有读端时:
- 写入数据会产生管道破裂的错误,使进程直接退出
2. 有名管道(FIFO)
2.1 概念
有名管道可以用于任意两个进程间的通信,不局限于具有亲缘关系的进程。
2.2 函数接口
mkfifo函数
int mkfifo(const char *pathname, mode_t mode);
- 功能:创建一个有名管道文件
- 参数:
pathname:有名管道的路径mode:权限(如0664)
- 返回值:成功返回0,失败返回-1
2.3 特性说明
- 读写两端必须同时加入才能继续向下执行
- 如果只有读端或写端,则会阻塞等待直到另一端加入
2.4 聊天程序示例
write.c(负责发送数据)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int fd = open("/tmp/fifo", O_WRONLY);
char buf[1024] = {0};
while (1) {
gets(buf);
write(fd, buf, strlen(buf));
}
close(fd);
return 0;
}
read.c(负责接收数据)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
mkfifo("/tmp/fifo", 0664);
int fd = open("/tmp/fifo", O_RDONLY);
char buf[1024] = {0};
while (1) {
read(fd, buf, sizeof(buf));
printf("收到消息:%s\n", buf);
}
close(fd);
return 0;
}
三、信号通信
1. 信号概述
信号是内核层与应用层通信的一种机制,用于通知进程发生了某种事件。
2. 信号处理方式
- 缺省:按照默认方式处理信号
- 忽略:不处理信号
- 捕捉:按照指定的方式处理信号
3. 常见信号类型
| 信号编号 | 信号名称 | 说明 |
|---|---|---|
| 2 | SIGINT | 从键盘输入Ctrl+C产生 |
| 3 | SIGQUIT | 从键盘输入Ctrl+\产生 |
| 9 | SIGKILL | 不能被忽略和捕捉 |
| 11 | SIGSEGV | 段错误信号 |
| 13 | SIGPIPE | 管道破裂信号 |
| 14 | SIGALRM | 定时时间到达信号 |
| 17 | SIGCHLD | 子进程结束信号 |
| 19 | SIGSTOP | 暂停信号 |
| 20 | SIGTSTP | 挂起信号(Ctrl+Z) |
4. 函数接口
4.1 signal函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- 功能:改变信号的处理方式
- 参数:
signum:信号的编号handler:SIG_IGN:忽略信号SIG_DFL:缺省处理信号- 自定义函数指针:捕捉信号
- 返回值:成功返回上一次注册的信号处理方式,失败返回SIG_ERR
使用示例:
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
printf("收到信号:%d\n", signum);
}
int main(void)
{
// 捕捉SIGINT信号
signal(SIGINT, handler);
// 忽略SIGQUIT信号
signal(SIGQUIT, SIG_IGN);
while (1) {
sleep(1);
}
return 0;
}
4.2 pause函数
int pause(void);
- 功能:让进程睡眠,直到收到信号
4.3 alarm函数
unsigned int alarm(unsigned int seconds);
- 功能:隔seconds秒后给调用进程发送SIGALRM信号
使用示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int signum)
{
printf("定时时间到!\n");
alarm(2); // 重新定时
}
int main(void)
{
signal(SIGALRM, handler);
alarm(2); // 2秒后发送SIGALRM信号
while (1) {
pause(); // 等待信号
}
return 0;
}
四、其他进程间通信方式
1. 消息队列
消息队列是内核中存放消息的链表,允许多个进程向同一个队列读写消息。
2. 共享内存
共享内存是最快的IPC方式,多个进程共享同一块物理内存区域,无需数据复制。
3. 信号灯
信号灯(信号量)主要用于进程间同步,控制多个进程对共享资源的访问。
总结
本文详细介绍了Linux下进程间通信的几种主要方式:
- 无名管道:适用于有亲缘关系的进程间通信
- 有名管道:适用于任意进程间的通信
- 信号:内核与应用层通信的异步通知机制
在实际开发中,需要根据具体场景选择合适的IPC方式:
- 简单数据流通信:使用管道
- 异步事件通知:使用信号
- 大量数据交换:使用共享内存
- 多个进程协同:使用消息队列或信号灯
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)