Interprocess communication -- Introduction to semaphores and ipcs/ipcrm

Posted by rolwong on Wed, 22 Sep 2021 02:34:16 +0200

1. Semaphore description

Semaphore is used to synchronize the process (i.e. control the execution of the process). It is a special variable and generally takes a positive value. Its value represents the number of resources allowed to access. When obtaining resources, the semaphore value needs to be atomic minus one. This operation is called P operation. When the semaphore value is 0, it means that no resources are available, and the P operation will be blocked. When releasing resources, you need to add one atom to the semaphore value. This operation is called V operation. Semaphores are mainly used to synchronize processes. If the value of semaphore is only 0,1, it is called binary semaphore. If the value of the semaphore is greater than 1, it is called a count semaphore.
Critical resources: resources that can only be accessed by one process or thread at the same time
Critical areas: code snippets that access critical resources

2. Semaphore usage

2.1 introduction to operation semaphore interface:

#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
/*
semget()Create or get existing semaphores
semget()The ID of the semaphore is returned successfully, and - 1 is returned for failure
key: If two processes use the same key value, they can use the same semaphore
nsems: The kernel maintains a semaphore set. When creating a new semaphore, it specifies the number of semaphores in the semaphore set
semflg Optional: IPC_CREAT IPC_EXCL
*/
int semget(key_t key, int nsems, int semflg);




/*
semop()Change the semaphore and do P operation or V operation
semop()0 is returned for success and - 1 is returned for failure
nsops Is the number of semaphores
struct sembuf
{
	unsigned short sem_num; //Specifies the semaphore subscript in the semaphore set
	short sem_op; //Its value is - 1 for P operation and 1 for V operation
	short sem_flg; //SEM_UNDO
};
*/
int semop( int semid, struct sembuf *sops, unsigned nsops);




/*
semctl()Control semaphore
semctl()0 is returned for success and - 1 is returned for failure
cmd Options: SETVAL / / synchronize the value of val to the semaphore
		  IPC_RMID
union semun//Semaphore subscript 
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct seminfo *_buf;
};
*/
int semctl( int semid, int semnum, int cmd, ...);

2.2 interface for encapsulating semaphores:

The code of sem.h is as follows:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<sys/sem.h>

union semu{int val};

void sem_init();//Create and initialize, or obtain the existing semaphore id
void sem_p();//p operation, semaphore minus one
void sem_v();//v operation, semaphore plus one
void sem_destroy();//Destroy semaphore

sem.c code is as follows:

#include"sem.h"

static int  semid = -1;

void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
    if(semid == -1)//Semaphore already exists
    {
        semid = semget((key_t)1234,1,0600);
    }
    else//The semaphore does not exist. The semaphore is created and initialized here
    {
        union semun a;
        a.val = 1;
        if(semctl(semid,0,SETVAL,a)==-1)
        {
            perror("semctl error");
        }
    }
}

void sem_p()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//P
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("semop P error");
    }
}


void sem_v()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;//V
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1)==-1)
    {
        perror("semop V error");
    }
}

void sem_destroy()
{
    if(semctl(semid,0,IPC_RMID) == -1)
    {
        perror("semtcl del error");
    }
}

It doesn't matter if we can't remember how to package it. Generally, when we use it, it's encapsulated. We just need to know how to use semaphore PV to operate and control the process.

2.3 example demonstration:

Example (1): process a and process b simulate accessing the printer. Process a outputs the first character 'a' to start using the printer, and the second character 'a' to end using. The operation of process b is the same as that of process a. (since the printer can only be used by one process at a time, abab should not appear in the output result), as shown in the figure:

a.c codes are as follows:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include"sem.h"

int main()
{
    sem_init();
    for(int i = 0; i < 5; i++)
    {
        sem_p();
        printf("a start\t");
        sleep(3);
        printf("a end\n");
        sem_v();
        sleep(3);
    }
    sleep(10);
    sem_destroy();
    exit(0);
}

a.c last sleep(10) is to end the a program, so that the semaphore can be destroyed in the a program.
b.c code:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include"sem.h"

int main()
{
    sem_init();
    for(int i = 0; i < 5; i++)
    {
        sem_p();
        printf("b start\t");
        sleep(3);
        printf("b end\n");
        sem_v();
        sleep(3);
    }
    exit(0);
}

Operation results:

It can be found that since only one printer is required, b or a can continue to use the printer after a uses the printer. It is impossible for a and b to use the printer at the same time.

(2) Example 2: there is A space that requires process A to write data to it first, process B to read data from it, and then process A to write
Analysis 1: a semaphore is as follows:

At first glance, there is no problem, but after careful consideration, it is found that the data written by A is not controlled. The data written by A is not controlled by the semaphore, and A can still write after writing. We require that after A writes and B reads, A can write again.
So it's not feasible.
Analysis 2: two semaphores:
One semaphore limits A to write and one semaphore limits B to read
As shown below:

3. Introduction to IPCs / ipcrm

ipcs can view the usage of message queues, shared memory and semaphores, and ipcrm can be used for deletion.
If we remove the operation of destroying semaphores in a.c program, the semaphores we create will not be destroyed. a. C code is as follows.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include"sem.h"

int main()
{
    sem_init();
    for(int i = 0; i < 5; i++)
    {
        sem_p();
        printf("a start\t");
        sleep(3);
        printf("a end\n");
        sem_v();
        sleep(3);
    }
    exit(0);
}

After running programs a and b, the semaphores we created still exist because they are not destroyed in the program. It can be viewed with the instruction ipcs.
The following is the semaphore information viewed with the instruction ipcs after running a and b programs:

The key value is hexadecimal, which is the key value 1234 set in our program.
The semaphore can be destroyed with the instruction ipcrm + -s + semid.

ipcrm -m is for shared memory;
ipcrm -q is for message queues;

Topics: Linux