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