给定写入器和读取器代码如下,该程序将无法按预期工作:
预期行为:在我们启动 writer 后,通常在 2 秒后,我们从另一个终端启动 reader,由于信号量保护,reader 应该阻塞等待信号量为 1,然后打印出派生的消息。
然而,结果却恰恰相反。
首先,我们在终端中启动 writer 进程,输出如下:
shared mem address: 0x10cc68000 [0..511]
backing file: /dev/shm/shMemEx
8
7
6
5
4
3
2
1
等待2秒,然后运行阅读器进程,几乎立即,阅读器打印出信号量“受保护”的数据:
mac@mbp cpp % ./r
hello worldThis is the way the world ends...
作者.c:
// compilation on macos: gcc -o w writer.c -lpthread
// compilation on linux: gcc -o w writer.c -lrt -lpthread
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include "shmem.h"
int main(){
int fd = shm_open(BackingFile, O_RDWR | O_CREAT, AccessPerms); // r/w + create
if(fd < 0){
report_and_exit("can't open shared mem segment");
}
ftruncate(fd, ByteSize); // adjust the backing file size
caddr_t memptr = mmap(
NULL, // let system pick shared mem seg pos
ByteSize, // bytes
PROT_READ | PROT_WRITE, // access protections
MAP_SHARED, // shared mem visible to other proc
fd, // fd
0 // offset: start at 0
);
if((caddr_t) -1 == memptr){ // mmap creation check
report_and_exit("can't get segment...");
}
fprintf(stderr, "shared mem address: %p [0..%d]\n", memptr, ByteSize - 1);
fprintf(stderr, "backing file: /dev/shm%s\n", BackingFile );
sem_t* semptr = sem_open( // create semaphore to lock the shared mem
SemaphoreName, // name
O_CREAT, // op = create
AccessPerms, // protection perms
0 // init value
);
if(semptr == (void*) -1){ // semaphore creation check
report_and_exit("sem_open");
}
// fprintf(stderr, "semaphore address: %p\n", semptr);
strcpy(memptr, MemContents); // write to mem (copy ascii bytes to shared mem seg)
int num = 8; // wait 8s -> reader should not access
while(num > 0){
printf("%d\n", num);
num--;m
sleep(1);d
}
if(sem_post(semptr) < 0){ // incr semaphore after writing finish, let reader access
report_and_exit("sem_post");
}
sleep(6); // for reader to be scheduled
// cleanups
munmap(memptr, ByteSize); // unmap the storage
close(fd);
sem_close(semptr);
shm_unlink(BackingFile); // unlink from the backing file
return 0;
}
读者.c
// compilation on macos: gcc -o r reader.c -lpthread
// compilation on linux: gcc -o r reader.c -lrt -lpthread
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include "shmem.h"
int main(){
int fd = shm_open(BackingFile, O_RDWR, AccessPerms); // r/w without create
if(fd < 0){
report_and_exit("can't get file descriptor...");
}
caddr_t memptr = mmap( // ptr to mem
NULL, // let system pick mem seg pos
ByteSize, // bytes of shared mem
PROT_READ | PROT_WRITE, // access protections
MAP_SHARED, // shared mem is visible to other proc
fd, // fd
0 // offset: start at 0
);
if((caddr_t) -1 == memptr){ //
report_and_exit("can't access segment...");
}
sem_t* semptr = sem_open( // semaphore as mutex
SemaphoreName, // name
O_CREAT, // create semaphore if not existed
AccessPerms, // protection perms
0 // init val
);
if(semptr == (void*) -1){ // err return if semaphore create failed (non-block)
report_and_exit("sem_open");
}
if(!sem_wait(semptr)){ // wait until semaphore != 0, then start reading
int i;
for(i=0; i<strlen(MemContents); i++){
write(STDOUT_FILENO, memptr+i, 1); // one byte at a time
}
sem_post(semptr); // incr semaphore by 1
}
munmap(memptr, ByteSize); // cleanup
close(fd);
sem_close(semptr);
unlink(BackingFile); // if omitted, file will persist after program exit
return 0;
}
shmem.h
#define ByteSize 512
#define BackingFile "/shMemEx"
#define AccessPerms 0644
#define SemaphoreName "mysemaphore"
#define MemContents "This is the way the world ends...\n"
void report_and_exit(const char* msg){
perror(msg);
exit(-1);
}
代码来源:https://opensource.com/sites/default/files/erated-content/inter-process_communication_in_linux.pdf
您的信号量没有被正确清理。您需要向编写器添加 sem_unlink() 以“真正”销毁信号量。 如果你不这样做,所有的 sem_opens() 都会得到信号量上次拥有的任何值(读者在打印结果后会执行 sem_post ,所以它不是 0)
您可以尝试使用 O_CREAT | 打开来确认编写器中的 O_EXCL 会告诉您信号量已经存在。