Linux 信號量之Posix有名字的信號量

信號量(semaphore),也和互斥鎖同樣提供了線程間或者進程間的同步功能。

信號量有三種:

信號量比互斥鎖高級,互斥鎖只容許一個線程訪問臨界區,信號量能夠多個,能夠把信號量看做成互斥鎖的升級版,可是若是能用互斥鎖解決,就用互斥鎖,互斥鎖比信號量節省資源。html

這篇文章只介紹Posix有名字的信號量

1,建立有名字的信號量,建立成功後,會在ubuntu的/dev/shm目錄下,生成一個文件,名字爲【sem.name】。【sem】是固定的,【name】是函數sem_open的第一個參數。c++

名字的信號量的生命週期和內核同樣,只要系統不重啓,它就一直存在。shell

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
                mode_t mode, unsigned int value);
  • name:任意名字,當不能包含【/】
  • oflag:和open函數同樣,O_RDWR,O_CREAT,O_EXCL等
  • mode:和open函數同樣,好比0664
  • value:能夠同時訪問臨界區的線程或者進程的數量。若是設置爲1,功能就和互斥鎖同樣了。
  • 返回值:成功0;失敗:SEM_FAILED(這個宏的實際值是-1)。

2,刪除有名字的信號量,並刪除文件。ubuntu

#include <semaphore.h>
int sem_unlink(const char *name);
  • 返回值:成功0微信

    • 失敗:-1,設置errno函數

      EACCES:沒有權限訪問這個信號量對應的文件
      ENAMETOOLONG:信號量的名字長了
      ENOENT:信號量不存在

3,取得信號量的value值post

#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
  • sem:信號量指針
  • sval:返回的信號量的value值
  • 返回值:成功0;失敗:-1,設置errno。(EINVAL :不是一個有效的信號量)。

4,若是信號量的value值大於0,把信號量的value值-1;若是信號量的value值小於1,阻塞等待,直到信號量的value值大於0。注意:在ubuntu下,若是sem_wait執行前,value值爲0,sem_wait執行後,value也不會變成-1,再次執行sem_wait,value仍是0。可是有的unix系統value會變成負數。即便value不變成負數,內核也會準確記錄它的值。學習

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • sem:信號量指針線程

  • 返回值:成功0;失敗:unix

    • EINTR :阻塞的過程種被信號終止了。
    • EINVAL:不是一個有效的信號量

    sem_trywait():不阻塞等待。

    • EAGAIN: 信號量的value值爲0。

    sem_timedwait():

    • EINVAL: Tabs_timeout.tv_nsecs 小於0,或者大於等於1000毫秒。
    • ETIMEDOUT: 超時了。

5,把信號量的value值+1。

#include <semaphore.h>
int sem_post(sem_t *sem);
  • 返回值:成功0;失敗:
    • EINVAL:不是一個有效的信號量
    • EOVERFLOW:超過了信號量的value。

用下面5個程序觀察有名字信號量的特性。

semcreate.c

#include <unistd.h>
#include <fcntl.h>          
#include <sys/stat.h>       
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
  int c, flags;
  unsigned int val = 1;
  sem_t* sem;

  flags = O_RDWR | O_CREAT;

  while((c = getopt(argc, argv, "ei:")) != -1){
    switch(c){
    case 'e':
      flags |= O_EXCL;
      break;
    case 'i':
      val = atoi(optarg);
      break;
    }
  }

  if(optind != argc - 1){
    printf("usage error\n");
    return -1;
  }

  sem = sem_open(argv[optind], flags, 0664, val);
  if(sem == SEM_FAILED){
    perror("sem");
    return -1;
  }
  sem_close(sem);
  exit(0);
}

semunlink.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
  if(argc != 2){
    printf("usage err\n");
    exit(1);
  }

  if(sem_unlink(argv[1]) == -1){
    perror("sem_unlink");
  }
}

semgetvalue.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
  sem_t* sem;
  int val;

  if(argc != 2){
    printf("usage error\n");
    exit(1);
  }

  sem = sem_open(argv[1], 0);
  sem_getvalue(sem, &val);
  printf("value = %d\n", val);
  exit(0);
}

semwait.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char** argv){
  sem_t* sem;
  int val;

  if(argc != 2){
    printf("usage error\n");
    exit(1);
  }

  sem = sem_open(argv[1], 0);
  sem_wait(sem);
  sem_getvalue(sem, &val);
  printf("pid %ld has semaphore, value = %d\n", (long) getpid(), val);

  pause();
  exit(0);
}

sempost.c

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char** argv){
  sem_t* sem;
  int val;

  if(argc != 2){
    printf("usage error\n");
    exit(1);
  }

  sem = sem_open(argv[1], 0);
  sem_post(sem);
  sem_getvalue(sem, &val);
  printf("value = %d\n", val);
  
  exit(0);
}

步驟1:建立有名字信號量。發如今/dev/shm/下生成了sem.test1,mode是0664,可是umask是0022,因此sem.test1mode是0644

ubuntu$ ./semcreate test1
ubuntu$ ls -l /dev/shm/
total 4
-rw-r--r-- 1 ys ys 32 6月  21 16:25 sem.test1

步驟2:查看有名字信號量的value值

ubuntu$ ./semgetvalue test1
value = 1

步驟3:等待信號量,發現執行完sem_wait函數後,信號量的value值變動爲0了。

ubuntu$ ./semwait test1
pid 2995 has semaphore, value = 0
^C
ubuntu$

步驟4:查看有名字信號量的value值,信號量的value值仍是0。

ubuntu$ ./semgetvalue test1
value = 0

步驟5:等待信號量2次,因爲執行前value值已是0了,按理來講執行了2次sem_wait後,應該變成-2,可是發現還行0。不要驚慌,內核是記住了value的值爲-2的。

ubuntu$ ./semwait test1 &
[8] 3000
ubuntu$ ./semgetvalue test1
value = 0
ubuntu$ ./semwait test1 &
[9] 3002
ubuntu$ ./semgetvalue test1
value = 0

步驟6:把信號量的value值+1

ubuntu$ ./sempost test1
pid 3000 has semaphore, value = 0 //來之第一個sem_wait程序的輸出
value = 0

步驟7:把信號量的value值+1

ubuntu$ ./sempost test1
pid 3002 has semaphore, value = 0  //來之第二個sem_wait程序的輸出
value = 0

步驟8:把信號量的value值+1,發現內核正確的記錄的value的值。

ubuntu$ ./sempost test1
value = 1

用信號量實現生產者和消費者

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>       
#include <sys/stat.h>    
#include <semaphore.h>

#define NBUFF 10
#define SEM_MUTEX   "mutex"
#define SEM_NEMPTY  "nempty"
#define SEM_NSTORED "nstored"

int nitems;
struct {
  int buff[NBUFF];
  sem_t *mutex, *nempty, *nstored;
} shared;

void* produce(void *args){
  int i;
  for(i = 0; i < nitems; ++i){
    sem_wait(shared.nempty);
    sem_wait(shared.mutex);
    shared.buff[i % NBUFF] = i;
    sem_post(shared.mutex);
    sem_post(shared.nstored);
  }

  return NULL;
}

void* consume(void* args){
  int i;
  for(i = 0; i < nitems; ++i){
    sem_wait(shared.nstored);
    sem_wait(shared.mutex);
    shared.buff[i % NBUFF] = i;
    sem_post(shared.mutex);
    sem_post(shared.nempty);
  }

  return NULL;
}

int main(int argc, char** argv){

  pthread_t tid_produce, tid_consume;

  if(argc != 2){
    printf("usage error\n");
    exit(1);
  }

  nitems = atoi(argv[1]);

  //create 3 semaphore
  shared.mutex = sem_open(SEM_MUTEX, O_CREAT | O_EXCL, 0664, 1);
  shared.nempty = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL, 0664, NBUFF);
  shared.nstored = sem_open(SEM_NSTORED, O_CREAT | O_EXCL, 0664, 0);

  //create produce and consume thread
  pthread_create(&tid_produce, NULL, produce, NULL);
  pthread_create(&tid_consume, NULL, consume, NULL);

  //wait for 2 thread
  pthread_join(tid_produce, NULL);
  pthread_join(tid_consume, NULL);

  sem_unlink(SEM_MUTEX);
  sem_unlink(SEM_NEMPTY);
  sem_unlink(SEM_NSTORED);
  exit(0);
}

c/c++ 學習互助QQ羣:877684253

本人微信:xiaoshitou5854

相關文章
相關標籤/搜索