Постоянный IPC между Bash-скриптом и C ++ - PullRequest
5 голосов
/ 16 августа 2011

Проблема: существует приложение C , которое вызывает Bash-скрипт каждый раз, когда происходит событие.И есть также C ++ приложение , которое должно отслеживать эти события.Приложение C ++ управляется циклом событий select ().Какой самый простой IPC можно реализовать между Bash-скриптом и C ++ приложением ?

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

Несколько решений, которые мне приходили в голову:

  1. Чтобы использовать сетевые сокеты TCP, но это будет означать, что select должен будет обрабатывать события как для прослушивающих, так и для фактических сокетов
  2. Для использования именованных каналов, но как только скрипт bash завершает работу, тогда другой конецканал также закрыт

Есть ли что-то более простое, что позволило бы мне использовать только один дескриптор файла в select ()?

Ответы [ 3 ]

3 голосов
/ 16 августа 2011

Я бы сделал это с неназванным pipe(). Помните: в UNIX файловые дескрипторы остаются открытыми после fork() и execve() в обоих процессах! Таким образом, вы можете использовать pipe(), чтобы получить пару файловых дескрипторов, а затем записать в fd, используя bash's echo >&FD, где FD - номер дескриптора файла.

Это очень просто, легко и использует меньше ресурсов, чем я полагаю. Использование select() не проблема, просто не блокируйте на read(), как я делаю в моем примере, но select() на pfds[0].

Пример программы (порождает 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
'

работает, bashs отправляет 'hello world, my pid: XXX \ n' в родительский процесс, используя один канал: -).

Тем не менее, похоже, что это работает, как показывает демонстрационная программа (все должно быть в порядке с использованием семантики POSIX и протестировано в Linux и MacOS X), я бы рекомендовал использовать один pipe() для дочернего процесса. Это приведет к меньшему количеству проблем, и возможно одновременное выполнение более одного дочернего процесса. select() или epoll() (если у вас МНОГИЕ дочерние процессы) - ваш друг.

Поскольку pipe() очень дешев, особенно по сравнению с bash !, я бы точно не использовал одну и ту же трубу для более чем одного ребенка (как сейчас делает мой обновленный образец).

3 голосов
/ 16 августа 2011

дейтаграмма Unix или UDP-сокет.Сценарий bash просто отправит дейтаграмму в этот сокет (вам может понадобиться вспомогательная программа, которая выполняет sendmsg() или sendto() для этого сокета, например, socat или netcat / nc).Приемнику не нужно принимать соединения для сокетов дейтаграмм, как только он будет готов к чтению, должна ожидать датаграмма.С учетом ограничений по длине дейтаграмм.

1 голос
/ 16 августа 2011

Вы можете использовать легкую очередь сообщений, такую ​​как ZeroMQ .Я думаю, вы бы использовали его механизм PUSH-PULL.Ваша программа C или скрипт bash отправляют события, в то время как приложение C ++ их извлекает.ZeroMQ написан на C, но, помимо многих других, существует C ++ и привязка Python для него.См. здесь для примера PUSH-PULL.

...