semop(2)

الاسم
semop, semtimedop - عمليات الإشارات (semaphore) لنظام V

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
               const struct timespec *_Nullable timeout);

    متطلبات ماكروات اختبار الميزات لـ glibc (انظر feature_test_macros(7)):

semtimedop():
    _GNU_SOURCE

الوصف
كل إشارة في مجموعة إشارات System V لها القيم المرتبطة التالية:

    unsigned short  semval;   /* semaphore value */
    unsigned short  semzcnt;  /* # waiting for zero */
    unsigned short  semncnt;  /* # waiting for increase */
    pid_t           sempid;   /* PID of process that last
                                 modified the semaphore value */

semop() ينفذ عمليات على إشارات محددة في المجموعة المشار إليها بواسطة semid. كل عنصر من عناصر nsops في المصفوفة المشار إليها بواسطة sops هو بنية تحدد عملية سيتم تنفيذها على إشارة واحدة. عناصر هذه البنية من النوع struct sembuf، وتحتوي على الأعضاء التالية:

    unsigned short sem_num;  /* semaphore number */
    short          sem_op;   /* semaphore operation */
    short          sem_flg;  /* operation flags */

الأعلام المعترف بها في sem_flg هي IPC_NOWAIT و SEM_UNDO. إذا حددت عملية SEM_UNDO، فسيتم التراجع عنها آليًا عند إنهاء العملية.

مجموعة العمليات الموجودة في sops تُنفذ بترتيب المصفوفة، وبشكل ذري، أي أن العمليات تُنفذ إما كوحدة كاملة، أو لا تُنفذ على الإطلاق. يعتمد سلوك استدعاء النظام إذا لم يمكن تنفيذ جميع العمليات فورًا على وجود علم IPC_NOWAIT في حقول sem_flg الفردية، كما هو مذكور أدناه.

كل عملية تُنفذ على الإشارة رقم sem_num من مجموعة الإشارات، حيث تُرقم الإشارة الأولى في المجموعة بالرقم 0. هناك ثلاثة أنواع من العمليات، يتم تمييزها بقيمة sem_op.

إذا كان sem_op عددًا صحيحًا موجبًا، تضيف العملية هذه القيمة إلى قيمة الإشارة (semval). علاوة على ذلك، إذا تم تحديد SEM_UNDO لهذه العملية، يطرح النظام قيمة sem_op من قيمة تعديل الإشارة (semadj) لهذه الإشارة. يمكن لهذه العملية أن تستمر دائمًا—فهي لا تجبر الخيط على الانتظار أبدًا. يجب أن تمتلك العملية المستدعية صلاحية التعديل على مجموعة الإشارات.

إذا كان sem_op صفرًا، يجب أن تمتلك العملية صلاحية القراءة على مجموعة الإشارات. هذه عملية "انتظار للصفر": إذا كان semval صفرًا، يمكن للعملية أن تستمر فورًا. خلاف ذلك، إذا تم تحديد IPC_NOWAIT في sem_flg، يفشل semop() مع تعيين errno إلى EAGAIN (ولا يتم تنفيذ أي من العمليات في sops). خلاف ذلك، يتم زيادة semzcnt (عدد الخيوط المنتظرة حتى تصبح قيمة هذه الإشارة صفرًا) بمقدار واحد وينام الخيط حتى يحدث أحد الأمور التالية:

•   تصبح semval 0، وعندها يتم تقليل قيمة semzcnt.

•   تتم إزالة مجموعة الإشارات: يفشل semop()، مع تعيين errno إلى EIDRM.

•   يلتقط الخيط المستدعي إشارة: يتم تقليل قيمة semzcnt ويفشل semop()، مع تعيين errno إلى EINTR.

إذا كان sem_op أقل من الصفر، يجب أن تمتلك العملية صلاحية التعديل على مجموعة الإشارات. إذا كانت semval أكبر من أو تساوي القيمة المطلقة لـ sem_op، يمكن للعملية أن تستمر فورًا: تُطرح القيمة المطلقة لـ sem_op من semval، وإذا تم تحديد SEM_UNDO لهذه العملية، يضيف النظام القيمة المطلقة لـ sem_op إلى قيمة تعديل الإشارة (semadj) لهذه الإشارة. إذا كانت القيمة المطلقة لـ sem_op أكبر من semval، وتم تحديد IPC_NOWAIT في sem_flg، يفشل semop()، مع تعيين errno إلى EAGAIN (ولا يتم تنفيذ أي من العمليات في sops). خلاف ذلك، يتم زيادة semncnt (عداد الخيوط المنتظرة لزيادة قيمة هذه الإشارة) بمقدار واحد وينام الخيط حتى يحدث أحد الأمور التالية:

•   تصبح semval أكبر من أو تساوي القيمة المطلقة لـ sem_op: تستمر العملية الآن، كما هو موصوف أعلاه.

•   تتم إزالة مجموعة الإشارات من النظام: يفشل semop()، مع تعيين errno إلى EIDRM.

•   يلتقط الخيط المستدعي إشارة: يتم تقليل قيمة semncnt ويفشل semop()، مع تعيين errno إلى EINTR.

عند الإكمال بنجاح، يتم تعيين قيمة sempid لكل إشارة محددة في المصفوفة المشار إليها بواسطة sops إلى معرف عملية المستدعي. بالإضافة إلى ذلك، يتم تعيين sem_otime إلى الوقت الحالي.

  semtimedop()
يتصرف semtimedop() بشكل مماثل لـ semop() باستثناء أنه في الحالات التي ينام فيها الخيط المستدعي، تكون مدة هذا النوم محدودة بمقدار الوقت المنقضي المحدد بواسطة بنية timespec التي يُمرر عنوانها في وسيط timeout. (سيتم تقريب فترة النوم هذه إلى دقة ساعة النظام، وتأخيرات جدولة النواة تعني أن الفترة قد تتجاوز بمقدار صغير.) إذا تم الوصول إلى الحد الزمني المحدد، يفشل semtimedop() مع تعيين errno إلى EAGAIN (ولا يتم تنفيذ أي من العمليات في sops). إذا كان وسيط timeout هو NULL، فإن semtimedop() يتصرف تمامًا مثل semop().

لاحظ أنه إذا تمت مقاطعة semtimedop() بواسطة إشارة، مما يتسبب في فشل الاستدعاء مع الخطأ EINTR، تظل محتويات timeout دون تغيير.

قيمة الإرجاع
عند النجاح، يعيد semop() و semtimedop() القيمة 0. عند الفشل، يعيدان القيمة -1، ويضبطان errno للإشارة إلى الخطأ.

الأخطاء

E2BIG
    الوسيط nsops أكبر من SEMOPM، وهو الحد الأقصى لعدد العمليات المسموح بها لكل استدعاء نظام.

EACCES
    لا تمتلك العملية المستدعية الصلاحيات المطلوبة لتنفيذ عمليات الإشارات المحددة، ولا تمتلك القدرة CAP_IPC_OWNER في مساحة اسم المستخدم التي تحكم مساحة اسم IPC الخاصة بها.

EAGAIN
    لم تتمكن عملية من الاستمرار فورًا وتم تحديد IPC_NOWAIT في sem_flg أو انتهى الحد الزمني المحدد في timeout.

EFAULT
    عنوان محدد في وسيط sops أو timeout غير قابل للوصول.

EFBIG
    لبعض العمليات، قيمة sem_num أقل من 0 أو أكبر من أو تساوي عدد الإشارات في المجموعة.

EIDRM
    حُذفت مجموعة السيمافور.

EINTR
    أثناء الحظر في استدعاء النظام هذا، التقط الخيط إشارة؛ انظر signal(7).

EINVAL
    مجموعة السيمافور غير موجودة، أو semid أقل من الصفر، أو nsops له قيمة غير موجبة.

ENOMEM
    يحدد sem_flg لبعض العمليات SEM_UNDO ولا يمتلك النظام ذاكرة كافية لتخصيص بنية التراجع.

ERANGE
    بالنسبة لبعض العمليات، sem_op+semval أكبر من SEMVMX، القيمة القصوى المعتمدة على التنفيذ لـ semval.

المعايير
POSIX.1-2024.

التاريخ

semop()
    SVr4, POSIX.1-2001.

semtimedop()
    SVr4، لينكس 2.5.52 (مُعاد توجيهه إلى لينكس 2.4.22)، glibc 2.3.3.

ملاحظات
لا تُورث بنى sem_undo لعملية ما إلى الطفل الناتج عن fork(2)، ولكنها تُورث عبر استدعاء نظام execve(2).

لا يُعاد تشغيل semop() آليًا أبدًا بعد مقاطعته بواسطة معالج إشارة، بغض النظر عن إعداد علامة SA_RESTART عند إنشاء معالج إشارة.

قيمة تعديل السيمافور (semadj) هي عدد صحيح لكل عملية ولكل سيمافور، وهو المجموع المنفي لجميع العمليات المنفذة على سيمافور مع تحديد علامة SEM_UNDO. تمتلك كل عملية قائمة من قيم semadj—قيمة واحدة لكل سيمافور عملت عليه باستخدام SEM_UNDO. عندما تنتهي عملية، تُضاف كل قيمة من قيم semadj الخاصة بكل سيمافور إلى السيمافور المقابل، مما يلغي تأثير عمليات تلك العملية على السيمافور (ولكن انظر الأخطاء أدناه). عندما تُضبط قيمة السيمافور مباشرة باستخدام طلب SETVAL أو SETALL إلى semctl(2)، تُمسح قيم semadj المقابلة في جميع العمليات. تسمح علامة clone(2) CLONE_SYSVSEM لأكثر من عملية بمشاركة قائمة semadj؛ انظر clone(2) للتفاصيل.

يمكن استرداد قيم semval و sempid و semzcnt و semnct للسيمافور باستخدام استدعاءات semctl(2) المناسبة.

  حدود السيمافور
تؤثر الحدود التالية على موارد مجموعة السيمافور على استدعاء semop():

SEMOPM
    الحد الأقصى لعدد العمليات المسموح بها لاستدعاء semop() واحد. قبل لينكس 3.19، كانت القيمة المبدئية لهذا الحد 32. منذ لينكس 3.19، القيمة المبدئية هي 500. على لينكس، يمكن قراءة هذا الحد وتعديله عبر الحقل الثالث من /proc/sys/kernel/sem. ملاحظة: لا ينبغي رفع هذا الحد فوق 1000، بسبب خطر فشل semop() بسبب تجزئة ذاكرة النواة عند تخصيص الذاكرة لنسخ مصفوفة sops.

SEMVMX
    القيمة القصوى المسموح بها لـ semval: معتمدة على التنفيذ (32767).

لا يمتلك التنفيذ حدودًا جوهرية للقيمة القصوى للتعديل عند الخروج (SEMAEM)، والحد الأقصى لعدد بنى التراجع على مستوى النظام (SEMMNU)، ومعاملات النظام للحد الأقصى لعدد إدخالات التراجع لكل عملية.

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

في لينكس 2.6.x، x <= 10، يوجد خطأ يمنع في بعض الظروف إيقاظ خيط ينتظر أن تصبح قيمة السيمافور صفرًا عندما تصبح القيمة صفرًا بالفعل. أُصلح هذا الخطأ في لينكس 2.6.11.

أمثلة
يستخدم مقطع الكود التالي semop() للانتظار ذريًا حتى تصبح قيمة السيمافور 0 صفرًا، ثم زيادة قيمة السيمافور بمقدار واحد.

    struct sembuf sops[2];
    int semid;

    /* Code to set semid omitted */

    sops[0].sem_num = 0;        /* Operate on semaphore 0 */
    sops[0].sem_op = 0;         /* Wait for value to equal 0 */
    sops[0].sem_flg = 0;

    sops[1].sem_num = 0;        /* Operate on semaphore 0 */
    sops[1].sem_op = 1;         /* Increment value by one */
    sops[1].sem_flg = 0;

    if (semop(semid, sops, 2) == -1) {
        perror("semop");
        exit(EXIT_FAILURE);
    }

يمكن العثور على مثال إضافي لاستخدام semop() في shmop(2).

انظر أيضًا
clone(2)، semctl(2)، semget(2)، sigaction(2)، capabilities(7)، sem_overview(7)، sysvipc(7)، time(7)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.