Linuxc - 信號量

linuxc-信號量

​ 信號燈(semaphore),也叫信號量,它是不一樣進程間或一個給定進程內部不一樣線程間同步的機制。linux

​ 信號燈種類:編程

​ 1. posix 有名信號燈 (見 posix信號量的應用--生產者-消費者)函數

​ 2. posix 基於內存的信號燈(無名信號燈 見 posix信號量的應用--生產者-消費者) 線程

​ 3. System V 信號燈 (IPC對象,如下主要說明);unix

​ 信號燈:code

​ 1. 二值信號燈:值爲 0 或 1。與互斥鎖相似,資源可用時值爲1,不可用時值爲 0;對象

​ 2. 計數信號燈:值在 0 到 n 之間。用來統計資源,其值表明可用資源數;隊列

等待操做時等待信號燈的值變爲大於0,而後將其減一;而釋放操做則相反,用來喚醒等待資源的進程或者線程;進程

System V 信號量

​ 詳情見《unix環境高級編程》15.8小節ip

在Linux 系統中,使用信號量一般分爲如下幾個步驟:

  1. 建立信號量或得到系統已存在的信號量,此時須要調用 semget() 函數。不一樣進程經過使用同一個信號鍵值來得到同一個信號量;
  2. 初始化信號量,此時使用 senctl() 函數的 SETVAL 操做。當使用二維信號量時,一般將信號量初始化爲1;
  3. 進行信號量的PV操做,此時調用 semop() 函數。這一步是實現進程之間的同步和互斥的核心工做部分;
  4. 若是不須要信號量,則從系統中刪除它,此時使用semctl() 函數的IPC_RMID 操做。此時須要注意,在程序中不該該出現對已經被刪除的信號量的操做;

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    
    /*建立或打開信號量集
     key :和信號燈集關聯的key 值
     nsems:信號燈集中包含的信號燈數目
     semflg:信號燈集的訪問權限,一般爲IPC_CREAT|0666
     返回值:成功,信號燈集ID   出錯,-1
    
    */
    int semget(key_t key, int nsems, int semflg );
    
    /*信號量集的控制
     semid:信號燈集ID
     semnum:要修改的信號燈編號
             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)   
            };  
     cmd :GETVAL:獲取信號燈的值
           SETVAL:設置信號燈的值
           IPC_RMID:從系統中刪除信號燈集合
     返回值:成功,0    出錯,-1
    */
    int semctl(int semid, int semnum, int cmd, union semun arg);
    
    /*對信號量集的操做
     semid:信號燈集ID
     struct sembuf 結構體每個元素表示一個操做;
             struct sembuf  {  
                unsigned short sem_num; //要操做的信號燈的編號  
                short sem_op;   //  0: 等待,知道信號燈的值變爲0  
                                //  1: 釋放資源,V操做   
                                // -1: 分配資源,P操做  
                short sem_flg; //0,IPC_NOWAIT,SEM_UNDO  
            }  
     nops:要操做的信號燈的個數
     返回值:成功,0    出錯,-1
    */
    int semop(int semid,struct sembuf  *opsptr,size_t nops);

示例

仍是以前POSIX信號量的生產者和消費者,使用的是system V的信號量實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>

#define MYFIFO             "myfifo"
#define BUFFER_SIZE     3
#define UNIT_SIZE         5
#define RUN_TIME         30
#define DELAY_TIME_LEVELS 5

#define MUTEX 0
#define FULL  1
#define AVAIL 2

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
};

void *producer(void *arg);//生產者
void *customer(void *arg);//消費者

int fd, semid;
time_t end_time;


int p(int semid, int semnum)
{
    struct sembuf sops = {semnum, -1, SEM_UNDO};
    return semop(semid, &sops, 1);
}

int v(int semid, int semnum)
{
    struct sembuf sops = {semnum, +1, SEM_UNDO};
    return semop(semid, &sops, 1);
}

void * signal_set(int signo, void (*func)(int))
{
    int ret;
    struct sigaction sig;
    struct sigaction osig;

    sig.sa_handler = func;
    sigemptyset(&sig.sa_mask);
    sig.sa_flags = 0;
#ifdef SA_RESTART
    sig.sa_flags |= SA_RESTART;
#endif
    ret = sigaction(signo, &sig, &osig);

    if (ret < 0) 
        return SIG_ERR;
    else 
        return osig.sa_handler;
}

void sigint(int sig)
{
    unlink(MYFIFO);
    semctl(semid, 0, IPC_RMID, 0);
    semctl(semid, 1, IPC_RMID, 0);
    semctl(semid, 2, IPC_RMID, 0);
    exit(sig);
}

int main(int argc, const char *argv[])
{
    int ret, key;
    union semun arg;
    pthread_t thrd_prd_id,thrd_cst_id;

    signal_set(SIGINT, sigint);//使用信號處理異常,防止程序異常退出,某些資源沒有回收
    signal_set(SIGTERM, sigint);
    signal_set(SIGQUIT, sigint);
    signal_set(SIGKILL, sigint);
    srand(time(NULL));
    end_time = time(NULL) + RUN_TIME;

    //建立管道
    if ((mkfifo(MYFIFO, 0644) < 0) && (errno != EEXIST)) {
        fprintf(stderr, "mkfifo error!");
        exit(-1);
    }

    //打開管道
    fd = open(MYFIFO, O_RDWR);
    if (fd == -1) {
        fprintf(stderr, "open fifo error");
        goto err1;
    }

    //系統建立IPC通信(消息隊列、信號量、共享內存)時必須指定一個ID值。一般狀況下,該id值經過ftok函數獲得。
    key = ftok("/tmp", 0x66);
    if (key < 0) {
        fprintf(stderr, "ftok key error");
        goto err1;
    }
    //建立了三個信號量
    semid = semget(key, 3, IPC_CREAT | 0600);
    if (semid == -1) {
        fprintf(stderr, "create semget error");
        goto err1;
    }
    //對信號量設置初始值
    arg.val = 1;
    ret = semctl(semid, MUTEX, SETVAL, arg);
    if (ret < 0) {
        fprintf(stderr, "ctl sem error");
        semctl(semid, MUTEX, IPC_RMID, arg);
        goto err2;
    }
    arg.val = BUFFER_SIZE;
    ret = semctl(semid, AVAIL, SETVAL, arg);
    if (ret < 0) {
        fprintf(stderr, "ctl sem error");
        semctl(semid, AVAIL, IPC_RMID, arg);
        goto err2;
    }
    arg.val = 0;
    ret = semctl(semid, FULL, SETVAL, arg);
    if (ret < 0) {
        fprintf(stderr, "ctl sem error");
        semctl(semid, FULL, IPC_RMID, arg);
        goto err2;
    }
    //取信號量的值
    ret = semctl(semid, 0, GETVAL, arg);
    printf("after semctl setval sem[0].val = [%d]\n", ret);
    ret = semctl(semid, 1, GETVAL, arg);
    printf("after semctl setval sem[1].val = [%d]\n", ret);
    ret = semctl(semid, 2, GETVAL, arg);
    printf("after semctl setval sem[2].val = [%d]\n", ret);

    //建立兩個線程
    ret = pthread_create(&thrd_prd_id, NULL, producer, NULL);
    if (ret != 0) {
        fprintf(stderr, "producer pthread_create error");
        goto err2;
    }
    ret = pthread_create(&thrd_cst_id, NULL, customer, NULL);
    if (ret != 0) {
        fprintf(stderr, "customer pthread_create error");
        goto err2;
    }
    pthread_join(thrd_prd_id, NULL);
    pthread_join(thrd_cst_id, NULL);
    
    close(fd);

err2:
    semctl(semid, 0, IPC_RMID, 0);
    semctl(semid, 1, IPC_RMID, 0);
    semctl(semid, 2, IPC_RMID, 0);
err1:
    unlink(MYFIFO);

    return 0;
}

void *producer(void *arg)
{
    int real_write;
    int delay_time;

    while (time(NULL) < end_time) {
        delay_time = rand()%DELAY_TIME_LEVELS;
        sleep(delay_time);
        
        //p操做
        if (p(semid, AVAIL) < 0) {
            fprintf(stderr, "p operate error");
            kill(0, SIGQUIT);
        }
        if (p(semid, MUTEX) < 0) {
            fprintf(stderr, "p operate error");
            kill(0, SIGQUIT);
        }
        printf("\nproducer have delayed %d seconds\n", delay_time);

        //生產者寫入數據  執行的操做
        if (-1 == (real_write = write(fd, "hello", UNIT_SIZE))) {
            if (errno == EAGAIN) {
                printf("The buffer is full, please wait for reading!\n");
            }
        } else {
            printf("producer writes %d bytes to the FIFO\n", real_write);
        }
        //v操做
        if (v(semid, FULL) < 0) {
            fprintf(stderr, "v operate error");
            kill(0, SIGQUIT);
        }
        if (v(semid, MUTEX) < 0) {
            fprintf(stderr, "v operate error");
            kill(0, SIGQUIT);
        }
    }
    pthread_exit(NULL);
}

void *customer(void *arg)
{
    unsigned char read_buffer[UNIT_SIZE];
    int real_read;
    int delay_time;

    while (time(NULL) < end_time) {
        delay_time = rand()%DELAY_TIME_LEVELS;
        sleep(delay_time);
        if (p(semid, FULL) < 0) {
            fprintf(stderr, "p operate error");
            kill(0, SIGQUIT);
        }
        if (p(semid, MUTEX) < 0) {
            fprintf(stderr, "p operate error");
            kill(0, SIGQUIT);
        }
        memset(read_buffer, 0, UNIT_SIZE);
        printf("\nCustomer have delayed %d seconds\n", delay_time);

        //消費 操做
        if (-1 == (real_read = read(fd, read_buffer, UNIT_SIZE))) {
            if (errno == EAGAIN) {
                printf("The buffer is empty, please wait for writing!\n");
            }
        } else {
            printf("customer reads %d bytes from the FIFO\n", real_read);
        }

        if (v(semid, AVAIL) < 0) {
            fprintf(stderr, "v operate error");
            kill(0, SIGQUIT);
        }
        if (v(semid, MUTEX) < 0) {
            fprintf(stderr, "v operate error");
            kill(0, SIGQUIT);
        }
    }

    pthread_exit(NULL);
}

相關文章
相關標籤/搜索