التابع select‎ الخاص بالصنف IO في روبي

من موسوعة حسوب

يستدعي التابع select استدعاء النظام select(2)‎. ويراقب المصفوفة المعطاة المكونة من مجاري د/خ (كائنات IO)، وينتظر حتى يكون واحد أو أكثر من تلك المجاري جاهزا للقراءة، وجاهزا للكتابة، ويكون له استثناءات معلقة (pending exceptions) تواليًا، ثم يعيد مصفوفة مؤلفة من مصفوفات مكونة من تلك المجاري. ستعاد القيمة nil إذا تم إعطاء قيمة للوسيط الاختياري timeout (انظر فقرة البنية العامة)، ولم يكن أي من مجاري د/خ جاهزاً خلال timeout ثانية.

يراقب التابع select المخزن المؤقت (buffer) لمجرى د/خ لاختبار قابليته للقراءة. إذا لم يكن المخزن المؤقت للمجرى فارغًا، فسيُبلغ التابع select على الفور عن جاهزية القراءة. هذه المراقبة تشمل فقط مجاري د/خ. ولا تشمل الكائنات شبيهة IO، مثل OpenSSL::SSL::SSLSocket.

أفضل طريقة لاستخدام التابع select هي باستدعائه بعد التوابع غير المُعطّلة (nonblocking methods)، مثل read_nonblock و write_nonblock، وغيرهما. تطلق تلك التوابع استثناء، والذي يُوسَّع بواسطة IO::WaitReadable أو IO::WaitWritable.

تُبلغالوحدات عن كيف ينبغي للمُستدعي (caller) الانتظار مع التابع select. في حال إطلاق IO::WaitReadable، فسيكون على المُستدعي أن ينتظر لأجل القراءة. وفي حال إطلاق IO::WaitWritable، فيجب على المُستدعي أن ينتظر لأجل الكتابة.

لذلك، يمكن محاكاة حظر القراءة (readpartial) باستخدام التابعين read_nonblock و select على النحو التالي:

begin
  result = io_like.read_nonblock(maxlen)
rescue IO::WaitReadable
  IO.select([io_like])
  retry
rescue IO::WaitWritable
  IO.select(nil, [io_like])
  retry
end

يُفضل الجمع بين التوابع غير المُعطلة و التابع select عند العمل على الكائنات شبيهة الصنفIO، مثل OpenSSL::SSL::SSLSocket. لأنها تُعرّف التابع to_io الذي يعيد مجرى د/خ الأساسي. فالتابع to_io يستدعى من قبل selectلأجل الحصول على واصف الملف (file descriptor) المُنتظر.

هذا يعني أن الإبلاغ عن قابلية القراءة من طرف select لا تعني إمكانية القراءة من الكائن OpenSSL::SSL::SSLSocket.

على الأرجح أن OpenSSL::SSL::SSLSocket تخزّن مؤقتا بعض البيانات. لكن لا يرى التابع select المخزن المؤقت (buffer). لذلك يمكن للتابع select أن يُعطل (block) عندما لا يفعل OpenSSL::SSL::SSLSocket#readpartial.

لكن توجد حالات أكثر تعقيدًا.

SSL هو بروتوكول أمني، وهو عبارة عن تسلسل للسجلات (records)، والتي تتكون من عدة بايتات. يرسل الجانب البعيد من SSL سجلًا جزئيًا، فيقوم select بالابلاغ عن إمكانية القراءة، ولكن لا يمكن لـ OpenSSL::SSL::SSLSocket فك ترميز البايت، فيقوم OpenSSL::SSL::SSLSocket#readpartial بالتعطيل.

يمكن أيضًا للجانب البعيد أن يطلب إعادة التفاوض SSL renegotiation، والذي سيُجبر محرك SSL المحلي على كتابة بعض البيانات. هذا يعني أنّ OpenSSL::SSL::SSLSocket#readpartial قد ينفذ نظام الاستدعاء write وأنه قد يُعطّل. في مثل هذه الحالة، يطلِق OpenSSL::SSL::SSLSocket#read_nonblock الاستثناء IO::WaitWritable بدلاً من التعطيل. لذلك، سيكون على المتصِل الانتظار إلى حين جاهزية الكتابة كما هو موضح في المثل أعلاه.

الجمع بين التوابع غير المُعطلة والتابع IO.select مفيد عندما تقوم عدة عمليات بالقراءة من مجرى معيّن. كما هو الحال مثلا في الطرفية tty، وأنابيب المقابس (pipe socket)ـ

وأخيراً، لا يمكن لمطوري نواة لينكس (Linux kernel developers) أن يضمنوا أنّ قابلية قراءة select(2)‎ تعني قابلية قراءة read(2)‎ الموالية حتى ولو في نفس العملية. انظر توثيق select(2)‎ في نظام جنو / لينكس.

استدعاء التابع select قبل readpartial مقبول. لكنه ليس الطريقة الأفضل لاستخدام select.

لا يُظهر الإبلاغ عن قابلية الكتابة من طرف select (2)‎ عدد البايتات القابلة للكتابة. سيقوم التابع write بالتعطيل (blocks) إلى حين كتابة السلسلة النصية المعطاة كاملة. لذلك، يمكن أن يقوم التعبير write(two or more bytes)‎ بالتعطيل بعد الابلاغ عن قابلية الكتابة من قبل select.

إن أردت تجنب التعطيل، فعليك استخدام write_nonblock.

يوضح المثال أدناه كيف يمكن محاكاة write باستخدام التابعين write_nonblock و select،

while 0 < string.bytesize
  begin
    written = io_like.write_nonblock(string)
  rescue IO::WaitReadable
    IO.select([io_like])
    retry
  rescue IO::WaitWritable
    IO.select(nil, [io_like])
    retry
  end
  string = string.byteslice(written..-1)
end

يجب معالجة IO::WaitReadable لأجل إعادة التفاوض SSL في OpenSSL::SSL::SSLSocket.

البنية العامة

select(read_array [, write_array [, error_array [, timeout]]])  array or nil

المعاملات

read_array‎

مصفوفة من مجاري د/خ تنتظر جاهزية للقراءة

write_array‎

مصفوفة من مجاري د/خ تنتظر جاهزية للكتابة

error_array‎

مصفوفة من مجاري د/خ تنتظر الاستثناءات

timeout‎

قيمة عددية تمثل عدد الثواني.

القيمة المُعادة

يعيد التابع select مصفوفة مؤلفة من مصفوفات مكونة من مجاري د/خ. أو يعيد القيمة nil إذا تم إعطاء قيمة للوسيط الاختياري timeout ، ولم يكن أي من مجاري د/خ جاهزاً خلال timeout ثانية.

أمثلة

مثال على استخدام التابع select‎:

rp, wp = IO.pipe
mesg = "ping "
100.times {
  # IO.select follows IO#read.  Not the best way to use IO.select.
  rs, ws, = IO.select([rp], [wp])
  if r = rs[0]
    ret = r.read(5)
    print ret
    case ret
    when /ping/
      mesg = "pong\n"
    when /pong/
      mesg = "ping "
    end
  end
  if w = ws[0]
    w.write(mesg)
  end
}

الناتج:

ping pong
ping pong
ping pong
(snipped)
ping

انظر أيضا

  • التابع sysopen: يفتح التابع sysopen المسار المحدد، ثم يعيد واصف الملف الأساسي كعدد صحيح.

مصادر