共享內(nèi)存同步機(jī)制

1. 使用posix有名信號量進(jìn)行同步

有名信號量既可用于線程間的同步,又可用于進(jìn)程間的同步。

兩個進(jìn)程,對同一個共享內(nèi)存讀寫,可利用有名信號量來進(jìn)行同步。一個進(jìn)程寫,另一個進(jìn)程讀,利用兩個有名信號量semr, semw。semr信號量控制能否讀,初始化為0。 semw信號量控制能否寫,初始為1。

示例代碼如下:

//讀共享內(nèi)存
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main()
{
    int shmid = -1;
    key_t key = 0x2234;
    Teacher *p = NULL;
    sem_t *semr = NULL, *semw = NULL;
    semr = sem_open("sem_r", O_CREAT | O_RDWR, 0666, 0);
    if (semr == SEM_FAILED )
    {
        printf("errno = %d\n", errno );
        return -1;
    }

    semw = sem_open("sem_w", O_CREAT | O_RDWR, 0666, 1 );
    if (semw == SEM_FAILED)
    {
        printf("errno = %d\n", errno );
        return -1;
    }
    
    shmid = shmget(key, 0, 0 );
    if ( shmid == -1 )
    {
        printf("shmget failed\n");
        perror("shmget err");
        return -1;
    }

    p = (Teacher*)shmat(shmid, NULL, 0);
    if (p == (Teacher*)(-1))
    {
        printf("shmat failed\n");
        perror("shmat");
        return -1;
    }

    while(1)
    {
        sem_wait(semr);
        printf("name:%s\n", p->name);
        printf("age:%d\n", p->age);
        sem_post(semw);
    }

    //shmdt(p);
    return 0;
}
//寫共享內(nèi)存
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

typedef struct _Teacher
{
    char name[64];
    int age;
}Teacher;

int main()
{
    int shmid = -1;
    key_t key = 0x2234;
    Teacher *p = NULL;
    int count = 0;
    sem_t *semr = NULL, *semw = NULL;
    semr = sem_open("sem_r", O_CREAT | O_RDWR, 0666, 0);
    if (semr == SEM_FAILED )
    {
        printf("errno = %d\n", errno );
        return -1;
    }

    semw = sem_open("sem_w", O_CREAT | O_RDWR, 0666, 1 );
    if (semw == SEM_FAILED)
    {
        printf("errno = %d\n", errno );
        return -1;
    }
    
    shmid = shmget(key, sizeof(Teacher), 0666 | IPC_CREAT );
    if ( shmid == -1 )
    {
        perror("shmget");
        return -1;
    }

    p = (Teacher*)shmat(shmid, NULL, 0);
    if (p == (Teacher*)(-1))
    {
        perror("shmat");
        return -1;
    }

    while(1)
    {
        sem_wait(semw);
        //printf(">name:");
        strcpy(p->name, "aaaa");
        p->age = count;
        ++count;
        sem_post(semr);
    }
    return 0;
}

注意:編譯上面的代碼需要鏈接動態(tài)庫-lpthread

2. 使用posix無名信號量進(jìn)行同步

POSIX無名信號量是基于內(nèi)存的信號量,可以用于線程間同步也可以用于進(jìn)程間同步。若實現(xiàn)進(jìn)程間同步,需要在共享內(nèi)存中來創(chuàng)建無名信號量。

因此,共享內(nèi)存需要定義以下的結(jié)構(gòu)體:

typedef struct
{
    sem_t semr;
    sem_t semw;
    char buf[MAXSIZE];
}SHM;

3. 使用system V的信號燈實現(xiàn)同步

System V的信號燈是一個或者多個信號燈的一個集合。其中的每一個都是單獨的計數(shù)信號燈。而Posix信號燈指的是單個計數(shù)信號燈。

System V 信號燈由內(nèi)核維護(hù),主要函數(shù)semget,semop,semctl 。

一個進(jìn)程寫,另一個進(jìn)程讀,信號燈集中有兩個信號燈,下標(biāo)0代表能否讀,初始化為0。 下標(biāo)1代表能否寫,初始為1。
示例代碼如下:

//進(jìn)程A
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

int shm_id, sem_id;
char* addr;

void ser_exit(int signo)
{
    semctl(sem_id, 0, IPC_RMID);
    semctl(sem_id, 1, IPC_RMID);
    shmdt(addr);
    shmctl(shm_id, IPC_RMID, NULL);

    printf("server exit ...\n");
    exit(0);
}

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    struct sigaction act;
    act.sa_handler = ser_exit;

    key_t shm_key = ftok("./readshm", 1);
    if (shm_key == -1 )
    {
        perror("ftok error");
        return -1;
    }

    int shm_id = shmget(shm_key, 1024, IPC_CREAT | IPC_EXCL | 0755);
    if (shm_id == -1)
    {
        perror("shmget");
        return -1;
    }
    
    char* addr = (char*)shmat(shm_id, NULL, 0);
    if (addr == (char*)(-1))
    {
        perror("shmat");
        return -1;
    }

    int sem_id = semget(shm_key, 2, IPC_CREAT|IPC_EXCL|0755);
    if (sem_id == -1 )
    {
        perror("semget");
        return -1;
    }

    union semun init;
    init.val = 0;

    semctl(sem_id, 0, SETVAL, init);
    semctl(sem_id, 1, SETVAL, init);

    struct sembuf v = {0, 1, SEM_UNDO};
    struct sembuf p = {1, -1, SEM_UNDO};

    sigaction(SIGINT, &act, NULL);
    while(1)
    {
        printf("ser:>");
        scanf("%s", addr);
        semop(sem_id, &v, 1);
        semop(sem_id, &p, 1);
        printf("cli:>%s\n", addr);
    }

    return 0;
}
//進(jìn)程B
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h> //declare O_CREAT O_RDWR

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

void cli_exit(int signo)
{
    printf("client exit ...\n");
    exit(0);
}

int main()
{
    struct sigaction act;
    act.sa_handler = cli_exit;

    key_t shm_key = ftok("./readshm", 1);
    if (shm_key == -1 )
    {
        perror("ftok error");
        return -1;
    }

    int shm_id = shmget(shm_key, 0, 0);
    if (shm_id == -1)
    {
        perror("shmget");
        return -1;
    }
    
    char* addr = (char*)shmat(shm_id, NULL, 0);
    if (addr == (char*)(-1))
    {
        perror("shmat");
        return -1;
    }

    int sem_id = semget(shm_key, 0, 0 );
    if (sem_id == -1 )
    {
        perror("semget");
        return -1;
    }

    struct sembuf v = {1, 1, SEM_UNDO};
    struct sembuf p = {0, -1, SEM_UNDO};

    sigaction(SIGINT, &act, NULL);
    while(1)
    {
        semop(sem_id, &p, 1);
        printf("ser:>%s\n", addr );

        printf("cli:>");
        scanf("%s", addr);
        semop(sem_id, &v, 1);
    }

    return 0;
}

4. 使用信號實現(xiàn)共享內(nèi)存的同步

其實就是使用kill和signal發(fā)送信號來實現(xiàn),這里不再實現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容