سؤال

لقد كنت بحاجة مؤخرًا إلى كتابة برنامج نصي يؤدي نظام التشغيل. شوكة () للتقسيم إلى عمليتين.تصبح العملية الفرعية عملية خادم وتقوم بتمرير البيانات مرة أخرى إلى العملية الأصلية باستخدام أنبوب تم إنشاؤه باستخدام نظام التشغيل. الأنابيب ().يقوم الطفل بإغلاق 'r' نهاية الأنبوب ويقوم الوالد بإغلاق 'w' نهاية الأنبوب، كالعادة.أقوم بتحويل المرتجعات من Pipe () إلى كائنات ملف باستخدام os.fdopen.

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

اعتقدت على الفور أن التخزين المؤقت هو المشكلة وأضفت الأنابيب. فلوش () المكالمات، ولكن هذه لم تساعد.

هل يمكن لأي شخص إلقاء بعض الضوء على سبب عدم ظهور البيانات حتى يتم إغلاق نهاية الكتابة بالكامل؟وهل هناك استراتيجية لجعل read() الاتصال غير محظور؟

هذا هو أول برنامج لي في بايثون يستخدم الأنابيب المتشعبة أو المتشعبة، لذا سامحني إذا ارتكبت خطأً بسيطًا.

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

المحلول

هل تستخدم read() دون تحديد الحجم أو التعامل مع الأنبوب كمكرر (for line in f)؟إذا كان الأمر كذلك، فمن المحتمل أن يكون هذا هو مصدر مشكلتك - تم تعريف read() للقراءة حتى نهاية الملف قبل العودة، بدلاً من مجرد قراءة ما هو متاح للقراءة.وهذا يعني أنه سيتم حظره حتى يتصل الطفل بـ Close().

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

يبدو أن التعامل مع الأنبوب باعتباره مُكرِّرًا يستخدم بعض المخزن المؤقت الإضافي أيضًا، من أجل "for line in r:"قد لا يمنحك ما تريده إذا كنت بحاجة إلى استهلاك كل سطر على الفور.قد يكون من الممكن تعطيل هذا، ولكن تحديد 0 لحجم المخزن المؤقت في fdopen لا يبدو كافيًا.

إليك بعض نماذج التعليمات البرمجية التي يجب أن تعمل:

import os, sys, time

r,w=os.pipe()
r,w=os.fdopen(r,'r',0), os.fdopen(w,'w',0)

pid = os.fork()
if pid:          # Parent
    w.close()
    while 1:
        data=r.readline()
        if not data: break
        print "parent read: " + data.strip()
else:           # Child
    r.close()
    for i in range(10):
        print >>w, "line %s" % i
        w.flush()
        time.sleep(1)

نصائح أخرى

استخدام

fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)

قبل استدعاء القراءة () حل كلتا المشكلتين.لم يعد استدعاء القراءة () محظورًا وتظهر البيانات بعد تدفق () فقط في نهاية الكتابة.

أرى أنك قد قمت بحل مشكلة حظر الإدخال/الإخراج والتخزين المؤقت.

ملاحظة إذا قررت تجربة نهج مختلف:العملية الفرعية هي المكافئة/البديلة لمصطلح fork/exec.يبدو أن هذا ليس ما تفعله:لديك فقط شوكة (وليست exec) وتبادل البيانات بين العمليتين - في هذه الحالة multiprocessing الوحدة النمطية (في Python 2.6+) ستكون مناسبة بشكل أفضل.

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

قم بتقسيم كود بايثون الخاص بك إلى قسمين منفصلين:الوالد والطفل.

يجب أن يستخدم الجزء الأصل عملية فرعية لتشغيل الجزء الطفل.

قد يحدث الشوكة والتنفيذ في مكان ما هناك - لكن لا داعي للقلق.

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