1. 程式人生 > 其它 >LINUX中訊號量的使用

LINUX中訊號量的使用

1. 概述

訊號量分為有名訊號量(named semaphore),無名訊號量(unnamed semaphore)。

(這裡說的訊號量主要是指semaphore.h中的訊號量)

  1. 有名訊號量由sem_open開啟,無名訊號量由sem_init開啟.
  2. 有名訊號量通過sem_unlink刪除, 無名訊號量通過sem_destroy刪除訊號量
  3. 有名訊號量由核心持續,正是因此多個程序間才能利用它;無名訊號量一般是執行緒之間使用,也可以搭配共享記憶體在多程序之間使用。

2. 函式原型

#include <semaphore.h>

// 有名訊號量的操作
sem_t *sem_open(const char *, int, ...);
int    sem_unlink(const char *);
int    sem_close(sem_t *);

// 無名訊號量的操作
int    sem_init(sem_t *, int, unsigned int);
int    sem_destroy(sem_t *);

// 獲取訊號量值
int    sem_getvalue(sem_t *, int *);

// 發出訊號,即釋放擁有權
int    sem_post(sem_t *);
// 等待訊號,即獲取擁有權
int    sem_trywait(sem_t *);
int    sem_wait(sem_t *);

3. 用訊號量解生產者-消費者問題

#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUMBER  50000
#define CHILD   5
#define BUFSIZE 10

// critical resource
int fd;

// semaphore
sem_t* empty;
sem_t* full;
sem_t* mutex;

void comsumer() {
    int buf_out = 0;
    int data = 0;
    int cnt = 0;
    for (int k = 0; k < NUMBER / CHILD; k++) {
        sem_wait(full);
        sem_wait(mutex);
        
        // fetch buf_out
        lseek(fd, BUFSIZE * sizeof(int), SEEK_SET);
        read(fd, (char*)&buf_out, sizeof(int));

        cnt++;
        lseek(fd, sizeof(int) * buf_out, SEEK_SET);
        read(fd, (char*)&data, sizeof(int));
        printf("%d comsume %d %d\n", getpid(), data, cnt);
        fflush(stdout);
        
        // write back
        buf_out = (buf_out + 1) % BUFSIZE;
        lseek(fd, BUFSIZE * sizeof(int), SEEK_SET);
        write(fd, (char *)&buf_out, sizeof(int));

        sem_post(mutex);
        sem_post(empty);
    }
    printf("%d total consume %d\n", getpid(), cnt);
}

void producer() {
    int buf_in = 0;
    for (int i = 0 ; i < NUMBER; i++) {
        sem_wait(empty);
        sem_wait(mutex);
        
        lseek(fd, buf_in * sizeof(int), SEEK_SET);
        write(fd, (char*)&i, sizeof(int));
        buf_in = (buf_in + 1) % BUFSIZE;
        printf("produce %d\n", i);
        fflush(stdout);

        sem_post(mutex);
        sem_post(full);
    }
}


int main() {
    mutex = sem_open("mutex", O_CREAT | O_EXCL, 0644, 1); 
    full = sem_open("full", O_CREAT | O_EXCL, 0644, 0); 
    empty = sem_open("empty", O_CREAT | O_EXCL, 0644, BUFSIZE); 
    
    int out_index = 0;
    fd = open("buffer.dat", O_CREAT | O_RDWR | O_TRUNC, 0666);
    lseek(fd, BUFSIZE * sizeof(int), SEEK_SET);
    write(fd, (char *)&(out_index), sizeof(int));

    pid_t p;
    // create producer
    if ((p = fork()) == 0) {
        producer();
        return 0;
    } else if (p < 0){
        printf("Fail to fork!\n");
        return -1;
    }

    // create comsumer
    for (int j = 0; j < CHILD ; j++)
    {
        if ((p = fork()) == 0) {
            comsumer();
            return 0;
        } else if (p < 0) {
            printf("Fail to fork!\n");
            return -1;
        }
    }
    int cnt = 0;
    printf("wait children!\n");
    pid_t pid;
    while (pid = waitpid(-1, NULL, 0)) {
        if (errno == ECHILD) {
            break;
        }
        cnt ++;
        printf("pid: %d end | sum: %d\n", pid, cnt);
    }

    sem_unlink("empty");
    sem_unlink("full");
    sem_unlink("mutex");

    return 0;
}

編譯命令:

g++ pcc.c -o test -lpthread

參考資料

[1] 訊號量

[2] semaphore.h