1. 程式人生 > >基於記憶體的posix訊號量用法

基於記憶體的posix訊號量用法

posix訊號量的兩種形式

基於檔案系統

不打算詳細講細節,提供兩種模板,簡單的用法

#include <semaphore.h>
sem_t* sem_open(const char*name,int oflag,.../*mode_t mode,unsigned int value*/)

oflag引數我一般指定O_CREAT|O_RDWR,因為一般不儲存訊號量,畢竟總覺得這東西會一直佔系統資源,所以我建立時指定O_CREAT,意思就是如果不存在和這個訊號量,則建立並初始化它,若存在則為這個訊號量重新賦值。一般程式退出時刪除它。

mode引數可以參考man 2 open。我一般指定#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH),方便好用。

int sem_close(semt_t *sem);

注意,close一個訊號量並沒釋放它的資源,posix訊號量至少是隨核心持續的,close只是“扯斷這個訊號量和程序的聯絡”。我們要真的刪除某個訊號量時,應該用

int sem_unlink(sem_t *sem);

刪除僅當沒有任何程序還開啟這個訊號量時成功

PV操作

int sem_wait(sem_t *sem);//p opration
int sem_post(sem_t *sem);//v opration

這就是作業系統課上講的PV操作

demo

兩個程序,mmap共享一個記憶體,通過訊號量來管理這個共享記憶體。一個程序從stdin收取字串,並寫入共享記憶體,然後V訊號量

另一個程序P訊號量,拿走字串,v訊號量。輸出到stdout。

傳送程式輸入exit,可以將接收程式終止

注意 編譯時要連結 lrt或者lpthread

Send.c

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
// void *mmap(void *addr, size_t length, int prot, int flags, // int fd, off_t offset); char buf[1024]; int main() { sem_t *mutex = sem_open("wudi_sem",O_CREAT|O_RDWR,FILE_MODE,0); int fd = open("0xabc",O_CREAT|O_RDWR,FILE_MODE); char *sharedFile = mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(!sharedFile) { perror("mmap:"); sem_unlink("wudi_sem"); exit(-1); } write(fd,buf,1024); close(fd); while(scanf("%s",buf)!=EOF) { memcpy(sharedFile,buf,128); sem_post(mutex); } sem_unlink("wudi_sem"); printf("unlink mutex\n"); return 0; }

Recv.c

#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
// void *mmap(void *addr, size_t length, int prot, int flags,
//                   int fd, off_t offset);
char buf[1024];
int main()
{
    sem_t *mutex = sem_open("wudi_sem",O_RDWR);
    int fd = open("0xabc",O_RDWR);
    if(fd<0)
    {
        perror("open:");
        exit(-1);
    }
    char *sharedFile = mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(!sharedFile)
    {
        perror("mmap:");
        sem_unlink("wudi_sem");
        exit(-1);
    }
    close(fd);

    while(1)
    {
        sem_wait(mutex);
        memcpy(buf,sharedFile,128);
        if(strcmp(buf,"exit")==0)
        {
            printf("ready to exit\n");
            break;
        }
        printf("get msg>> %s\n",buf);
    }
    sem_unlink("wudi_sem");
    printf("unlink mutex\n");
    return 0;
}

注意以上兩個程式並未十分安全的處理臨界區,send方對共享記憶體的寫其實並不安全,因為可能recv方正在讀取那個資料,正確的做法應該是將共享記憶體做成一個佇列,這樣寫的位置和讀的位置就能避免衝突。但此處僅作演示用,而且從stdin收東西其實很慢的。。。。。

基於共享記憶體的訊號量

int sem_init(sem_t *sem,int shared,int value);
int sem_destroy(sem_t *sem);

注意:

  • sem的記憶體由呼叫者自己分配,並得確保這個記憶體一直存在
  • 不要重複對一個訊號量呼叫sem_init(),其結果是未定義的
  • 不要copy訊號量,使用copy後的訊號量的副本,其結果是未定義的
  • sem_init失敗時返回-1,但成功時並不返回0
  • 就算是在stack空間上分配的空間,也要在不使用時,呼叫sem_destroy釋放資源

其中 shared 引數 指示是否在程序間共享

demo

將上一個例子改成基於共享記憶體的訊號量

Send.c

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
// void *mmap(void *addr, size_t length, int prot, int flags,
//                   int fd, off_t offset);
char buf[1024];
int main()
{
    sem_t *mutex; //= sem_open("wudi_sem",O_CREAT|O_RDWR,FILE_MODE,0);
    char  *dataStart;
    int fd = open("0xabc",O_CREAT|O_RDWR,FILE_MODE);
    char *sharedFile = mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(!sharedFile)
    {
        perror("mmap:");
        sem_unlink("wudi_sem");
        exit(-1);
    }
    write(fd,buf,1024);
    close(fd);
    mutex = (sem_t*)sharedFile;
    if(sem_init(mutex,1,0) < 0)
    {
        perror("mutex init");
        exit(-1);
    }
    dataStart = (char*)sharedFile + sizeof(sem_t);
    while(scanf("%s",buf)!=EOF)
    {
        memcpy(dataStart,buf,128);
        sem_post(mutex);
    }
    sem_destroy(mutex);
    printf("exit\n");
    return 0;
}

Recv.c

#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#define  FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
// void *mmap(void *addr, size_t length, int prot, int flags,
//                   int fd, off_t offset);
char buf[1024];
int main()
{
    sem_t *mutex; //= sem_open("wudi_sem",O_RDWR);
    char *dataStart;
    int fd = open("0xabc",O_RDWR);
    if(fd<0)
    {
        perror("open:");
        exit(-1);
    }
    char *sharedFile = mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(!sharedFile)
    {
        perror("mmap:");
        sem_unlink("wudi_sem");
        exit(-1);
    }
    close(fd);
    mutex = (sem_t*)sharedFile;
    //make sure there exist a sempahore at the start of sharedFile 
    dataStart = (char*)sharedFile + sizeof(sem_t);
    while(1)
    {
        sem_wait(mutex);
        memcpy(buf,dataStart,128);
        if(strcmp(buf,"exit")==0)
        {
            printf("ready to exit\n");
            break;
        }
        printf("get msg>> %s\n",buf);
    }
    sem_destroy(mutex);
    printf("exit\n");
    return 0;
}