命名信号量不会像进程之间的同步中假设的那样工作

问题描述 投票:0回答:1

给定写入器和读取器代码如下,该程序将无法按预期工作:

预期行为:在我们启动 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

c synchronization ipc semaphore
1个回答
0
投票

您的信号量没有被正确清理。您需要向编写器添加 sem_unlink() 以“真正”销毁信号量。 如果你不这样做,所有的 sem_opens() 都会得到信号量上次拥有的任何值(读者在打印结果后会执行 sem_post ,所以它不是 0)

您可以尝试使用 O_CREAT | 打开来确认编写器中的 O_EXCL 会告诉您信号量已经存在。

© www.soinside.com 2019 - 2024. All rights reserved.