Bash 脚本和 C++ 之间的持久 IPC;

发布于 2024-11-30 01:04:29 字数 531 浏览 2 评论 0原文

问题: 有一个 C 应用程序,每次事件发生时都会调用 Bash 脚本。还有一个C++ 应用程序需要跟踪这些事件。 C++ 应用程序 由 select() 事件循环驱动。在 Bash 脚本C++ 应用程序 之间实现的最简单的 IPC 是什么?

C Application ---Each time calls Bash script---> Bash application ---???---> C++ Application

我想到的解决方案很少:

  1. 使用 TCP 网络套接字,但这意味着 select 必须处理侦听套接字和实际套接字的事件
  2. 使用命名管道,但一旦 bash 脚本终止,管道的另一端就是也已关闭

有没有更简单的方法可以让我在 select() 中仅使用一个文件描述符?

Problem:
There is a C application that calls a Bash script each time an event happens. And there is also a C++ application that needs to track down those events. The C++ application is driven by a select() event loop. What would be the most simplest IPC to implement between Bash script and C++ application?

C Application ---Each time calls Bash script---> Bash application ---???---> C++ Application

Few solutions that came into my mind:

  1. To use TCP networking sockets, but this would mean that select will have to handle events for both Listening and Actual sockets
  2. To use Named pipes, but once the bash script terminates then the other end of the pipe is closed as well

Is there something simpler that would allow me to use only one File Descriptor in select()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

用心笑 2024-12-07 01:04:30

您可以使用轻量级消息队列,例如 ZeroMQ。我认为你会使用它的推拉机制。您的 C 程序或 bash 脚本推送事件,而 C++ 应用程序拉取事件。 ZeroMQ 是用 C 语言编写的,但除了许多其他语言之外,还存在 C++ 和 Python 绑定。有关推拉示例,请参阅此处

You could use a lightweight messaging queue like ZeroMQ. I think you would use its PUSH-PULL mechanism. Your C program or the bash script pushes events while the C++ application pulls them. ZeroMQ is written in C but, besides many others, there exists a C++ and a Python binding for it. See here for a PUSH-PULL example.

怼怹恏 2024-12-07 01:04:29

Unix 数据报 或 UDP 套接字即可。 bash 脚本只会向该套接字发送一个数据报(您可能需要一个在该套接字上执行 sendmsg()sendto() 的帮助程序,例如 socat 或 netcat/nc)。接收方不需要接受数据报套接字的连接,一旦准备好读取,就必须有一个数据报在等待。受数据报长度限制。

Unix datagram or UDP socket would do. The bash script would just send a datagram to that socket (you may need a helper program that does sendmsg() or sendto() on that socket, such as socat or netcat/nc). The receiver does not need to accept connections for datagram sockets, once it is ready for read there must be a datagram waiting. Subject to datagram length restrictions.

梦境 2024-12-07 01:04:29

我会使用未命名的 pipe() 来完成此操作。请记住:在 UNIX 中,文件描述符在两个进程中的 fork()execve() 之后保持打开状态!因此,您可以使用 pipe() 获取一对文件描述符,然后使用 bash 的 echo >&FD 写入 fd,其中 FD 是文件描述符编号。

这非常简单、简单,并且比我想象的任何其他方法都使用更少的资源。使用 select() 没有问题,只是不要在 read() 上阻塞,这是我在示例中所做的,但在 select()pfds[0]

示例程序(生成 10 个 bash 进程,这些进程发送“hello work,我的 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
'

有效,bashs使用一个管道向父进程发送“hello world,我的pid:XXX \ n” :-)。

尽管如此,这似乎可以像演示程序所示那样工作(使用 POSIX 语义应该没问题,并且在 Linux 和 MacOS X 下进行了测试),我建议每个子进程使用一个 pipe() 。这将减少问题,并且一次运行多个子进程也是可能的。 select()epoll() (如果你有很多子进程)是你的朋友。

由于 pipe() 非常便宜,特别是与 bash! 相比,我绝对不会为多个子级使用同一个管道(正如我更新的示例现在所做的那样)。

I would do it with a unnamed pipe(). Remember: In UNIX file descriptors remain open after fork() and execve() in both processes! So you can use pipe() to get a pair of file descriptors and then write into the fd using bash's echo >&FD where FD is the file descriptor number.

That is very straight forward, easy and uses less resources than anything else I assume. The use of select() is no problem, just do not block on read() is I do in my sample but select() on pfds[0].

Sample program (spawns 10 bash processes which send 'hello work, my pid: XXX', waiting 1s between spawning the processes. The sample only uses one pipe for all the children. I changed it that way because the author asked about that. In practice, I would NOT recommend it (see the note below the sample)):

#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;
}   

sample program output:

$ 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
'

works, bashs send 'hello world, my pid: XXX\n' to parent process all using one pipe :-).

Nevertheless that seems to work as the demo program shows (should be ok using the POSIX semantics and tested under Linux and MacOS X), I would recommend using one pipe() per child process. That will lead to fewer problems and running more than one child process at a time is possible, too. select() or epoll() (if you have MANY child processes) are your friend.

Since pipe() is very cheap, in particular compared to bash!, I would definitely not use the same pipe for more than more than one child (as my updated sample now does).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文