(P31)system v信號量

1.用信號量實現使用臨界區的進程間互斥

  • 要求:
    (1)父進程輸出O,子進程輸出X,只有一個進程能夠進入該臨界區
    (2)當父進程sleep的時候,子進程也不能獲得臨界區資源,因爲父進程的P操作已經鎖定了資源,直到它釋放資源(V操作)纔可以。
    同理,子進程的X也是同對出現的,不可能出現輸出奇數個X,然後被父進程搶佔輸出O
    在這裏插入圖片描述
  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P30sem02.cpp
//
// Created by wangji on 19-8-13.
//

// p30 system v信號量(二)

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/wait.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <wait.h>

#define ERR_EXIT(m) \
        do \
        { \
             perror(m); \
             exit(EXIT_FAILURE);    \
        } while (0);

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};

// -c
int sem_creat(key_t key)
{
    int semid;
    semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
    if (semid == -1)
    {
        ERR_EXIT("semget");
    }
    return semid;
}


int sem_open(key_t key)
{
    int semid;
    semid = semget(key, 0, 0);
    if (semid == -1)
    {
        ERR_EXIT("semget");
    }
    return semid;
}

// -s <val>
int sem_setval(int semid, int val)
{
    union semun su;
    su.val = val;
    int ret;
    ret = semctl(semid, 0, SETVAL, su);
    if (ret == -1)
    {
        ERR_EXIT("setval")
    }
    return 0;
}

// -g
int sem_getval(int semid)
{
    int ret;
    ret = semctl(semid, 0, GETVAL);
    if (ret == -1)
    {
        ERR_EXIT("getval")
    }
    printf("sem.val = %d\n", ret);
    return ret;
}

// -d
int sem_d(int semid)
{
    int ret;
    ret = semctl(semid, 0, IPC_RMID, NULL);
    if (ret == -1)
    {
        ERR_EXIT("rm_sem")
    }
    return 0;
}

// -p
int sem_p(int semid)
{
    struct sembuf sembuf;
    sembuf.sem_num = 0;
    sembuf.sem_op = -1;
    sembuf.sem_flg = 0;
    int ret;
    ret = semop(semid, &sembuf, 1);
    if (ret == -1)
    {
        ERR_EXIT("sem_p")
    }
    return ret;
}

// -v
int sem_v(int semid)
{
    struct sembuf sembuf;
    sembuf.sem_num = 0;
    sembuf.sem_op = 1;
    sembuf.sem_flg = 0;
    int ret;
    ret = semop(semid, &sembuf, 1);
    if (ret == -1)
    {
        ERR_EXIT("sem_v")
    }
    return ret;
}

// -f
int sem_getmode(int semid)
{
    union semun su;
    struct semid_ds sem;
    su.buf = &sem;
    int ret =semctl(semid, 0, IPC_STAT, su);
    if (ret == -1)
    {
        ERR_EXIT("semctl");
    }
    printf("current permissions is %o\n", su.buf->sem_perm.mode);

    return ret;
}


// -m <mode>
int sem_setmode(int semid, char* mode)
{
    union semun su;
    struct semid_ds sem;
    su.buf = &sem;

    int ret =semctl(semid, 0, IPC_STAT, su);
    if (ret == -1)
    {
        ERR_EXIT("semctl");
    }
    printf("current permissions is %o\n", su.buf->sem_perm.mode);

    sscanf(mode, "%o", (unsigned int*)&su.buf->sem_perm.mode);
    ret = semctl(semid, 0, IPC_SET, su);
    if (ret == -1)
    {
        ERR_EXIT("semctl");
    }

    printf("permission updated..\n");
    return ret;
}

void print(int sigmid, char* s)
{
    int i = 0;
    int pause_time;
    srand(getpid());//以當前進程最爲隨機數的發生種子
    for (int i = 0; i < 10; ++i)
    {
        sem_p(sigmid);//互斥操作

        //臨界區開始,同時只有一個進程能夠使用它
        printf("%s", s);
        fflush(stdout);//立刻輸出到標準輸出上來,清空緩衝區
        pause_time = rand() % 3;//去個隨機數0,1,2,不會超處3s
        sleep(pause_time);//即使該進程屬於slepp狀態,也不能被其它進程所使用
        printf("%s", s);
        fflush(stdout);
        //臨界區結束:

        sem_v(sigmid);//互斥操作

        pause_time = rand() % 2;
        sleep(pause_time);
    }
}





int main(int argc, char** argv)
{
    int sigmid = sem_creat(IPC_PRIVATE);//父子進程間互斥可以用IPC_PRIVATE,私有的信號量集
    sem_setval(sigmid, 0);//由於信號量集初始值=0,那麼任何一個進程都不能進入到臨界區,p操作的時候就以及阻塞了
    pid_t pid = fork();

    if (pid == -1)
    {
        ERR_EXIT("fork");
    }

    if (pid > 0)
    {
        sem_setval(sigmid, 1);//父進程就能夠進入臨界區打印O了
        print(sigmid, "o");//父進程睡眠幾秒鐘,時間片輪轉給其它進程,子進程也無法進入臨界區
        wait(nNULL);//父進程等待子進程退出
        sem_d(sigmid);
    }
    else
    {
        print(sigmid, "x");
    }
    return 0;
}
  • 測試結果如下:
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章