سؤال

أنا مهتم في الكتابة منفصلة وحدات البرنامج التي تعمل مستقلة عن المواضيع التي يمكنني ربط جنبا إلى جنب مع أنابيب.الدافع سيكون بإمكاني كتابة و اختبار كل وحدة بشكل مستقل تماما ، وربما حتى يكتب لهم في لغات مختلفة ، أو تشغيل وحدات مختلفة على أجهزة مختلفة.وهناك مجموعة متنوعة واسعة من الاحتمالات هنا.لقد استخدمت الأنابيب لفترة من الوقت, ولكن أنا غير مألوف مع الفروق الدقيقة في سلوكها.

  • يبدو أن المتلقي سوف كتلة انتظار الإدخال ، مما كنت أتوقع ، ولكن سيتم إرسال نهاية كتلة في بعض الأحيان في انتظار شخص ما أن تقرأ من تيار ؟
  • إذا أنا أكتب eof إلى تيار يمكن أن أستمر في مواصلة الكتابة إلى أن تيار حتى إغلاق هذا ؟
  • هل توجد فروق في السلوك المسماة وغير المسماة الأنابيب ؟
  • لا يهم أي نهاية الأنابيب أفتح الأولى مع اسمه الأنابيب ؟
  • هو سلوك أنابيب متناسقة بين مختلف أنظمة لينكس ؟
  • هل سلوك الأنابيب تعتمد على شل أنا باستخدام أو الطريقة لقد تكوينه ؟
  • هل هناك أي أسئلة أخرى لا ينبغي أن يسأل أو القضايا يجب أن تكون على علم إذا كنت ترغب في استخدام أنابيب في هذا الطريق ؟
هل كانت مفيدة؟

المحلول

نجاح باهر, وهذا هو الكثير من الأسئلة.دعونا نرى إذا كنت يمكن أن تغطي كل شيء...

يبدو أن المتلقي سوف كتلة انتظار الإدخال ، نتوقع

تتوقع بشكل صحيح الفعلية 'قراءة' دعوة كتلة حتى شيء هناك.ومع ذلك, أعتقد أن هناك بعض ج وظائف من شأنها أن تسمح لك "نظرة خاطفة" في ما (وكم) ينتظر في الأنابيب.للأسف, أنا لا أتذكر إذا كان هذا القطع أيضا.

سيتم إرسال نهاية كتلة في بعض الأحيان في انتظار شخص ما أن يقرأ من تيار

لا إرسال لا ينبغي أبدا كتلة.التفكير في العواقب إذا كانت هذه الأنابيب عبر الشبكة إلى جهاز كمبيوتر آخر.هل تريد الانتظار (من خلال وربما استتار عالية) على الكمبيوتر الآخر للرد أنه تلقى ذلك ؟ الآن هذا هو حالة مختلفة إذا كان القارئ التعامل معها من الوجهة كانت مغلقة.في هذه الحالة, يجب أن يكون لديك بعض التحقق من الخطأ التعامل مع ذلك.

إذا أنا أكتب eof إلى تيار يمكنني الحفاظ على مواصلة الكتابة إلى أن تيار حتى إغلاقه

أعتقد أن هذا يعتمد على لغة ما كنت تستخدم من الأنابيب.في C, أنا أقول لا.في قذيفة لينكس, أقول نعم.شخص آخر مع المزيد من الخبرة أن الإجابة على هذا.

هل توجد فروق في السلوك المسماة وغير المسماة الأنابيب ؟ بقدر ما أعرف ، نعم.ومع ذلك, ليس لدي الكثير من الخبرة مع اسمه مقابل لم يكشف عن اسمه.أعتقد أن الفرق هو:

  • اتجاه واحد مقابل الاتصال ثنائي الاتجاه
  • القراءة والكتابة إلى "في" و "الخروج" تيارات الموضوع

لا يهم أي نهاية الأنبوب أنا الأولى مفتوحة مع اسمه الأنابيب ؟

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

هو سلوك أنابيب ثابت بين مختلف أنظمة لينكس ؟

مرة أخرى هذا يعتمد على لغة ما ، ولكن بشكل عام نعم.سمعت من POSIX?هذا القياسية (على الأقل بالنسبة لينكس, ويندوز يفعل الشيء نفسه).

هل سلوك الأنابيب تعتمد على قذيفة أنا باستخدام أو الطريقة التي كنت تكوينه ؟

هذا هو الدخول في أكثر من منطقة رمادية.الجواب يجب أن أي منذ قذيفة ينبغي أن تكون أساسا مما يجعل نظام المكالمات.ولكن كل شيء حتى تلك اللحظة هو لقمة سائغة.

هل هناك أي أسئلة أخرى يجب أن أن يسأل

الأسئلة كنت قد طلبت يدل على أن لديك فهم النظام.الحفاظ على البحث و التركيز على أي مستوى أنت ذاهب إلى العمل على (قذيفة ، ج ، وهلم جرا).عليك أن تتعلم الكثير أكثر من مجرد محاولة ذلك على الرغم من.

نصائح أخرى

كل هذا يعتمد على نظام يشبه UNIX؛لست على دراية بالسلوك المحدد للإصدارات الحديثة من Windows.

يبدو أن الطرف المتلقي سيمنع انتظار الإدخال، وهو ما أتوقعه، ولكن هل سيمنع الطرف المرسل أحيانًا أن يقرأ شخص ما من الدفق؟

نعم، على الرغم من أن هذا قد لا يحدث كثيرًا على الجهاز الحديث.يحتوي الأنبوب على مخزن مؤقت متوسط يستطيع يحتمل أن تملأ.إذا حدث ذلك، فسيتم حظر جانب الكتابة من الأنبوب بالفعل.لكن إذا فكرت في الأمر، فستجد أنه لا يوجد الكثير من الملفات الكبيرة بما يكفي للمخاطرة بذلك.

إذا كتبت eof إلى الدفق، فهل يمكنني الاستمرار في الكتابة إلى هذا الدفق حتى أقوم بإغلاقه؟

أم، تقصد مثل CTRL-D، 0x04؟بالتأكيد، طالما تم إعداد الدفق بهذه الطريقة.بمعنى.

506 # cat | od -c
abc
^D
efg
0000000    a   b   c  \n 004  \n   e   f   g  \n                        
0000012

هل هناك اختلافات في سلوك توجيهات الإخراج المسماة وغير المسماة؟

نعم، لكنها دقيقة وتعتمد على التنفيذ.أكبرها هو أنه يمكنك الكتابة إلى أنبوب مسمى قبل تشغيل الطرف الآخر؛باستخدام الأنابيب غير المسماة، تتم مشاركة واصفات الملفات أثناء عملية fork/exec، لذلك لا توجد طريقة للوصول إلى المخزن المؤقت العابر دون بدء العمليات.

هل يهم أي طرف من الأنبوب أفتحه أولاً باستخدام الأنابيب المسماة؟

لا.

هل سلوك الأنابيب متسق بين أنظمة لينكس المختلفة؟

في حدود المعقول، نعم.قد تختلف أحجام المخزن المؤقت وما إلى ذلك.

هل يعتمد سلوك الأنابيب على الصدفة التي أستخدمها أو على الطريقة التي قمت بتكوينها؟

لا.عندما تقوم بإنشاء أنبوب، تحت الأغطية، ما يحدث هو أن العملية الأم (الصدفة) تنشئ أنبوبًا يحتوي على زوج من واصفات الملفات، ثم يقوم بتنفيذ شوكة مثل هذا الكود الزائف:

الأبوين:

create pipe, returning two file descriptors, call them fd[0] and fd[1]
fork write-side process
fork read-side process

جانب الكتابة:

close fd[0]
connect fd[1] to stdout
exec writer program

جانب القراءة:

close fd[1]
connect fd[0] to stdin
exec reader program

هل هناك أي أسئلة أخرى يجب أن أطرحها أو مشكلات يجب أن أكون على دراية بها إذا كنت أرغب في استخدام الأنابيب بهذه الطريقة؟

هل كل ما تريد القيام به سيتم وضعه حقًا في سطر مثل هذا؟إذا لم يكن الأمر كذلك، فقد ترغب في التفكير في بنية أكثر عمومية.لكن فكرة أن وجود الكثير من العمليات المنفصلة التي تتفاعل من خلال الواجهة "الضيقة" للأنبوب أمر مرغوب فيه هي فكرة جيدة.

[محدث:لقد تم عكس مؤشرات واصف الملف في البداية.إنهم على حق الآن، انظر man 2 pipe.]

وكما أشار داشوغون وتشارلي مارتن، فإن هذا سؤال كبير.بعض أجزاء إجاباتهم غير دقيقة، لذلك سأجيب أيضًا.

أنا مهتم بكتابة وحدات برامج منفصلة تعمل كخيوط مستقلة يمكنني ربطها ببعضها البعض باستخدام الأنابيب.

كن حذرًا من محاولة استخدام الأنابيب كآلية اتصال بين سلاسل عمليات واحدة.نظرًا لأنه سيكون لديك طرفي القراءة والكتابة للأنبوب مفتوحًا في عملية واحدة، فلن تحصل أبدًا على إشارة EOF (صفر بايت).

إذا كنت تشير حقًا إلى العمليات، فهذا هو أساس منهج يونكس الكلاسيكي لبناء الأدوات.العديد من برامج يونكس القياسية عبارة عن مرشحات تقرأ من المدخلات القياسية، وتحولها بطريقة ما، وتكتب النتيجة إلى الإخراج القياسي.على سبيل المثال، tr, sort, grep, ، و cat كلها مرشحات، على سبيل المثال لا الحصر.يعد هذا نموذجًا ممتازًا يجب اتباعه عندما تسمح البيانات التي تعالجها بذلك.ليست كل عمليات معالجة البيانات تؤدي إلى هذا النهج، ولكن هناك الكثير منها.

سيكون الدافع هو أن أتمكن من كتابة واختبار كل وحدة بشكل مستقل تمامًا، وربما حتى كتابتها بلغات مختلفة، أو تشغيل الوحدات المختلفة على أجهزة مختلفة.

نقاط جيدة.انتبه إلى أنه لا توجد آلية توصيل بين الأجهزة، على الرغم من أنه يمكنك الاقتراب منها باستخدام برامج مثل rsh أو (أفضل) ssh.ومع ذلك، داخليًا، قد تقرأ هذه البرامج البيانات المحلية من الأنابيب وترسل تلك البيانات إلى الأجهزة البعيدة، ولكنها تتواصل بين الأجهزة عبر المقابس، وليس باستخدام الأنابيب.

هناك مجموعة واسعة من الاحتمالات هنا.لقد استخدمت الأنابيب لفترة من الوقت، ولكنني لست على دراية بالفروق الدقيقة في سلوكها.

نعم؛طرح الأسئلة هو إحدى الطرق (الجيدة) للتعلم.التجربة شيء آخر بالطبع.

يبدو أن الطرف المتلقي سيمنع انتظار الإدخال، وهو ما أتوقعه، ولكن هل سيمنع الطرف المرسل أحيانًا أن يقرأ شخص ما من الدفق؟

نعم.هناك حد لحجم المخزن المؤقت للأنبوب.تقليديًا، كان هذا صغيرًا جدًا - 4096 أو 5120 كانت قيمًا مشتركة.قد تجد أن Linux الحديث يستخدم قيمة أكبر.يمكنك استخدام fpathconf() و_PC_PIPE_BUF لمعرفة حجم المخزن المؤقت للأنبوب.يتطلب POSIX فقط أن يكون المخزن المؤقت 512 (أي _POSIX_PIPE_BUF هو 512).

إذا كتبت eof إلى الدفق، فهل يمكنني الاستمرار في الكتابة إلى هذا الدفق حتى أقوم بإغلاقه؟

من الناحية الفنية، لا توجد طريقة لكتابة EOF إلى الدفق؛قمت بإغلاق واصف توجيه الإخراج للإشارة إلى EOF.إذا كنت تفكر في control-D أو control-Z كحرف EOF، فهذه مجرد أحرف عادية فيما يتعلق بالأنابيب - لها تأثير مثل EOF فقط عند كتابتها على محطة تعمل في الوضع الأساسي (المطبوخ ، أو عادي).

هل هناك اختلافات في سلوك توجيهات الإخراج المسماة وغير المسماة؟

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

هل يهم أي طرف من الأنبوب أفتحه أولاً باستخدام الأنابيب المسماة؟

لا.سيتم (عادة) حظر العملية الأولى لفتح FIFO حتى تكون هناك عملية مع فتح الطرف الآخر.إذا قمت بفتحه للقراءة والكتابة (غير تقليدي ولكن ممكن) فلن يتم حظرك؛إذا استخدمت علامة O_NONBLOCK، فلن يتم حظرك.

هل سلوك الأنابيب متسق بين أنظمة Linux المختلفة؟

نعم.لم أسمع أو واجهت أي مشاكل مع الأنابيب في أي من الأنظمة التي استخدمتها فيها.

هل يعتمد سلوك الأنابيب على الصدفة التي أستخدمها أو على الطريقة التي قمت بتكوينها؟

لا:الأنابيب وFIFOs مستقلة عن الغلاف الذي تستخدمه.

هل هناك أي أسئلة أخرى يجب أن أطرحها أو مشكلات يجب أن أكون على دراية بها إذا كنت أرغب في استخدام الأنابيب بهذه الطريقة؟

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


لقد لاحظت أعلاه وفي التعليقات على الإجابات الأخرى أن المخازن المؤقتة للأنابيب تقتصر بشكل كلاسيكي على أحجام صغيرة جدًا.علقCharlie Martin بأن بعض إصدارات Unix تحتوي على مخازن مؤقتة ديناميكية للأنابيب ويمكن أن تكون كبيرة جدًا.

لست متأكدا من تلك التي لديه في الاعتبار.لقد استخدمت برنامج الاختبار الذي يتبع أنظمة Solaris وAIX وHP-UX وMacOS X وLinux وCygwin/Windows XP (النتائج أدناه):

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static const char *arg0;

static void err_syserr(char *str)
{
    int errnum = errno;
    fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
    exit(1);
}

int main(int argc, char **argv)
{
    int pd[2];
    pid_t kid;
    size_t i = 0;
    char buffer[2] = "a";
    int flags;

    arg0 = argv[0];

    if (pipe(pd) != 0)
        err_syserr("pipe() failed");
    if ((kid = fork()) < 0)
        err_syserr("fork() failed");
    else if (kid == 0)
    {
        close(pd[1]);
        pause();
    }
    /* else */
    close(pd[0]);
    if (fcntl(pd[1], F_GETFL, &flags) == -1)
        err_syserr("fcntl(F_GETFL) failed");
    flags |= O_NONBLOCK;
    if (fcntl(pd[1], F_SETFL, &flags) == -1)
        err_syserr("fcntl(F_SETFL) failed");
    while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
    {
        putchar('.');
        if (++i % 50 ==  0)
            printf("%u\n", (unsigned)i);
    }
    if (i % 50 !=  0)
        printf("%u\n", (unsigned)i);
    kill(kid, SIGINT);
    return 0;
}

سأكون فضوليًا للحصول على نتائج إضافية من منصات أخرى.وهنا الأحجام التي وجدتها.جميع النتائج كانت أكبر مما توقعت، يجب أن أعترف، ولكن ربما نتناقش أنا وتشارلي حول معنى "كبير جدًا" عندما يتعلق الأمر بأحجام المخزن المؤقت.

  • 8196-HP-UX 11.23 لـ IA-64 (فشل fcntl(F_SETFL))
  • 16384 - سولاريس 10
  • 16384 - MacOS X 10.5 (لم يعمل O_NONBLOCK، رغم أن fcntl(F_SETFL) لم يفشل)
  • 32768-إيكس 5.3
  • 65536 - Cygwin / Windows XP (لم يعمل O_NONBLOCK، رغم أن fcntl(F_SETFL) لم يفشل)
  • 65536 - SuSE Linux 10 (و CentOS) (فشل fcntl(F_SETFL))

إحدى النقاط الواضحة من هذه الاختبارات هي أن O_NONBLOCK يعمل مع الأنابيب على بعض الأنظمة الأساسية وليس على منصات أخرى.

يقوم البرنامج بإنشاء الأنابيب، والشوك.يغلق الطفل طرف الكتابة في الأنبوب، ثم ينام حتى يحصل على إشارة - وهذا ما يفعله الإيقاف المؤقت ().يقوم الأصل بعد ذلك بإغلاق نهاية قراءة الأنبوب، ويقوم بتعيين العلامات على واصف الكتابة بحيث لا يمنع محاولة الكتابة على أنبوب كامل.ثم يتكرر، ويكتب حرفًا واحدًا في كل مرة، ويطبع نقطة لكل حرف مكتوب، وعددًا وسطرًا جديدًا كل 50 حرفًا.عندما يكتشف مشكلة في الكتابة (المخزن المؤقت ممتلئ، لأن الطفل لا يقرأ شيئًا)، فإنه يوقف الحلقة، ويكتب العدد النهائي، ويقتل الطفل.

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