天天看點

linux程序間通信:POSIX信号量

文章目錄

  • ​​概念描述​​
  • ​​程式設計接口​​
  • ​​注意事項​​
  • ​​程式設計案例​​
  • ​​信号量基本接口使用案例​​
  • ​​信号量父子程序間通信​​
  • ​​信号量實作 兩程序之間通信​​

概念描述

  • 英文:​

    ​semaphore​

    ​ 簡稱SEM,主要用來進行程序間同步
  • 本質:核心維護的一個正整數,可對其進行各種+/-操作
  • 分類:systemV 信号量、POSIX 有名信号量、POSIX 無名信号量
  • 用途:用來标示系統中可用資源的的個數,協調各個程序有序的通路資源,防止發生沖突
  • P操作:程式在進入臨界區之前要對資源進行申請,對資源的引用計數要-1,當資源個數為0時程序p操作會發生阻塞
  • V操作:程式離開臨界區後要釋放相應的資源,對資源對引用計數要+1

程式設計接口

以下接口編碼過程中都可以通過​

​man sem_open​

​這種方式檢視接口對具體使用方式,詳細資訊則不列舉

sem_t *sem_open(const char *name, int oflag); //使用字元串建立一個信号量
sem_t *sem_open(const char *name, int oflag,
            mode_t mode, unsigned int value); //使用字元串創一個一個信号量,并初始化信号量的值
int sem_close(sem_t *sem);
int sem_post(sem_t *sem); //信号量的v操作 +1
int sem_wait(sem_t *sem); //信号量的p操作 -1
int sem_trywait(sem_t *sem); //信号量進行p操作,但是值已經為0,此時會立即傳回錯而非阻塞

//信号量進行p操作,如信号量的值為0,則阻塞abs_timeout結構體中的時間内還是無法執行p操作,則傳回錯誤
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 
int sem_unlink(const char *name);//删除名稱為name的信号量
int sem_getvalue(sem_t *sem, int *sval);//擷取信号量sem所代表的value數值      

注意事項

  • 以上程式設計接口包含頭檔案: ​

    ​#include <semaphore.h>​

  • 編譯時需指定:​

    ​-lpthread​

    ​進行編譯

程式設計案例

linux系統中建立的信号量存放路徑為​

​/dev/shm​

信号量基本接口使用案例

​sem_demo.c​

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

int main (){
  //建立信号量并初始化信号量的value值為4
  unsigned int sem_value = 4;
  sem_t *sem = sem_open("posix_sem", O_RDWR | O_CREAT,0777,sem_value);
  if (sem == SEM_FAILED) {
    printf("sem_open failed\n");
    _exit(-1);
  }
  
  //擷取信号量的value值
  if (sem_getvalue(sem,&sem_value) != -1) {
    printf("sem_getvalue is %d\n",sem_value);
  }

  //對信号量進行p(-1)操作
  sem_wait(sem);
  sem_wait(sem);
  sem_wait(sem);
  sem_wait(sem);
  //sem_trywait(sem);//并不會發生阻塞,此時sem值已經為0,無法再進行p操作

  if(sem_getvalue(sem,&sem_value) != -1) {
    printf("sem_getvalue after sem_wait is %d\n",sem_value);
  }

  //對信号量進行v(+1)操作
  sem_post(sem);
  sem_post(sem);
  sem_post(sem);
  sem_post(sem);
  
  if(sem_getvalue(sem,&sem_value) != -1) {
    printf("sem_getvalue after sem_post is %d\n",sem_value);
  }

  if(sem_close(sem) != -1) {
    printf("sem_close success\n");
  }

  printf("wait for unlink sem\n");
  sleep(10);
  
  //删除名稱為posix_sem信号量
  if(sem_unlink("posix_sem") != -1) {
    printf("sem_unlink success \n");
  }
  
  return 0;
}      

編譯​

​gcc sem_demo.c -lpthread​

​ 輸出如下:

sem_getvalue is 4
sem_getvalue after sem_wait is 0
sem_getvalue after sem_post is 4
sem_close success
wait for unlink sem
sem_unlink success      

同時在等待10秒删除期間我們檢視​

​/dev/shm​

​目錄下的sem檔案存在,當10秒過後sem檔案則被删除

linux程式間通信:POSIX信号量
信号量父子程序間通信

​sem_comu.c​

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

#define SEM_NAME "posix_sem_comu"

int main (){
  int i = 0, j = 0,sem_val = 0,ret;
  sem_t *sem;
  sem = sem_open(SEM_NAME, O_CREAT,0666,1);

  ret = fork();
  if (ret == -1) {
    printf("sem_open failed \n");
    sem_close(sem);
    sem_unlink(SEM_NAME);
    _exit(-1);
  }
  //建立的子程序,執行10次p操作,每次間隔1秒
  else if(ret == 0) {
    while (i++ <= 10) {
      sem_wait(sem);
      if (sem_getvalue(sem,&sem_val) != -1) {
        printf("child process :sem value is %d\n",sem_val);
        sleep(1);
      }
    }
    _exit(1);
  }
  //父程序執行10次v操作,每次間隔兩秒
  else {
    while (j++ <=10) {
      sem_post(sem);
      if (sem_getvalue(sem,&sem_val) != -1) {
        printf("prarent process :sem value is %d\n",sem_val);
        sleep(2);
      }
    }
  }
  
  //最終删除sem信号量
  sem_close(sem);
  if (sem_unlink(SEM_NAME) != -1) {
    printf("sem_unlink success \n");
  }
  return 0;
}      

編譯:​

​gcc sem_comu.c -o comu -lpthread​

​ 輸出如下,可以很明顯的看到子即使子程序的p操作頻率快于父程序v操作,但是對同一信号量,他們仍然能夠有序同步通路。因為子程序p操作頻率較快,此時信号量的value會很快減為0,子程序此時再進行p操作會發生阻塞;直到父程序執行v操作将信号量的值變為1子程序才能繼續通路

prarent process :sem value is 2
child process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
child process :sem value is 0
prarent process :sem value is 1
sem_unlink success      
信号量實作 兩程序之間通信

兩個程序,使用同一個信号量共同通路一個臨界區,利用信号量的pv操作,實作同步通路

​​

​sem_post.c​

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

int main(){
  char *name = "/posix_sem";
  int j = 0, sem_value = 4;
  sem_t *sem;
  sem = sem_open(name, O_CREAT, 0666, sem_value);
  if (sem == SEM_FAILED) {
    printf("sem open failed \n");
    _exit(-1);
  }
  printf("sem_open %s success \n",name);

  //對信号量進行5次 v操作,每次間隔5秒
  while(j++ <= 10) {
    if(sem_post(sem) == -1) {
      printf("sem _posd failed\n");
      _exit(-1);
    }
    if(sem_getvalue(sem,&sem_value) != -1) {
      printf("sem post success %d\n",sem_value);
    }
    sleep(5);
  }

  sleep(10);
  if(sem_close(sem) != -1) {
    printf("sem_close success \n");
  }
  if (sem_unlink(name) != -1) {
    printf("sem_unlink success \n");
  }
  return 0;
}      

​sem_wait.c​

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

int main(){
  char *name = "/posix_sem";
  int j = 0, sem_value = 4;
  sem_t *sem;
  sem = sem_open(name, O_CREAT, 0666, sem_value);
  if (sem == SEM_FAILED) {
    printf("sem open failed \n");
    _exit(-1);
  }
  printf("sem_open %s success \n",name);

  //對信号量進行p操作,每次間隔5秒,總共進行10次
  while(j++ <= 10) {
    if(sem_post(sem) == -1) {
      printf("sem _posd failed\n");
      _exit(-1);
    }
    if(sem_getvalue(sem,&sem_value) != -1) {
      printf("sem post success %d\n",sem_value);
    }
    sleep(5);
  }

  //執行結束後關閉信号量,并删除信名稱為name的信号量
  sleep(10);
  if(sem_close(sem) != -1) {
    printf("sem_close success \n");
  }
  if (sem_unlink(name) != -1) {
    printf("sem_unlink success \n");
  }
  return 0;
}      

編譯:

​gcc sem_post.c -o post -lpthread​

​​

​gcc sem_wait.c -o wait -lpthread​