Linux信號量和共享內存

實現代碼

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>

static int set_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static void del_sem_set(void);

/* 定義自己的semun聯合體*/
union semun {
    int                 val;
    struct semid_ds     *buf;
    unsigned short      *array;
    struct seminfo      *__buf;
};

/* 定義全局變量sem_id保存信號量集的標識符*/
static int sem_id;
/* 定義全局變量shm_id保存共享內存的標識符*/
static int shm_id;

int main()
{
    int i;
    pid_t pid;
    char ch1, ch2;
    char* pData = NULL;

    /* 創建信號量集*/
    sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);  
    if(sem_id == -1) {
        fprintf(stderr, "Failed to create semaphore set. \n");
        exit(EXIT_FAILURE);
    }
    if(!set_semvalue()) {                   /* 設置信號量的值*/
        fprintf(stderr, "Failed to initialize semaphore\n");
        exit(EXIT_FAILURE);
    }
    shm_id = shmget(IPC_PRIVATE, 4096, 0666 | IPC_CREAT);   
    if(shm_id == -1) {
        fprintf(stderr, "Failed to create sharememory. \n");
        del_sem_set();
        exit(EXIT_FAILURE);
    }

    pid = fork();                           /* 創建子進程*/
    if(pid == -1) {
        perror("fork failed");
        shmctl(shm_id, IPC_RMID, 0);        /* 刪除共享內存*/
        del_sem_set();
        exit(EXIT_FAILURE);
    }
    else {
        srand((unsigned int)getpid());      /* 爲隨機數播種*/
        pData = (char*)shmat(shm_id, 0, 0); /* 綁定*/
        if (pid == 0) {                     /* 子進程*/
            do {
                semaphore_p();
                ch1 = *pData;               /* 讀*/
                ch2 = *(pData + 1);
                if(ch2 == '@') {
                    *pData = tolower(ch1);  /* 寫*/
                    *(pData + 1) = '#';
                }
                if(ch1 == 'Z') break;
                semaphore_v();
               
                sleep(1);
            }while(1);
        }
        else {                              /* 父進程*/
            for(i=0; i < 26; i++){
                semaphore_p();
                *pData = 'A' + rand() % 26; /* 寫*/
                if(i == 25) *pData = 'Z';
                printf("%c", *pData);
                *(pData + 1) = '@';
                semaphore_v();
                sleep(1);

                do {
                    semaphore_p();
                    ch1 = *pData;           /* 讀*/
                    ch2 = *(pData + 1);
                    if(ch2 == '#') {
                        printf("%c", ch1);
                        fflush(stdout);
                        semaphore_v();
                        break;
                    }
                    semaphore_v();
                }while(1);
            }
        }

        shmdt(pData);                       /* 分離*/
    }

    if(pid > 0) {                           /* 父進程*/
        wait(NULL);                         /* 等待子進程退出*/
        shmctl(shm_id, IPC_RMID, 0);        /* 刪除共享內存*/
        del_sem_set();                      /* 刪除信號量集*/
    }

    printf("\n%d - finished\n", getpid());
    exit(EXIT_SUCCESS);
}

/* 設置信號量的值*/
static int set_semvalue(void)
{
    union semun sem_union;

    sem_union.val = 1;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
        return 0;

    return 1;
}

/* P操作,獲取信號量*/
static int semaphore_p(void)
{
    struct sembuf sem_b;

    sem_b.sem_num  = 0;
    sem_b.sem_op   = -1;
    sem_b.sem_flg  = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed/n");
        return 0;
    }

    return 1;
}

/* V操作,釋放信號量*/
static int semaphore_v(void)
{
    struct sembuf sem_b;

    sem_b.sem_num  = 0;
    sem_b.sem_op   = 1;
    sem_b.sem_flg  = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_v failed/n");
        return 0;
    }

    return 1;
}

/* 刪除信號量集*/
static void del_sem_set(void)
{
    union semun sem_union;

    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        fprintf(stderr, "Failed to delete semaphore/n");
}

描述:父進程循環隨機產生大寫字母,並寫入共享內存,子進程從共享內存中讀取到該字母后將其轉換爲相應的小寫字母並將該小寫字母寫入共享內存,最後父進程讀取共享內存中被修改後的小寫字母並輸出。

運行結果

MmDdWwLlSsWwMmUuIiYyQqQqRrYyWwKkSsJjSsZz

程序分析

1)試說明父子進程都包括讀和寫,但它們的順序有什麼不同?
子進程時先讀取共享內存再寫入,父進程是先寫入共享內存再讀取。根據程序的邏輯,父進程向共享內存寫入數據,子進程收到並修改之後父進程再讀取並顯示。

2)父進程讀寫分別各用一次信號量互斥訪問,而子進程讀寫共用一次型號量互斥訪問,試說明原因?
父進程讀寫各用一次信號量互斥訪問,而子進程只用了一次。這是因爲父進程寫完以後不能馬上讀取,要釋放信號量讓子進程讀取並修改。所以父進程的信號量控制要讀寫分離。

3)共享內存的第二個內存用特殊字符“#”或“@”有什麼用途?
共享內存中的第二個字符“#”或“@”用來標識當前字符爲大寫還是小寫,若爲“#”則表明當前字符爲小寫,就可以輸出顯示。若爲“@”就表明當前字符爲大寫,需要子進程修改。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章