هل يمكن أن يصنع Popen () أنابيب ثنائية الاتجاه مثل الأنابيب () + شوكة ()؟

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

  •  28-09-2019
  •  | 
  •  

سؤال

أنا أقوم بتنفيذ الأنابيب على نظام ملفات محاكاة في C ++ (مع في الغالب C). يحتاج إلى تشغيل الأوامر في قذيفة المضيف ولكن تنفيذ الأنابيب نفسها على نظام الملفات المحاكاة.

يمكنني تحقيق ذلك مع pipe(), fork(), ، و system() مكالمات النظام ، لكنني أفضل استخدامها popen() (الذي يتعامل مع إنشاء أنبوب ، وتجاوز عملية ، وتمرير أمر إلى القشرة). قد لا يكون هذا ممكنًا لأنني (على ما أظن) يجب أن أكون قادرًا على الكتابة من العملية الوالدية للأنبوب ، وقراءة نهاية عملية الطفل ، وكتابة الإخراج مرة أخرى من الطفل ، وقراءة هذا الإخراج من الوالد. صفحة الرجل ل popen() يقول على نظامي أن الأنبوب ثنائي الاتجاه ممكن ، ولكن يجب تشغيل الكود الخاص بي على نظام مع إصدار أقدم يدعم أنابيب أحادية الاتجاه فقط.

مع المكالمات المنفصلة أعلاه ، يمكنني فتح/إغلاق الأنابيب لتحقيق ذلك. هل هذا ممكن مع popen()?

للحصول على مثال تافهة للتشغيل ls -l | grep .txt | grep cmds أنا بحاجة لــ:

  • افتح أنبوبًا وعملية للتشغيل ls -l على المضيف اقرأ الإخراج مرة أخرى
  • أنبوب إخراج ls -l العودة إلى جهاز المحاكاة الخاص بي
  • افتح أنبوبًا وعملية للتشغيل grep .txt على المضيف على إخراج الأنابيب من ls -l
  • الأنابيب إخراج هذا العودة إلى المحاكاة (عالقة هنا)
  • افتح أنبوبًا وعملية للتشغيل grep cmds على المضيف على إخراج الأنابيب من grep .txt
  • أنبوب إخراج هذا العودة إلى المحاكاة وطباعته

رجل بوب

من Mac OS X:

ال popen() الوظيفة "تفتح" عملية عن طريق إنشاء أنبوب ثنائي الاتجاه ، ودعم ، واستدعاء القشرة. أي تدفقات تم فتحها من قبل السابق popen()يتم إغلاق المكالمات في عملية الوالدين في عملية الطفل الجديدة. تاريخيا ، popen() تم تنفيذها مع أنبوب أحادي الاتجاه. وبالتالي ، العديد من تطبيقات popen() اسمح فقط وسيطة الوضع بتحديد القراءة أو الكتابة ، وليس كلاهما. لان popen() يتم تنفيذها الآن باستخدام أنبوب ثنائي الاتجاه ، قد تطلب وسيطة الوضع تدفق بيانات ثنائية الاتجاه. وسيطة الوضع هي مؤشر لسلسلة منتهية خالية يجب أن تكون "R" للقراءة أو "W" للكتابة أو "R+" للقراءة والكتابة.

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

المحلول

يبدو أنك أجبت على سؤالك الخاص. إذا احتاج الكود الخاص بك إلى العمل على نظام أقدم لا يدعمه popen فتح أنابيب ثنائية الاتجاه ، فلن تتمكن من استخدامها popen (على الأقل ليس الشخص الذي تم توفيره).

سيكون السؤال الحقيقي حول القدرات الدقيقة للأنظمة القديمة المعنية. على وجه الخصوص ، يفعل pipe دعم إنشاء أنابيب ثنائية الاتجاه؟ إذا كان لديهم pipe يمكن أن يخلق أنبوب ثنائي الاتجاه ، ولكن popen هذا لا ، ثم أكتب الدفق الرئيسي للرمز المطلوب استخدامه popen مع أنبوب ثنائي الاتجاه ، وتزويد تنفيذ popen يمكن أن تستخدم أنبوب ثنائي الاتجاه يتم تجميعه في استخدام عند الحاجة.

إذا كنت بحاجة إلى دعم الأنظمة القديمة بما يكفي pipe يدعم الأنابيب أحادية الاتجاه فقط ، فأنت عالق كثيرًا في استخدامه pipe, fork, dup2, ، وما إلى ذلك ، بنفسك. ربما ما زلت ألف هذا في وظيفة تعمل تقريبيا مثل نسخة حديثة من popen, ، ولكن بدلاً من إرجاع مقبض ملف واحد ، يملأ بنية صغيرة بمقابض ملفين ، أحدهما للطفل stdin, والآخر للطفل stdout.

نصائح أخرى

أود أن أقترح كتابة وظيفتك الخاصة للقيام بالأنابيب/forking/system-ing لك. يمكن أن تفرخ الوظيفة من العملية وإرجاع واصفات الملفات/الكتابة ، كما في ...

typedef void pfunc_t (int rfd, int wfd);

pid_t pcreate(int fds[2], pfunc_t pfunc) {
    /* Spawn a process from pfunc, returning it's pid. The fds array passed will
     * be filled with two descriptors: fds[0] will read from the child process,
     * and fds[1] will write to it.
     * Similarly, the child process will receive a reading/writing fd set (in
     * that same order) as arguments.
    */
    pid_t pid;
    int pipes[4];

    /* Warning: I'm not handling possible errors in pipe/fork */

    pipe(&pipes[0]); /* Parent read/child write pipe */
    pipe(&pipes[2]); /* Child read/parent write pipe */

    if ((pid = fork()) > 0) {
        /* Parent process */
        fds[0] = pipes[0];
        fds[1] = pipes[3];

        close(pipes[1]);
        close(pipes[2]);

        return pid;

    } else {
        close(pipes[0]);
        close(pipes[3]);

        pfunc(pipes[2], pipes[1]);

        exit(0);
    }

    return -1; /* ? */
}

يمكنك إضافة أي وظيفة تحتاجها هناك.

ينص Posix على أن popen() المكالمة غير مصممة لتوفير اتصال ثنائي الاتجاه:

وسيطة الوضع إلى popen () هي سلسلة تحدد وضع الإدخال/الإخراج:

  1. إذا كان الوضع R ، عند بدء عملية الطفل ، يجب أن يكون واصف ملف stdout_fileno هو الطرف القابل للكتابة للأنبوب ، و descriptor fileno (دفق) في عملية الاتصال ، حيث يكون الدفق هو مؤشر الدفق الذي تم إرجاعه بواسطة popen () ، يجب أن يكون الطرف القابل للقراءة للأنبوب.
  2. إذا كان الوضع W ، عند بدء عملية الطفل ، يجب أن يكون Descriptor STDIN_FILENO هو الطرف القابل للقراءة للأنبوب ، ويجب أن يكون واصف الملف (دفق) في عملية الاتصال ، حيث يكون الدفق هو مؤشر الدفق الذي يتم إرجاعه بواسطة popen () ، كن الطرف القابل للكتابة للأنبوب.
  3. إذا كان الوضع هو أي قيمة أخرى ، فإن النتيجة غير محددة.

أي رمز محمول لن يجعل أي افتراضات تتجاوز ذلك. BSD popen() يشبه ما يصفه سؤالك.

بالإضافة إلى ذلك ، تختلف الأنابيب عن المقابس وكل واصف ملف الأنابيب أحادي الاتجاه. سيكون عليك إنشاء أنبوبين ، واحد تم تكوينه لكل اتجاه.

في واحد من netresolve الخلفية أنا أتحدث إلى نص النصي ، وبالتالي أحتاج إلى الكتابة إليه stdin وقراءة من stdout. تنفذ الوظيفة التالية أمرًا مع stdin وإعادة توجيه stdout إلى أنبوب. يمكنك استخدامه وتكييفه مع رغبتك.

static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], 0);
        dup2(p2[1], 1);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#l46

(استخدمت نفس الرمز في القراءة والكتابة في وقت واحد)

لا حاجة لإنشاء أنبوبين وإهدار فريد في كل عملية. فقط استخدم مقبس بدلاً من ذلك. https://stackoverflow.com/a/25177958/894520

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