اختر (يونكس)
تُعدّ دالة select استدعاءً نظاميًا وواجهة برمجة تطبيقات (API) في أنظمة التشغيل الشبيهة بنظام Unix والمتوافقة مع معيار POSIX ، وتُستخدم لفحص حالة مُعرّفات الملفات لقنوات الإدخال/الإخراج المفتوحة. [ 1 ] وتُعدّ pselect أداةً مُحسّنة تدعم إخفاء الإشارات. يُشبه استدعاء النظام select أداة poll الأحدث التي طُرحت في نظام UNIX System V والأنظمة اللاحقة. مع ذلك، وبسبب مشكلة C10k ، تم استبدال كلٍّ من select و poll بأدوات مثل kqueue و /dev/poll و epoll ومنافذ إكمال الإدخال/الإخراج . [ 2 ]
من الاستخدامات الشائعة للدالة select، بخلاف وظيفتها الأساسية المتمثلة في انتظار مؤشرات الملفات، تنفيذ عملية انتظار مؤقتة (sleep) قابلة للنقل لأقل من ثانية . ويمكن تحقيق ذلك بتمرير قيمة NULL لجميع وسائط fd_set الثلاثة، ومدة الانتظار المطلوبة كوسيط timeout.
في لغة البرمجة C ، يتم تعريف استدعاء النظام select في ملف الرأس sys/select.h أو unistd.h ، وله الصيغة التالية:
int select ( int nfds , fd_set * readfds , fd_set * writefds , fd_set * errorfds , struct timeval * timeout );| دعوى | وصف |
|---|---|
| nfds | هذا عدد صحيح يزيد بمقدار واحد عن القيمة القصوى لأي مُعرّف ملف في أي من المجموعات. بعبارة أخرى، عند إضافة مُعرّفات ملفات إلى كل مجموعة، يجب حساب القيمة القصوى الصحيحة لجميعها، ثم زيادة هذه القيمة بمقدار واحد، ثم تمريرها كـ nfds. |
| readfds | نوع fd_set يحتوي على مُعرّفات الملفات المراد التحقق من جاهزيتها للقراءة، ويُشير عند الإخراج إلى مُعرّفات الملفات الجاهزة للقراءة. يمكن أن تكون قيمته NULL . |
| writefds | نوع fd_set يحتوي على مُعرّفات الملفات المراد فحصها للتأكد من جاهزيتها للكتابة، ويُشير عند الإخراج إلى مُعرّفات الملفات الجاهزة للكتابة. يمكن أن تكون قيمته NULL . |
| باستثناء fds | نوع fd_set يحتوي على مُعرّفات الملفات المراد فحصها بحثًا عن حالات استثنائية معلقة، ويُشير عند الإخراج إلى مُعرّفات الملفات التي تحتوي على حالات استثنائية معلقة. مثال على حالة استثنائية هو وجود بيانات خارج النطاق على مقبس TCP. يمكن أن تكون قيمته NULL . |
| نفذ الوقت | بنية من نوع struct timeval تحدد الحد الأقصى للفاصل الزمني للانتظار حتى اكتمال عملية الاختيار. إذا كان وسيط timeout يشير إلى كائن من نوع struct timeval جميع عناصره تساوي صفرًا، فإن الدالة select() لا تتوقف. أما إذا كان وسيط timeout يساوي NULL ، فإن الدالة select() تتوقف حتى يحدث حدث ما يؤدي إلى إرجاع أحد الأقنعة بقيمة صالحة (غير صفرية). يقوم نظام Linux بتحديث قيمة timeout تلقائيًا للإشارة إلى المدة الزمنية المنقضية، مع العلم أن هذا السلوك لا تشترك فيه معظم أنظمة Unix الأخرى. |
fd_set typeيمكن معالجة الوسائط باستخدام أربعة وحدات ماكرو مساعدة: FD_SET() و FD_CLR() و FD_ZERO() و FD_ISSET() .
تقوم الدالة Select بإرجاع العدد الإجمالي للبتات التي تم تعيينها في readfds و writefds و errorfds ، أو صفر إذا انتهت مهلة الانتظار، و -1 في حالة حدوث خطأ.
تكون مجموعات مُعرّفات الملفات المستخدمة في دالة select محدودة الحجم، وتعتمد على نظام التشغيل. أما دالة poll الأحدث، فتُقدّم حلاً أكثر مرونة.
مثال
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <err.h> #include <fcntl.h> #include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/types.h> #include <unistd.h>#define PORT "9421"void die ( const char * msg ) { perror ( msg ); exit ( EXIT_FAILURE ); }typedef struct addrinfo AddressInfo ;int main ( int argc , char * argv []) { int sockfd ;AddressInfo * res0 ;مخزن الأحرف [ BUFSIZ ];fd_set master ; fd_set readfds ;خطأ عددي ؛ssize_t nbytes ;memset ( & hints , '\0' , sizeof ( struct addrinfo ));AddressInfo * hints = { . ai_family = AF_INET , . ai_socktype = SOCK_STREAM , . ai_protocol = IPPROTO_TCP , . ai_flags = AI_PASSIVE };إذا ( كان الخطأ = getaddrinfo ( NULL , PORT , & hints , & res0 )) { errx ( EXIT_FAILURE , "%s" , gai_strerror ( error )); }for ( AddressInfo * res = res0 ; res ; res = res -> ai_next )) { if (( sockfd = مقبس ( res -> ai_family , res -> ai_socktype , res -> ai_protocol )) == -1 ) { perror ( "socket()" ); يكمل ؛ }int on = 1 ; if ( setsockopt ( sockfd , SOL_SOCKET , SO_REUSEADDR , ( char * ) & on , sizeof ( int )) == -1 ) { perror ( "setsockopt()" ); continue ; }إذا كانت نتيجة عملية الربط ( bind ( sockfd , res- > ai_addr , res- > ai_addrlen ) تساوي -1 ) { perror ( "bind()" ); continue ; }استراحة ؛ }إذا كانت قيمة ( sockfd == -1 ) { أرجع EXIT_FAILURE ; }freeaddrinfo ( res0 );إذا كانت نتيجة الاستماع ( sockfd ، 32 ) تساوي -1 ، فسيتم إنهاء البرنامج مع عرض رسالة " listen() " .إذا كانت نتيجة استدعاء الدالة fcntl ( sockfd , F_SETFD , O_NONBLOCK ) تساوي -1 ، فسيتم إنهاء البرنامج باستخدام الأمر die ( " fcntl()" ); }FD_ZERO ( & master ); FD_ZERO ( & readfds );FD_SET ( sockfd , & master );int maxfd = sockfd ;بينما ( صحيح ) { memcpy ( & readfds , & master , sizeof ( master ));printf ( "تشغيل select() \n " );int nready ; if ( nready = select ( maxfd + 1 , & readfds , NULL , NULL , NULL ) == -1 ) { die ( "select()" ); }printf ( "عدد الواصفات الجاهزة: %d \n " , nready );for ( int i = 0 ; i < = maxfd && nready > 0 ; i ++ ) { if ( FD_ISSET ( i , & readfds )) { nready-- ;إذا كان ( i == sockfd ) { printf ( "محاولة قبول اتصال (اتصالات) جديدة \n " );int newval ; if (( newval = accept ( sockfd , NULL , NULL )) == -1 ) { if ( EWOULDBLOCK != errno ) { die ( "accept()" ); }break ; } else {إذا كانت نتيجة استدعاء الدالة fcntl ( newval , F_SETFD , O_NONBLOCK ) تساوي -1 ، فسيتم إنهاء البرنامج باستخدام الأمر die ( "fcntl()" ); }FD_SET ( newval , & master );إذا كان ( maxfd < newval ) { maxfd = newval ; } } } else { printf ( "تم استلام البيانات من أحد الواصفات \n " );nbytes = recv ( i , buffer , sizeof ( buffer ), 0 ); if ( nbytes <= 0 ) { if ( EWOULDBLOCK != errno ) { die ( "recv()" ); }استراحة ؛ }buffer [ nbytes ] = '\0' ; printf ( "%s" , buffer );printf ( "تم استلام %zi بايت. \n " , nbytes );أغلق ( i )؛ FD_CLR ( i ، & master )؛} }}} return 0 ; }انظر أيضاً
مراجع
- ↑ مجموعة أبحاث أنظمة الحاسوب (1994). "select, pselect — تعدد إرسال/إدخال متزامن" . مرجع BSD المتقاطع . NetBSD .
- ↑ "طرق معالجة الاتصال" . nginx .org .
روابط خارجية
- – مرجع واجهات النظام، مواصفات يونكس الموحدة ، الإصدار 5 من مجموعة Open Group
- صفحات الدليل
select(2)لأنظمة FreeBSD و NetBSD و OpenBSD و DragonFly BSD
- مكتبة C POSIX
- الأحداث (الحوسبة)
- استدعاءات النظام
