Interprocess communication - semaphore

More linux knowledge points: linux directory index


1. What is a semaphore

    The essence of a semaphore is a data operation lock, which itself does not have the function of data exchange, but realizes inter-process communication by controlling other communication resources (files, external devices), which itself is only an identification of an external resource. In this process, the semaphore is responsible for the mutual exclusion and synchronization of data operations.

    Personal understanding: A semaphore is an atomic counter. When a resource is used, the counter is decremented by one, indicating that there is one less resource available. When the resource is used up and returned, the counter is incremented by one, indicating that it is available. one more resource

2. How semaphores work

  Since semaphores can only perform two operations to wait and send signals, namely P(sv) and V(sv), their behavior is as follows:

  P(sv): If the value of sv is greater than zero, decrement it by 1; if its value is zero, suspend the execution of the process

  V(sv): If another process is suspended waiting for sv, let it resume running, if no process is suspended waiting for sv, add 1 to it.

example:

  Suppose there is only one plate on the table, then we will record the number of available plates as count=1. Xiaoming now takes this plate away, then there is no plate on the table, (perform p operation) count = 0, and Xiaohong also wants to He took the plate from the table and found that count = 0. There was no plate. He was waiting here. After a while, Xiaoming used up the plate and put it back on the table (perform the v operation), Xiaohong found that count = 1 (indicates that there is a plate), take the plate away (perform the p operation)

3. Operations on semaphores

  1. create

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>
      //如果信号量不存在就创建,存在就打开
       int semget(key_t key, int nsems, int semflg);
       //返回值:函数成功返回一个相应信号标识符,失败返回-1

    parameter:

    1. key: the identifier of the semaphore, generally obtained by the ftok function
    2. nsems: the number of semaphores, indicating how many semaphores you want to create
    3. semflg: A set of flags, when you want to create a new semaphore when the semaphore does not exist, you can do a bitwise OR operation with the value IPC_CREAT. With the IPC_CREAT flag set, no error will be generated even if the given key is a key with an existing semaphore. And IPC_CREAT | IPC_EXCL can create a new, unique semaphore and return an error if the semaphore already exists.
  2. pv operation

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>
        //改变信号量的值,也就是执行pv操作
       int semop(int semid, struct sembuf *sops, unsigned nsops);
    
    

    Parameters: the identifier of the semaphore semget

    The return value of semget, the identifier of the semaphore

    Parameters: sembuf structure

    struct sembuf{  
        short sem_num;//除非使用一组信号量,否则它为0  
        short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,  
                    //一个是+1,即V(发送信号)操作。  
        short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
                    //并在进程没有释放该信号量而终止时,操作系统释放信号量  
    };  

    Parameters: the number of semaphores

    Indicates that you want to operate several semaphores

  3. control

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>
    
       int semctl(int semid, int semnum, int cmd, union semun arg);
       //返回值:成功返回信号量集的标识符,失败返回-1

    Parameters: the identifier of the semaphore semid

    return value of semget

    Parameters: the number of the semaphore semnum

    The number of the signal in the signal set to operate on. Numbering starts from 0.

    Parameters: option cmd

    cmd: command, indicating the operation to be performed. (SETVAL: set the initial value; GETVAL: get the initial value

    Order explain
    IPC_STAT Retrieve the semid_ds structure from the semaphore set and store it in the address of the member buf of the semiun union parameter
    IPC_SET Set the value of the ipc_perm field in the semid_ds structure of a semaphore set and get the value from the buf of semun
    NUMBER Get the values ​​of all semaphores from the semaphore set and store their integer values ​​in an array of pointers to the members of the semun union
    GETNCNT Returns the number of processes currently waiting for resources
    GETPID Returns the PID of the last process that executed the system call semop()
    GETTZCNT Returns the number of processes currently waiting for 100% resource utilization
    SETALL The exact opposite of GETALL
    GETVAL 返回信号量集合内单个信号量的值
    SETVAL 用联合体中val成员的值设置信号量集合中单个信号量的值
    IPC_RMID 从内核中删除信号量集合

    Parameters: union arg

     union semun {
     *          short val;             /*SETVAL用的值,一般写这一个就够了*/
     *          struct semid_ds* buf;  /*IPC_STAT、IPC_SET用的semid_ds结构*/
     *          unsigned short* array; /*SETALL、GETALL用的数组值*/
     *          struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/
     *      } arg;

4. Example: Using semaphores to solve the dining philosophers problem

  1. Problem Description:

    Suppose five philosophers sit around a circular dining table and do one of two things: eat, or think. When they eat, they stop thinking, and when they think, they stop eating. There is a large bowl of pasta in the middle of the table, and a fork between every two philosophers. Since it is difficult to eat pasta with one fork, it is assumed that philosophers must eat with two forks. They can only use the two forks to their left and right. The dining philosophers problem is also sometimes described in terms of rice and chopsticks rather than pasta and fork, because it is clear that two chopsticks are necessary to eat rice.

  2. problem analysis:

    When 5 philosopher processes are executed concurrently, at a certain moment, each philosopher process executes the application for chopsticks, and successfully applies for the ith chopstick (equivalent to 5 philosophers picking up the chopsticks on his left at the same time), and then they They also apply for the right chopsticks and apply for the i+1 chopsticks. At this time, each philosopher only gets one chopstick, and the other one has to wait indefinitely, causing a deadlock.

  3. Solutions

    1. Number each philosopher and chopstick: 0~4
    2. Set a semaphore (counter) for each chopstick
    3. Every time each philosopher takes chopsticks, he agrees to give him two chopsticks on the left and right, namely his own number (num) and num+1, and perform the p operation on these two chopsticks. When other philosophers take chopsticks, they are waiting for
      4. When two chopsticks are used up, return them and perform the v operation so that other philosophers can use them
    4. This ensures that every philosopher can get enough chopsticks when he takes them, avoiding deadlock

    4. Code:

        #include <unistd.h>
        #include <sys/sem.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
    
        union su
        {
            int val;
        };
    
        int semid;
        void p(int num)
        {
            struct sembuf array[2] = {
                {num,-1,0},
                {(num+1)%5,-1,0}
            };
            semop(semid,array,2);
        }
        void v(int num)
        {
            struct sembuf array[2] = {
                {num,1,0},
                {(num+1)%5,1,0}
            };
            semop(semid,array,2);
        }
        void zxj(int num)
        {
            while(1)
            {
                printf("%d 哲学家开始思考.....\n",num);
                sleep(rand()%5);
                printf("%d 哲学家开始饿了.....\n",num);
                p(num);
                printf("%d 哲学家吃饭,拿筷子\n",num);
                sleep(rand()%3);
                printf("%d 哲学家吃完,放筷子\n",num);
                v(num);
            }
        }
        //哲学家就餐,先创建信号量,用于管理筷子,在创建5个进程,表示哲学家
        int main()
        {
            srand(getpid()); 
            key_t key = ftok(".",0x06666);
            if(key < 0){
                perror("ftok");
                return -1;
            }
            semid = semget(key,5,IPC_CREAT|0644);
            if(semid < 0){
                perror("semid");
                return -2;
            }
    
            int i=0;//对5个信号量进行初始化,每个信号量初始化为1
            for(; i < 5;++i)
            {
                union su s = {1};
                semctl(semid,i,SETVAL,s);
            }
    
             int num = 0;
            for(i = 1;i<5;++i)
            {
                pid_t pid = fork();
    
                if(pid == 0){//给子进程编号
                    num = i;
                    break;
            }
        }
    
            zxj(num);
            return 0;
        }
    

    result:

    write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325766475&siteId=291194637