1. 程式人生 > >linux 程序通訊 訊號量

linux 程序通訊 訊號量

一.訊號量概述
訊號量的使用主要是用來保護共享資源,協調同步使用資源,使得資源在一個時刻只有一個程序(執行緒)所擁有。

二.訊號量的分類
Linux提供兩種訊號量:
(1) 核心訊號量,由核心控制路徑使用
(2) 使用者態程序使用的訊號量,這種訊號量又分為POSIX訊號量和SYSTEM V訊號量。
       POSIX訊號量又分為有名訊號量和無名訊號量。
       有名訊號量,其值儲存在檔案中, 所以它可以用於執行緒也可以用於程序間的同步。
       無名訊號量,其值儲存在記憶體中。

       POSIX 訊號量與SYSTEM V訊號量的比較
       1.對POSIX來說,訊號量是個非負整數。常用於執行緒間同步。
           而SYSTEM V訊號量則是一個或多個訊號量的集合,它對應的是一個訊號量結構體,
           這個結構體是為SYSTEM V IPC服務的,訊號量只不過是它的一部分。常用於程序間同步。
       2.POSIX訊號量的引用標頭檔案是“<semaphore.h>”,而SYSTEM V訊號量的引用標頭檔案是“<sys/sem.h>”。
       3.從使用的角度,System V訊號量是複雜的,而Posix訊號量是簡單。
           比如,POSIX訊號量的建立和初始化或PV操作就很非常方便。

一)核心訊號量

      待續 : https://blog.csdn.net/qinxiongxu/article/details/7830537

二)使用者訊號量
 1)SYSTEM V訊號量

 1. man semget

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semget(key_t key, int nsems, int semflg);

2. man semop

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semop(int semid, struct sembuf *sops, size_t nsops);

       int semtimedop(int semid, struct sembuf *sops, size_t nsops,
                      const struct timespec *timeout);

3. man semctl

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semctl(int semid, int semnum, int cmd, ...);

semctl() performs the control operation specified by cmd on the System V semaphore set identified by semid, or on the semnum-th semaphore of that set.  (The semaphores in a set are numbered starting at 0.)

This function has three or four arguments, depending on cmd.  When there are four, the fourth has the type union semun.  The calling program must define this union as follows:

           union semun {
               int              val;              /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };

       The semid_ds data structure is defined in <sys/sem.h> as follows:

           struct semid_ds {
               struct ipc_perm sem_perm;  /* Ownership and permissions */
               time_t          sem_otime; /* Last semop time */
               time_t          sem_ctime; /* Last change time */
               unsigned long   sem_nsems; /* No. of semaphores in set */
           };

       The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):

           struct ipc_perm {
               key_t          __key; /* Key supplied to semget(2) */
               uid_t          uid;   /* Effective UID of owner */
               gid_t          gid;   /* Effective GID of owner */
               uid_t          cuid;  /* Effective UID of creator */
               gid_t          cgid;  /* Effective GID of creator */
               unsigned short mode;  /* Permissions */
               unsigned short __seq; /* Sequence number */
           };

 

 

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<time.h>
#include<unistd.h>
#include<sys/wait.h>
#define MAX_SEMAPHORE 1

#define FILE_BASE "msg.txt"

/*
struct sembuf{
    short sem_num;    //除非使用一組訊號量,否則它為0
    short sem_op;        //訊號量在一次操作中需要改變的資料,通常是兩個數,一個是-1,即P(等待)操作,
                    //一個是+1,即V(傳送訊號)操作。
    short sem_flg;    //通常為SEM_UNDO,使作業系統跟蹤訊號,
                    //並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量
};

*/
union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *_buf;
};


static int s_sem_idx = 0;

static int g_sem_id = 0;
 
static int semaphore_create(void);
static int semaphore_set(int sem_id, int val);
static void semaphore_destory(int sem_id);
static int semaphore_p(int sem_id);
static int semaphore_v(int sem_id);

static int semaphore_create(void)
{
    key_t key;
    int semid;
    
    key = ftok(FILE_BASE,s_sem_idx++); //要想保證key一致,檔案被建立後不能被刪改,函式引數也必須一致
    semid = semget(key,MAX_SEMAPHORE,IPC_CREAT|0666); //建立訊號量  rwx 6 110 讀寫
    if(semid == -1)
    {
        printf("Error in semget \n");
        s_sem_idx--;
        return -1;
        //exit(1);
    }
    printf("Semaphore have been created,ID is :%d\n",semid);

    return semid;
}
static int semaphore_set(int sem_id, int val)
{
    union semun sem_union;    
    sem_union.val = val;
    if(semctl(sem_id,0,SETVAL,sem_union)==-1)
        return 0;
    return 1;
}

static void semaphore_destory(int sem_id)
{
    //刪除訊號量
    union semun sem_union;
    if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
        fprintf(stderr,"Failed to delete semaphore\n");
}

static int semaphore_p(int sem_id)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id,&sem_b,1)==-1)
    {
        fprintf(stderr,"semaphore_p failed\n");
        return -1;
    }
    return 0;
}

static int semaphore_p_nowait(int sem_id)
{
      int ret = 0;
      struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = IPC_NOWAIT; // 直接返回無等待,結合 error 來判斷使用
    ret = semop(sem_id,&sem_b,1);
    
    if(ret != 0)
    {
        if(errno == EAGAIN)
        {
            //printf(" need wait for other process ... \n");
            return EAGAIN;
        }
        else
        {
            //printf(" semaphore_p_nowait ... \n");
            return -1;
        }
    }
     //printf(" ret = %d\n", ret);
    return 0;
}

static int semaphore_v(int sem_id)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id,&sem_b,1)==-1)
    {
        fprintf(stderr,"semaphore_v failed\n");
        return -1;
    }
    return 0;
}

int main()
{
    int pid;
    //int semid;
    int loop = 5;
    int ret = 0;
   
   g_sem_id = semaphore_create();
   semaphore_set(g_sem_id, 1);

   pid = fork();
   
   if(pid == 0)
   {
       while(loop-- > 0)
       {
               printf("0 try get sem [%d]\n", loop);
               if(semaphore_p(g_sem_id) != -1)
               {
                   printf("0 got sem [%d]\n", loop);
                   sleep(1);
                   semaphore_v(g_sem_id);
                   sleep(1);
               }
           }
   }

   pid = fork();
   
   if(pid == 0)
   {
       while(1)
       {
               //printf("2 try get sem [%d]\n", loop);
               ret = semaphore_p_nowait(g_sem_id);
               
               if(ret == EAGAIN)
               {
                   //printf(" 2 is waitting other process.sleep 1...... [%d]\n", loop);
                   continue;
               }
               else if(ret != -1)
               {
                   printf("2 got sem [%d]\n", loop);
                   sleep(1);
                   semaphore_v(g_sem_id);
                   exit(0);
               }
           }
   }

   pid = fork();
   if(pid == 0)
   {
       while(1)
       {
               //printf("3 try get sem [%d]\n", loop);
               ret = semaphore_p_nowait(g_sem_id);
               
               if(ret == EAGAIN)
               {
                   //printf(" 3 is waitting other process.sleep 1...... [%d]\n", loop);
                   continue;
               }
               else if(ret != -1)
               {
                   printf("3 got sem [%d]\n", loop);
                   sleep(2);
                   semaphore_v(g_sem_id);
                   exit(0);
               }
           }
   }
   else if(pid >0)
   {
       while(loop-- > 0)
       {
               printf("1 try get sem [%d]\n", loop);
               if(semaphore_p(g_sem_id) != -1)
               {
                   printf("1 got sem [%d]\n", loop);
                   sleep(1);
                   semaphore_v(g_sem_id);
                   sleep(1);
               }
           }
           
   sleep(5);
   system("ipcs -a");
   semaphore_destory(g_sem_id);

   }
   else
   {
       
   }

    return 0;
}