هل يمكن أن يصنع Popen () أنابيب ثنائية الاتجاه مثل الأنابيب () + شوكة ()؟
سؤال
أنا أقوم بتنفيذ الأنابيب على نظام ملفات محاكاة في 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 () هي سلسلة تحدد وضع الإدخال/الإخراج:
- إذا كان الوضع R ، عند بدء عملية الطفل ، يجب أن يكون واصف ملف stdout_fileno هو الطرف القابل للكتابة للأنبوب ، و descriptor fileno (دفق) في عملية الاتصال ، حيث يكون الدفق هو مؤشر الدفق الذي تم إرجاعه بواسطة popen () ، يجب أن يكون الطرف القابل للقراءة للأنبوب.
- إذا كان الوضع W ، عند بدء عملية الطفل ، يجب أن يكون Descriptor STDIN_FILENO هو الطرف القابل للقراءة للأنبوب ، ويجب أن يكون واصف الملف (دفق) في عملية الاتصال ، حيث يكون الدفق هو مؤشر الدفق الذي يتم إرجاعه بواسطة popen () ، كن الطرف القابل للكتابة للأنبوب.
- إذا كان الوضع هو أي قيمة أخرى ، فإن النتيجة غير محددة.
أي رمز محمول لن يجعل أي افتراضات تتجاوز ذلك. 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