SHMOP(2)

الاسم
shmat, shmdt - عمليات الذاكرة المشتركة System V

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

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

void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);
int shmdt(const void *shmaddr);

الوصف

  shmat()
shmat() يربط مقطع الذاكرة المشتركة System V المحدد بواسطة shmid بمساحة عنوان العملية المستدعية. يتم تحديد عنوان الربط بواسطة shmaddr وفقًا لأحد المعايير التالية:

•   إذا كان shmaddr NULL، يختار النظام عنوانًا مناسبًا (غير مستخدم) محاذيًا للصفحة لربط المقطع.

•   إذا لم يكن shmaddr NULL وتم تحديد SHM_RND في shmflg، يحدث الربط عند العنوان المساوي لـ shmaddr مقربًا إلى الأسفل لأقرب مضاعف لـ SHMLBA.

•   خلاف ذلك، يجب أن يكون shmaddr عنوانًا محاذيًا للصفحة يحدث عنده الربط.

بالإضافة إلى SHM_RND، يمكن تحديد الأعلام التالية في وسيطة قناع البت shmflg:

SHM_EXEC (خاص بلينكس؛ منذ لينكس 2.6.9)
    يسمح بتنفيذ محتويات المقطع. يجب أن يكون لدى المستدعي صلاحية التنفيذ على المقطع.

SHM_RDONLY
    يربط المقطع للوصول للقراءة فقط. يجب أن يكون لدى العملية صلاحية القراءة للمقطع. إذا لم يتم تحديد هذا العلم، يتم ربط المقطع للوصول للقراءة والكتابة، ويجب أن يكون لدى العملية صلاحيات القراءة والكتابة للمقطع. لا يوجد مفهوم لمقطع ذاكرة مشتركة للكتابة فقط.

SHM_REMAP (خاص بلينكس)
    يحدد هذا العلم أن تخطيط المقطع يجب أن يستبدل أي تخطيط موجود في النطاق الذي يبدأ من shmaddr ويستمر بحجم المقطع. (عادةً، ينتج خطأ EINVAL إذا كان هناك تخطيط موجود بالفعل في نطاق العنوان هذا.) في هذه الحالة، يجب ألا يكون shmaddr NULL.

لا يتم تغيير قيمة brk(2) للعملية المستدعية بواسطة الربط. سيتم فصل المقطع آليًا عند خروج العملية. يمكن ربط نفس المقطع كقراءة وكقراءة-كتابة، وأكثر من مرة، في مساحة عنوان العملية.

استدعاء shmat() ناجح يحدّث أعضاء بنية shmid_ds (انظر shmctl(2)) المرتبطة بمقطع الذاكرة المشتركة كما يلي:

•   يتم تعيين shm_atime إلى الوقت الحالي.

•   يتم تعيين shm_lpid إلى معرف العملية للعملية المستدعية.

•   يتم زيادة shm_nattch بمقدار واحد.

  shmdt()
shmdt() يفصل مقطع الذاكرة المشتركة الموجود في العنوان المحدد بواسطة shmaddr من مساحة عنوان العملية المستدعية. يجب أن يكون المقطع المراد فصله مرتبطًا حاليًا مع shmaddr مساويًا للقيمة التي أرجعها استدعاء shmat() الرابط.

عند استدعاء shmdt() ناجح، يحدّث النظام أعضاء بنية shmid_ds المرتبطة بمقطع الذاكرة المشتركة كما يلي:

•   يتم تعيين shm_dtime إلى الوقت الحالي.

•   يتم تعيين shm_lpid إلى معرف العملية للعملية المستدعية.

•   يتم إنقاص shm_nattch بمقدار واحد. إذا أصبح 0 وتم وضع علامة على المقطع للحذف، يتم حذف المقطع.

قيمة الإرجاع
عند النجاح، تُرجع shmat() عنوان مقطع الذاكرة المشتركة المرفق؛ عند الخطأ، يُرجع (void *) -1، ويُضبط errno للإشارة إلى الخطأ.

عند النجاح، يُرجع shmdt() 0؛ عند الخطأ يُرجع -1، ويتم تعيين errno للإشارة إلى الخطأ.

الأخطاء
يمكن أن يفشل shmat() مع أحد الأخطاء التالية:

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

EIDRM
    يشير shmid إلى معرف مزال.

EINVAL
    قيمة shmid غير صالحة، غير محاذية (أي غير محاذية للصفحة ولم يتم تحديد SHM_RND) أو قيمة shmaddr غير صالحة، أو لا يمكن ربط المقطع عند shmaddr، أو تم تحديد SHM_REMAP وكان shmaddr NULL.

ENOMEM
    تعذر تخصيص الذاكرة للوصف أو لجداول الصفحات.

يمكن أن تفشل shmdt() بأحد الأخطاء التالية:

EINVAL
    لا يوجد مقطع ذاكرة مشتركة موصول عند shmaddr؛ أو، shmaddr غير محاذٍ لحدود الصفحة.

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

التاريخ
POSIX.1-2001، SVr4.

في SVID 3 (أو ربما أقدم)، تغير نوع الوسيط shmaddr من char * إلى const void *، والنوع المُعاد من shmat() من char * إلى void *.

غير POSIX.1-2024 رمز الخطأ من (void *) -1 إلى SHM_FAILED.

ملاحظات
بعد fork(2)، يرث الطفل مقاطع الذاكرة المشتركة الموصولة.

بعد execve(2)، تُفصل جميع مقاطع الذاكرة المشتركة الموصولة عن العملية.

عند _exit(2)، تُفصل جميع مقاطع الذاكرة المشتركة الموصولة عن العملية.

استخدام shmat() مع shmaddr يساوي NULL هو الطريقة المفضلة والمحمولة لوصل مقطع ذاكرة مشتركة. كن على علم بأن مقطع الذاكرة المشتركة الموصول بهذه الطريقة قد يُوصل بعناوين مختلفة في عمليات مختلفة. لذلك، يجب جعل أي مؤشرات محفوظة داخل الذاكرة المشتركة نسبية (عادةً إلى عنوان بداية المقطع)، بدلاً من مطلقة.

في لينكس، من الممكن وصل مقطع ذاكرة مشتركة حتى لو كان مُعلماً بالفعل للحذف. ومع ذلك، لا يحدد POSIX.1 هذا السلوك والعديد من التطبيقات الأخرى لا تدعمه.

المعامل النظامي التالي يؤثر على shmat():

SHMLBA
    مضاعف عنوان الحد الأدنى للمقطع. عند تحديد عنوان وصل صراحةً في استدعاء لـ shmat()، يجب على المستدعي التأكد من أن العنوان هو مضاعف لهذه القيمة. هذا ضروري في بعض البنى، إما لضمان أداء جيد لذاكرة التخزين المؤقت لوحدة المعالجة المركزية أو لضمان أن وصلات مختلفة لنفس المقطع لها مناظر متسقة داخل ذاكرة التخزين المؤقت لوحدة المعالجة المركزية. SHMLBA عادةً ما يكون مضاعفاً لحجم صفحة النظام. (في العديد من بنى لينكس، SHMLBA هو نفس حجم صفحة النظام.)

لا يضع التطبيق حداً جوهرياً لكل عملية على عدد مقاطع الذاكرة المشتركة (SHMSEG).

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

في نافذة طرفية واحدة، نشغل برنامج "القارئ"، الذي ينشئ مقطع ذاكرة مشتركة من System V ومجموعة إشارة من System V. يطبع البرنامج معرفات الكائنات المنشأة، ثم ينتظر تغير قيمة الإشارة.

    $ ./svshm_string_read;
    shmid = 1114194
    semid = 15

في نافذة طرفية أخرى، نشغل برنامج "الكاتب". يأخذ برنامج "الكاتب" ثلاث وسائط لسطر الأوامر: معرفات مقطع الذاكرة المشتركة ومجموعة الإشارة المنشأة بواسطة "القارئ"، وسلسلة نصية. يصل مقطع الذاكرة المشتركة الموجود، وينسخ السلسلة النصية إلى الذاكرة المشتركة، ويعدل قيمة الإشارة.

    $ ./svshm_string_write 1114194 15 'Hello, world';

بالعودة إلى الطرفية حيث يعمل "القارئ"، نرى أن البرنامج توقف عن الانتظار على الإشارة وطبع السلسلة النصية التي نُسخت إلى مقطع الذاكرة المشتركة بواسطة الكاتب:

    Hello, world

  مصدر البرنامج: svshm_string.h
ملف الرأس التالي مُضمن بواسطة برنامجي "القارئ" و"الكاتب":

    /* svshm_string.h

       Licensed under GNU General Public License v2 or later.
    */
    #ifndef SVSHM_STRING_H
    #define SVSHM_STRING_H

    #include <err.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/sem.h>

    union semun {                   /* Used in calls to semctl() */
        int                 val;
        struct semid_ds     *buf;
        unsigned short      *array;
    #if defined(__linux__)
        struct seminfo      *__buf;
    #endif
    };

    #define MEM_SIZE 4096

    #endif  // include guard

  مصدر البرنامج: svshm_string_read.c
ينشئ برنامج "القارئ" مقطع ذاكرة مشتركة ومجموعة إشارة تحتوي على إشارة واحدة. ثم يصل كائن الذاكرة المشتركة إلى مساحة عنوانه ويُهيئ قيمة الإشارة إلى 1. أخيراً، ينتظر البرنامج حتى تصبح قيمة الإشارة 0، وبعد ذلك يطبع السلسلة النصية التي نُسخت إلى مقطع الذاكرة المشتركة بواسطة "الكاتب".

    /* svshm_string_read.c

       Licensed under GNU General Public License v2 or later.
    */
    #include <err.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/shm.h>

    #include "svshm_string.h"

    int
    main(void)
    {
        int            semid, shmid;
        char           *addr;
        union semun    arg, dummy;
        struct sembuf  sop;

        /* Create shared memory and semaphore set containing one
           semaphore.  */

        shmid = shmget(IPC_PRIVATE, MEM_SIZE, IPC_CREAT | 0600);
        if (shmid == -1)
            err(EXIT_FAILURE, "shmget");

        semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
        if (semid == -1)
            err(EXIT_FAILURE, "semget");

        /* Attach shared memory into our address space.  */

        addr = shmat(shmid, NULL, SHM_RDONLY);
        if (addr == (void *) -1)
            err(EXIT_FAILURE, "shmat");

        /* Initialize semaphore 0 in set with value 1.  */

        arg.val = 1;
        if (semctl(semid, 0, SETVAL, arg) == -1)
            err(EXIT_FAILURE, "semctl");

        printf("shmid = %d\n", shmid);
        printf("semid = %d\n", semid);

        /* Wait for semaphore value to become 0.  */

        sop.sem_num = 0;
        sop.sem_op = 0;
        sop.sem_flg = 0;

        if (semop(semid, &sop, 1) == -1)
            err(EXIT_FAILURE, "semop");

        /* Print the string from shared memory.  */

        printf("%s\n", addr);

        /* Remove shared memory and semaphore set.  */

        if (shmctl(shmid, IPC_RMID, NULL) == -1)
            err(EXIT_FAILURE, "shmctl");
        if (semctl(semid, 0, IPC_RMID, dummy) == -1)
            err(EXIT_FAILURE, "semctl");

        exit(EXIT_SUCCESS);
    }

  مصدر البرنامج: svshm_string_write.c
يأخذ برنامج الكاتب ثلاث وسائط لسطر الأوامر: معرفات مقطع الذاكرة المشتركة ومجموعة الإشارة التي أنشأها "القارئ" بالفعل، وسلسلة نصية. يصل مقطع الذاكرة المشتركة إلى مساحة عنوانه، ثم يُنقص قيمة الإشارة إلى 0 لإعلام "القارئ" بأنه يمكنه الآن فحص محتويات الذاكرة المشتركة.

    /* svshm_string_write.c

       Licensed under GNU General Public License v2 or later.
    */
    #include <err.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/sem.h>
    #include <sys/shm.h>

    #include "svshm_string.h"

    int
    main(int argc, char *argv[])
    {
        int            semid, shmid;
        char           *addr;
        size_t         size;
        struct sembuf  sop;

        if (argc != 4) {
            fprintf(stderr, "Usage: %s shmid semid string\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        size = strlen(argv[3]) + 1;  /* +1 to include trailing '\0' */
        if (size > MEM_SIZE) {
            fprintf(stderr, "String is too big!\n");
            exit(EXIT_FAILURE);
        }

        /* Get object IDs from command-line.  */

        shmid = atoi(argv[1]);
        semid = atoi(argv[2]);

        /* Attach shared memory into our address space and copy string
           (including trailing null byte) into memory.  */

        addr = shmat(shmid, NULL, 0);
        if (addr == (void *) -1)
            err(EXIT_FAILURE, "shmat");

        memcpy(addr, argv[3], size);

        /* Decrement semaphore to 0.  */

        sop.sem_num = 0;
        sop.sem_op = -1;
        sop.sem_flg = 0;

        if (semop(semid, &sop, 1) == -1)
            err(EXIT_FAILURE, "semop");

        exit(EXIT_SUCCESS);
    }

انظر أيضًا
brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7)

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

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

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