إرسال الأوامر إلى عملية فرعية من خلال توجيه الإخراج/dup2 في C

StackOverflow https://stackoverflow.com//questions/11689421

  •  12-12-2019
  •  | 
  •  

سؤال

أحاول كتابة برنامج للتحكم عن بعد لـ omxplayer على جهاز rasperry Pi الخاص بي

يمكنني جعل omxplayer يعمل بشكل جيد في عملية فرعية ولكن لا يبدو أنني قادر على جعل الأنابيب تعمل بشكل صحيح لإرسال الأوامر فعليًا إلى العملية الفرعية.

int fd_pipe[2];
pipe (fd_pipe);

while(1) {  // main accept() loop

    printf("server: got connection from %s\n", s);

    /* Attempt to fork and check for errors */
    if( (pid=fork()) == -1){
        fprintf(stderr,"Fork error. Exiting.\n");  /* something went wrong */
        exit(1);
    }

    if (pid==0) 
    { // this is the child process
        dup2(0, fd_pipe[0]);
        close(fd_pipe[1]);


        if(execl("/usr/bin/top","top",NULL) == -1){
            fprintf(stderr,"execl Error!");
            exit(1);
        }

        //try and send command here
        write(fd_pipe[0], "q", 1);

        exit(0);

    } else {

        close(new_fd);  // parent doesn't need this
        dup2(1, fd_pipe[1]);

        //try and send here too
        write(fd_pipe[0], "q", 1);
    }
}

عندما كنت أختبر البرنامج وقمت بتشغيله، أستطيع رؤية الإخراج العلوي يظهر في النافذة الطرفية ويمكنني رؤية الأمر q في النافذة ولكن يبدو أنه ينتقل إلى العملية الأصلية بدلاً من العملية الفرعية.هل أفعل شيئًا خاطئًا مع الأنابيب أم أنه من غير الممكن إرسال أوامر إلى العملية الفرعية التي تم إنشاؤها؟

حاولت تغيير عبارة dup2 التابعة للنسخ من الأنبوب إلى stdin

        { // this is the child process
        dup2(fd_pipe[0], 0);

ولكن بعد ذلك يفشل الجزء العلوي في البدء برسالة الحصول على tty الفاشلة

هل كانت مفيدة؟

المحلول

للتفاعل مع برنامج يتوقع أن يكون قادرًا على التعامل مع الوحدة الطرفية، يجب عليك استخدام tty زائف.هذا سوف يتجنب failed tty get خطأ.

/*...*/
#include <pty.h>

void do_child () {
    if (execlp("top", "top", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOTREACHED */
}

void do_parent (int fd, pid_t p) {
    sleep(5);
    char r;
    write(fd, "q", 1);
    while (read(fd, &r, 1) > 0) { write(1, &r, 1); }
    waitpid(p, 0, 0);
    close(fd);
}

int main () {
    int fd;
    pid_t p = forkpty(&fd, 0, 0, 0);
    switch (p) {
    case 0:  do_child();
             /* NOTREACHED */
    case -1: perror("forkpty");
             exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(fd, p);
    return 0;
}

لاحظ أن forkpty ليست POSIX، ولكنها واجهة متوفرة على نكهات BSD وLinux من UNIX.

يمكنك التنفيذ top في الوضع الدفعي، لكن لا يمكنك استخدام أمر للإنهاء.عليك أن تقتله.

void do_child () {
    if (execlp("top", "top", "-b", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOT REACHED */
}

void do_parent (pid_t p) {
    sleep(5);
    if (kill(p, SIGINT) < 0) {
        perror("kill");
        exit(EXIT_FAILURE);
    }
    waitpid(p, 0, 0);
}

int main () {
    pid_t p;
    switch ((p = fork())) {
    case 0:  do_child();
    case -1: perror("fork"); exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(p);
    return 0;
}

على الرغم من أن التشغيل في الوضع الدفعي سيسمح لك بفتح العملية من خلال استدعاء أبسط (مثل popen كما يقترح Mux)، ما لم يُرجع هذا الاستدعاء معرف العملية الخاص بالطفل، فلن تتمكن من قتله (بدون تنفيذ pkill, ، أو ابحث في جدول العمليات للعثور على عملية الطفل المناسبة للقتل).

أعتقد أن لديك بعض الارتباك حول كيفية الاستخدام dup2.تستخدم الصفحة اليدوية المصطلحات oldfd و newfd, ، وهذا يعني ذلك oldfd سيصبح newfd.للتوضيح، إليك برنامج بسيط يعيد توجيه stdout وstderr إلى ملف سجل، ويستدعي دالة، ثم يستعيد stdout وstderr بعد ذلك.

void do_something () {
    fputs("Error message\n", stderr);
    puts("This is regular output.");
    fputs("Error message\n", stderr);
    puts("This is regular output.");
}

int main () {
    int fd = creat("/tmp/output.log", 0664);
    int outfd = dup(fileno(stdout));
    int errfd = dup(fileno(stderr));
    fflush(stdout);
    fflush(stderr);
    dup2(fd, fileno(stdout));
    dup2(fd, fileno(stderr));
    setlinebuf(stdout);

    do_something();

    fflush(stdout);
    fflush(stderr);
    dup2(outfd, fileno(stdout));
    dup2(errfd, fileno(stderr));
    close(outfd);
    close(errfd);

    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");
    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");

    return 0;
}

كما أشار Mux، فإن برنامجك يكتب إلى الطرف الخاطئ من الأنبوب.ال pipe يقوم الأمر بإرجاع زوج أحادي الاتجاه من واصفات الملفات، حيث يتم كتابة أي شيء على الإطلاق fd_pipe[1] يمكن القراءة منها fd_pipe[0].

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top