這篇帖子主要是記錄一下自己使用訊號量遇到的坑。
首先是需求:建立兩個程式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
由此實現了同步讀寫:
-----------------------------------------------------------------------------
該文章為原創,轉載請註明出處。
-----------------------------------------------------------------------------