003_音频开发_基础篇_Linux进程通信(20种你了解几种?)
文章目录
- 003_音频开发_基础篇_Linux进程通信(20种你了解几种?)
- 创作背景
- `Linux` 进程通信类型
- `fork()` 函数
- `fork()` 输出 `2` 次
- `fork()` 输出 `8` 次
- `fork()` 返回值
- `fork()` 创建子进程 方式一
- `fork()` 创建子进程 方式二
- `fork()` 创建子进程 方式三
- `fork()` 创建子进程 方式四
- `fork()` 复制父进程上下文
- `Socket` 套接字(待更新)
- `Socket` - (`Linux IP` 协议)
- `Socket` - `UDS`
- `IP 协议` VS. `UDS`
- `UDS` 应用场景 —— `Docker Daemon`
- `UDS` 应用场景 —— `Azure IoT Edge`
- Socket Activation
- 管道 `pipe`
- 匿名管道 `Anonymous pipes`
- 命名管道 `Named pipe/FIFOs`
- 消息队列
- 共享内存
- 信号量
- 文件锁
- 共享文件
- RPC (Remote Procedure Call)
- Protocol Buffer
- gRPC
- RabbitQM
- ZeroMQ
- RPC over HTTP
- FlatBuffers
创作背景
学历代表过去、能力代表现在、学习力代表将来。 一个良好的学习方法是通过输出来倒逼自己输入。写博客既是对过去零散知识点的总结和复盘,也是参加了 零声教育 写博客活动。
零声教育体验课:https://xxetb.xetslk.com/s/3fbO81
本文是开发过程中的知识点总结,供大家学习交流使用,如有任何错误或不足之处,请在评论区指出。
Linux
进程通信类型
如下表描述了Linux
常用进程间通信方式了,包含了多种技术,例如基于系统调用、套接字、共享内存和消息队列等,用于实现进程之间的数据传输、同步和协作,以满足不同应用场景下的需求。
序号 | 中文名 | 英文名 | 描述 |
---|---|---|---|
1 | 管道 | Pipe | 匿名管道 在父子进程或兄弟进程之间进行通信。 命名管道 独立进程间通信 |
2 | 消息队列 | Message Queues | 进程之间通过消息队列进行通信,可以实现异步通信。 |
3 | 共享内存 | Shared Memory | 进程可以通过映射共享内存区域来实现共享数据,提高通信效率。 |
4 | 信号 | Signals | 进程可以通过信号来通知其他进程发生了某些事件。 |
5 | 信号量 | Semaphores | 用于控制对共享资源的访问,防止多个进程同时访问造成冲突。 |
6 | 文件锁 | File Locks | 进程可以通过文件锁来实现对共享文件的互斥访问。 |
7 | 共享文件 | Shared Files | 进程可以通过读写共享文件来进行通信,但需要注意同步和并发访问的问题。 |
8 | Memory Mapped Files | Memory Mapped Files | 进程可以将文件映射到它们的地址空间中,实现对文件内容的共享访问。 |
9 | 直接操作内存 | Direct Memory Access | 进程可以直接读写其他进程的内存来进行通信,但这通常需要特殊的权限和保护机制。 |
10 | Event Loops | Event Loops | 进程可以使用事件循环来监听和处理事件,实现进程间的消息传递和通知。 |
11 | 套接字/UDS | Unix | Unix域套接字(Unix Domain Socket),用于本地进程间通信。 |
12 | DBus | dbus | D-Bus 是一个进程间通信机制,常用于 Linux 桌面环境中实现进程间通信和协作。 |
13 | 套接字/IP | IP | Internet套接字(Internet Domain Socket),用于在网络中进行通信。 |
14 | Remote Procedure Calls | RPC | 允许进程调用另一个进程中的函数,实现远程通信和协作。 |
15 | RPC over HTTP | RPC over HTTP | 使用HTTP协议作为传输层,通过远程过程调用(RPC)来进行进程间通信。 |
16 | ZeroMQ | ZeroMQ | ZeroMQ 是一个开源的消息队列库,提供了丰富的 API 和通信模式,用于实现进程间通信。 |
17 | Socket Activation | Socket Activation | systemd中的一种机制,允许在需要时才启动服务进程。通过UNIX域套接字传递连接。 |
18 | RabbitMQ | RabbitMQ | 基于AMQP协议的开源消息代理,用于实现可靠的异步通信。 |
19 | gRPC | gRPC | gRPC 是一个高性能、开源的远程过程调用(RPC)框架,可以用于实现进程间通信。 |
20 | Protocol Buffers | Protocol Buffers | 使用序列化格式将数据结构编码后进行传输,实现进程间通信。 |
21 | FlatBuffers | FlatBuffers | 使用序列化格式将数据结构编码后进行传输,实现进程间通信。 |
fork()
函数
fork()
是一个系统调用,用于创建一个与当前进程完全相同的新进程。- 原进程称为 父进程,新创建的进程称为 子进程。
- 在父进程中,
fork()
返回子进程的进程ID
;在子进程中,fork()
返回0
。 - 在调用
fork()
时,两个内存空间具有相同的内容。 - 一个进程进行的内存写操作、文件映射和解除映射操作不会影响另一个进程。
- 注意:
fork()
调用会有2
次返回,正常我们的函数调用只有一次返回。
fork()
输出 2
次
fork()
函数调用后输出两次:当前进程 + 子进程
int main(void)
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
fork();
printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
输出 8
次
int main(void)
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
fork();
fork();
fork();
printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
返回值
fork()
函数在父进程中返回子进程的进程 ID
,而在子进程中返回 0
。调用失败,它将返回一个负值,通常是 -1
,表示创建新进程失败。
int main(void)
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
pid_t pid = fork();
if (0 == pid)
{
printf("child pid=%d, parent pid=%d\n", getpid(), getppid());
}
else if (0 < pid)
{
printf("pid=%d, parent pid=%d\n", getpid(), getppid());
}
else
{
printf("fork() failed.\n");
}
printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
创建子进程 方式一
父进程创建 2
个子进程
int main()
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
// 1. parent
pid_t id = fork();
if (id == 0) // 2. new child
{
fork(); // 3. return twice (one is new child)
}
// total: 3 thread = 1 current + 2 child
printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
创建子进程 方式二
父进程创建 4
个子进程
int main()
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid()); // 1. parent
pid_t id = fork();
if (id == 0) // 2. new child
{
id = fork();
if (id == 0) // 3. new child
{
id = fork();
if (id == 0) // 4. new child
{
fork(); // 5. return twice (one is new child)
}
}
} // total: 3 thread = 1 parent + 4 child printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
创建子进程 方式三
父进程创建 4
个子进程
int main(void) {
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid()); pid_t id[4]; // 1. parent
for (int i = 0; i < 4; i++)
{
id[i] = fork();
if (id[i] == 0) // 2. new 4 child, by the same parent.
{
printf("\tI'm child pid=%d, parent pid=%d\n", getpid(), getppid());
break;
}
else if (id[i] > 0)
{
printf("\tI'm parent pid=%d, parent pid=%d\n", getpid(), getppid());
}
else
{
printf("fork() failed.\n");
}
} // total: 5 thread = 1 parent + 4 child printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid()); // print 5 times
return 0;
}
fork()
创建子进程 方式四
父进程比子进程先退出,子进程会变为“孤儿进程”,被1
号/init --user
进程收养,进程编号也会变为 1
。
int main()
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
pid_t id[4];
// 1. parent
for (int i = 0; i < 4; i++)
{
id[i] = fork();
if (id[i] > 0) // 2. new 4 child, child create child.
{
break;
}
else if (id[i] == 0) // child exit
{}
else //failed
{}
}
// total: 5 thread = 1 parent + 4 child printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}
fork()
复制父进程上下文
char buffer[64] = "hello world!";
int main()
{
printf("#### Start pid=%d, parent pid=%d\n\n", getpid(), getppid());
pid_t pid = fork();
if (0 < pid) // parent
{
printf("\tI'm parent pid=%d, parent pid=%d\n", getpid(), getppid());
for (int i = 0; i < 10; i++)
{
sleep(1);
printf("\t%d)Parent (%s)\n", i, buffer);
}
}
else if (0 == pid) // child
{
printf("\tI'm child pid=%d, parent pid=%d\n", getpid(), getppid());
for (int i = 0; i < 5; i++)
{
if (1 == i) // child thread modify buffer
{
strcpy(buffer, "Sub message.");
}
sleep(1);
printf("\t%d)Sub (%s)\n", i, buffer);
}
}
else
{
printf("fork() failed.\n");
}
printf("#### Exit pid=%d, parent pid=%d\n", getpid(), getppid());
return 0;
}