当被问到this question时,我有一个问题要完成我的例子 我搜索了在谷歌实施IPC的方法 我无法决定哪种方式最适合编写我的程序 我尝试了很多实现,并且有很多并发症。
我希望:
1.管理子进程的父进程 - 确定(模板)
2.父母和孩子必须实施新消息信号回调
3.一个进程不知道来自其他进程的消息大小(char *)
我的代码:
header.h:
#ifndef MESSAGES_H
#define MESSAGES_H
#include <stdio.h>
#include <stdlib.h>
// need here: some includes and definitions
inline char * read_message( /* need here: some params */ ) {
// need here: read message function
}
inline char * send_message( /* need here: some params */ ) {
// need here: send message function
}
#endif
parent.c:
#include "header.h"
// parent specyfic includes and definitions
void on_message( /* need here: some parameters */ ) {
char *message = read_message( /* need here: some other parameters */ );
// do something with / if message etc.
}
int runChild(key) {
int pid = fork();
if (pid == 0) {
execl("./child", "./child", /* params here */, null);
}else{
return pid;
}
}
int main(int argc, char *argv[]) {
// need here: prepare IPC
// need here: on new message event call function "on_message"
int childPid = runChild(key);
// loop - for example: gtk_main()
// need here: close childs
}
child.c
#include "header.h"
// child specyfic includes and definitions
void on_message( /* need here: some parameters */ ) {
char *message = read_message( /* need here: some other parameters */ );
// do something with / if message etc.
}
int main(int argc, char *argv[]) {
// need here: prepare IPC
// need here: on new message event call function "on_message"
int pid = getpid();
int parentPid = getppid();
printf("Child with pid %d is ready for messages from parent with pid: %d", pid, parentPid);
// event loop - for example: gtk_main()
}
在该示例程序模板中哪种IPC方式更好(安全和速度)? 你能分享一个与上述模板匹配的非常简单的例子吗?
答案 0 :(得分:9)
实施IPC有许多不同的方法。要获得良好的比较,请参阅Stevens'本书。经典之作是在UNIX环境中&#39;高级编程,但也有&#39; UNIX网络编程,第2卷,第2版:进程间通信&#39; 。我知道有时候在其他地方指向引用并不是一种好的形式,但无论这是一个学术问题还是商业问题,大多数UNIX程序员都会认为史蒂文斯是一个非常宝贵的资源。
那就是说,这是IPC的主要选择:
在进程之间使用pipe()
。格式将始终基于流;如果你要发送数据结构,这可能会很麻烦,因为你不仅要担心序列化,还需要担心缓冲和翻译数据包。回到消息。管道是单向的,因此您需要两个用于双向通信。
使用命名管道或fifo。这允许多对一通信,并且在一端退出后,fifo仍然存在。否则按照(1)。
在进程之间使用socketpair
- 特别是unix域套接字。套接字是双向的。您可以使用流套接字(SOL_STREAM
),数据报(不可靠,无法保证排序 - SOCK_DGRAM
)或可能更好的顺序可靠双向数据包通信(SOCK_SEQPACKET
)。基于分组的通信意味着您可以(例如)在每个数据包中放置一个数据结构。
使用信号。实际上,您可以一次发送整数。信号与线程混合不好,处理中断的系统调用很难,并且各种竞争条件使它们不可靠,除非您知道自己在做什么并且不太担心可移植性。在大多数情况下最好避免。
使用系统V信号量(semget
等)或POSIX信号量(sem_open
等)。用于在进程之间发送信号以实现同步,但不多。
使用共享内存(shmget
等) - 使多个进程可以看到相同的页面。您需要结合某种同步方法。
System V消息队列(msgget
等) - 维护两个进程之间的数据包(消息)队列。
以上的一些组合。
我只在内核的分支(例如Binder)或正在开发中(例如KDBus)省略了一些东西。
几乎所有上述内容的示例和教程都可以找到here。
现在,大部分内容都可用于您提及的应用程序。看起来你想发送可变大小的消息,所以如果你使用基于流的协议,正常的hack是将数据包的长度作为前1,2或4个字节发送(取决于最大长度)。基于分组的协议在这里(显然)更容易,但每个都有自己的最大数据包大小。你关心可靠性吗?你关心便携性吗?你关心速度吗?在选择它们时,所有这些都是有效的关注点。
最后,基于FD的方法(例如管道,套接字)的一个优点是你可以将它们添加到普通的select()
循环中;如果您的计划中还有其他事情发生,这可能会有所帮助。
您在评论中询问了socketpair
代码的一些示例。我重申最高层对斯蒂芬斯的评论。如果没有:
fork()
为IPC设置套接字对的一个很好的示例。socketpair()
here上有一个很好的部分。答案 1 :(得分:2)
这是我最近写的一个多进程程序的一些设置代码,使用select来提供非阻塞等待。这显然也是在C ++中实现它的更好方法之一,因为从我收集的文件描述符得不到标准库的良好支持......
// Parent
int main(int argc, char **argv) {
// Pipe, fork, exec (to run robot in child)
int toParent[2], fromParent[2];
pipe(toParent);
pipe(fromParent);
// Redirect childs stdin/stdout
if (fork()) { // parent
close(toParent[1]); // [1] == write
close(fromParent[0]); // [0] == read
}
else {
close(toParent[0]);
close(fromParent[1]);
dup2(toParent[1], 1);
dup2(fromParent[0], 0);
close(toParent[1]);
close(fromParent[0]);
execl("../robot/robot", "../robot/robot", (char *) NULL);
}
FILE * output = fdopen(fromParent[1], "w");
FILE * input = fdopen(toParent[0], "r");
// Set up for select() read of input pipe
fd_set set;
struct timeval timeout;
// Initialize the file descriptor set.
FD_ZERO(&set);
FD_SET(toParent[0], &set);
// Initialize the timeout data structure
timeout.tv_sec = 0;
timeout.tv_usec = 10;
while(1) {
// Non-blocking read of pipe
// NOTE: only expecting to read one pipe so no need to check which pipe got data
if (select(toParent[0]+1, &set, NULL, NULL, &timeout) > 0) {
// read the input pipe here
}
// Reset select FD -- maybe only do this when an input has been read?
FD_ZERO(&set);
FD_SET(toParent[0],&set);
}
一般的想法是允许孩子通过stdin / stdout(通过使用dup2())与父进行通信,然后使用FILE *输出和输入写入child。唯一的警告是调试打印到子节点中的stdout可能会导致意外行为,如果父节点没有处理它,所以一般来说最安全的是将调试消息打印到子节点中的stderr。
至于回调,您可以使用选择,这在其他地方已有很好的记录。