C Application ---Each time calls Bash script---> Bash application ---???---> C++ Application
我想到了几种解决方案:
- 使用TCP网络套接字,但这意味着select将需要处理监听和实际套接字的事件
- 使用命名管道,但一旦bash脚本终止,管道的另一端也会关闭
有没有更简单的方法可以在select()中只使用一个文件描述符?
C Application ---Each time calls Bash script---> Bash application ---???---> C++ Application
我想到了几种解决方案:
有没有更简单的方法可以在select()中只使用一个文件描述符?
我会使用一个无名的pipe()
来完成任务。请记住,在UNIX系统中,通过fork()
和execve()
创建的文件描述符会在两个进程中保持打开状态! 所以,您可以使用pipe()
获取一对文件描述符,然后使用bash的echo >&FD
将内容写入文件描述符,其中FD
是文件描述符号码。
这非常简单易懂,且比其他任何方法都要节省资源。使用select()
也没有问题,只需要不要像我在示例中那样在read()
上阻塞,而是在pfds[0]
上使用select()
。
下面是示例程序(产生10个bash进程,每个进程发送“hello work, my pid: XXX”,在产生进程之间等待1秒。该示例仅使用一个管道用于所有子进程。我按照作者的要求更改了它,但实际上我不建议这样做(请参见示例下面的说明):
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char **argv) {
int pfds[2];
pid_t p;
assert(0 == pipe(pfds));
p = fork();
if (p == 0) {
unsigned int i;
char str_fd[3]; char *env[] = {NULL};
char *args[] = { "/bin/bash", "-c", "echo >&$1 hello world, my pid: $$"
, "-s", str_fd, NULL};
snprintf(str_fd, 3, "%d", pfds[1]);
str_fd[2] = 0;
for (i = 0; i < 10; i++) {
p = fork();
if(0 == p) {
assert(0 ==
execve( "/bin/bash", (char *const*)args
, (char *const*)env));
} else if (0 > p) {
perror("fork");
exit(1);
} else {
wait(NULL);
}
sleep(1);
}
} else if(p > 0) {
char *buf = malloc(100);
ssize_t sz;
printf("fd is %d, <hit Ctrl+C to exit>\n", pfds[1]);
while(0 < ( sz = read(pfds[0], buf, 100))) {
buf[99] = 0;
printf("received: '%s'\n", buf);
}
free(buf);
if (0 == sz) {
fprintf(stderr, "EOF!");
} else {
perror("read from bash failed");
}
wait(NULL);
} else {
perror("fork failed");
exit(1);
}
return 0;
}
示例程序输出:
$ gcc test.c && ./a.out
fd is 4, <hit Ctrl+C to exit>
received: 'hello world, my pid: 779
'
received: 'hello world, my pid: 780
'
received: 'hello world, my pid: 781
'
received: 'hello world, my pid: 782
'
received: 'hello world, my pid: 783
'
received: 'hello world, my pid: 784
'
received: 'hello world, my pid: 785
'
received: 'hello world, my pid: 786
'
received: 'hello world, my pid: 787
'
received: 'hello world, my pid: 788
'
这个程序可以使用一个管道,让子进程都向父进程发送“hello world, my pid: XXX\n”信息。虽然这个演示程序可以工作(在Linux和MacOS X下测试过,应该满足POSIX语义规范),但我仍然建议每个子进程使用单独的pipe()
。这样做会减少问题,并且允许同时运行多个子进程,如果你有很多子进程,可以使用select()
或epoll()
。
由于pipe()
非常便宜,特别是与bash比较,我绝对不会使用同一个管道来处理多个子进程(就像我的更新示例一样)。
sendmsg()
/sendto()
的外部程序)。 - Johannes Weissecho "abc" > socket
可能就足够了。 - Maxim Egorushkin
epoll()
。 - Johannes Weissfork()
来生成进程。即使您使用system()
,它也会执行fork()
+execve()
。对于我的方法,我使用参数($1
)将fd传递给bash脚本,如果您不喜欢这样,也可以使用文件。 - Johannes Weiss