利用訊號量semaphore實現兩個程式讀寫同步 Linux C

aalanwyr發表於2022-02-23

    這篇帖子主要是記錄一下自己使用訊號量遇到的坑。

    首先是需求:建立兩個程式A,B。A往buffer中寫,B讀。兩個程式利用命名管道進行通訊,並實現讀寫同步。即A寫完後通知B讀,B讀完後通知A寫。

    如果A,B兩個程式各自獨立操作的話,很容易出現下列情況。 看哪個程式先搶佔到這個buffer,由於write和read這個buffer都會阻塞另一個程式,所以可能會出現一個程式一直寫資料,然後讀程式會讀到多條資料。

    解決方案,利用linux POSIX中的semaphore完成讀寫同步。設定兩個訊號量semwr,semrd;semwr控制讀,初始化值設定為1(in unlocked state),semrd控制寫,初始化設定為0(in locked state)。並由讀程式釋放寫鎖,由寫程式釋放讀鎖。(一個訊號量是無法完成讀寫同步的)。

    讀程式:

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


#define MAXLINE 100
#define CONTEXT "HELLO WORLD"
#define FILENAME "MY_FIFO"
#define LOOP 200
#define SEMRD "sem_read"
#define SEMWR "sem_write"

int main(int argc,char *argv[]){
    /* create the named pipe fifo*/
    int fd;
    int ret;
    ret = mkfifo(FILENAME,0666);
    if(ret!=0){
        perror("mkfifo");
    }
    fd=open(FILENAME,O_RDONLY);
    if(fd<0){
        perror("open fifo");
    }
    /*open the semaphore*/
    sem_t *semwr,*semrd;
    int pwr,prd;
    semwr=sem_open(SEMWR,O_CREAT,0666,1);
    semrd=sem_open(SEMRD,O_CREAT,0666,0);
    if(semwr==(void*)-1 ||semrd==(void*)-1){
        perror("sem_open failure");
    }
    printf("sem address\n");
    printf("semwr=%p\n",semwr);
    printf("semrd=%p\n",semrd);
    /*get this value*/
    sem_getvalue(semwr,&pwr);
    sem_getvalue(semrd,&prd);
    printf("wr value=%d\n",pwr);
    printf("rd value=%d\n",prd);
    /* communication period*/
    int i=LOOP;
    while (i--){
        /*lock*/
        sem_wait(semrd);
        char recv[MAXLINE]={0};
        read(fd,recv,sizeof(recv));
        printf("read from my_fifo buf=[%s]\n",recv);
        sem_post(semwr);
    }
    /*close the file*/
    close(fd);
    sem_close(semwr);
    sem_close(semrd);
    /* release resource*/
    unlink(FILENAME);
    sem_unlink(SEMWR);
    sem_unlink(SEMRD);
    return 0;

}

  寫程式:

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

#define MAXLINE 100
#define CONTEXT "HELLO WORLD"
#define FILENAME "MY_FIFO"
#define LOOP 200
#define SEMRD "sem_read"
#define SEMWR "sem_write"

int main(int argc,char *argv[]){
    /* create the named pipe fifo*/
    int fd;
    int ret;
    ret = mkfifo(FILENAME,0666);
    if(ret!=0){
        perror("mkfifo");
    }
    fd=open(FILENAME,O_WRONLY);
    if(fd<0){
        perror("open fifo");
    }
    /*open the semaphore*/
    sem_t *semwr,*semrd;
    int pwr,prd;
    semwr=sem_open(SEMWR,O_CREAT,0666,1);
    semrd=sem_open(SEMRD,O_CREAT,0666,0);
    if(semwr==(void*)-1 ||semrd==(void*)-1){
        perror("sem_open failure");
    }
    printf("sem address\n");
    printf("semwr=%p\n",semwr);
    printf("semrd=%p\n",semrd);
    /*get this value*/
    sem_getvalue(semwr,&pwr);
    sem_getvalue(semrd,&prd);
    printf("wr value=%d\n",pwr);
    printf("rd value=%d\n",prd);
    /* communication period*/
    int i=LOOP;
    char send[MAXLINE]=CONTEXT;
    while (i--){
        /*lock*/
        sem_wait(semwr);
        write(fd,send,strlen(send));
        printf("send to my_fifo buf\n",send);
        sem_post(semrd);
    }
    /*close the file*/
    close(fd);
    sem_close(semwr);
    sem_close(semrd);
    /* release resource*/
    unlink(FILENAME);
    sem_unlink(SEMWR);
    sem_unlink(SEMRD);
    return 0;

}

     需要注意的是,POSIX中的訊號量是隨核心持續的,如果訊號量不sem_unlink的話,該命名訊號量會常駐在kernel之中,即使程式結束了也會存在,而sem_open建立訊號量時,如果該named semaphore存在核心中,你設定的初始化引數是無效的(一定要man 3 sem_open 看看引數的解釋,別百度,垃圾文件太多,看官方的最好),所以用完之後需要統一釋放資源。

    gcc 編譯的時候需要加上 -pthread 

    即 gcc XXXX.c -pthread -o xxx

    由此實現了同步讀寫:

-----------------------------------------------------------------------------

該文章為原創,轉載請註明出處。

-----------------------------------------------------------------------------

相關文章