Linux inter-process communication ③ - semaphore (Semaphore)

1. Semaphore

1.1 Basic introduction

A semaphore is an inter-process communication mechanism used to deal with inter-process synchronization and mutual exclusion issues, including a variable called a semaphore and a process waiting queue waiting for resources under the semaphore, as well as two processes for the semaphore. Atomic operation (PV operation), in which the semaphore corresponds to a certain resource and takes a non-negative integer value. The semaphore value (usually represented by sem_id) refers to the number of resources currently available. If it is equal to 0, it means that there are no resources currently available.

1.1.1 Synchronization and mutual exclusion

Synchronization relationship : refers to the constraint relationship generated by the coordination of multiple processes to complete a certain task at a certain moment, also known as the direct constraint relationship (such as in pipeline communication, must first write and then read); mutual exclusion relationship:
refers to When a process accesses a critical resource, another process that wants to access the critical resource must wait until the current process accessing the critical resource ends. After the resource is released, another process can access the critical resource, also known as indirect Constraint relationship; (in layman's terms, when there is only one toilet waiting for multiple people, when the people in the toilet have finished using it, it will be the next person's turn) Synchronization
is a more complicated mutual exclusion, and mutual exclusion It is a special kind of
synchronization and mutual exclusion relationship between synchronization processes. The root of the existence is critical resources.

1.1.2 Critical resources

Critical resources : Only a limited number (usually only one) process can access (read) or modify (write) resources at the same time, usually including hardware resources (processors, memory, storage and other peripherals, etc.) and software resources ( Shared code segments, shared structures and variables, etc.)
critical section : code that accesses critical resources (the critical section itself is also called critical resources)

1.1.3 PV operation

P operation : If there are available resources (semaphore value > 0), the process in which this operation is located occupies a resource (at this time, the semaphore value is reduced by 1 and enters the critical section code); if there are no available resources (semaphore value = 0,) the process where this operation is located is blocked until the system allocates resources to the process (enters the waiting queue and waits until the resource is the turn of the process); V operation: if there is a process waiting in the waiting queue of the
semaphore value resource, wake up a blocked process; if no process is waiting for it, release a resource (that is, add 1 to the semaphore value);

1.1.4 Basic steps to use semaphore

①Create a semaphore or obtain an existing semaphore in the system, call the semget() function (different processes obtain the same semaphore by using the same semaphore key value) ②Initialize the semaphore, call SETVAL of the semctl()
function Operation (when using a mutex semaphore, the semaphore is usually initialized to 1)
③Perform the PV operation of the semaphore and call the semop() function (which is the core part of the synchronization and mutual exclusion between processes)
④In the system Delete unnecessary semaphores, call the IPC_RMID operation of the semctl() function

1.2 Function introduction

1.2.1 semget()

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 
int semget( key_t key, int nsems, int semflg);

①Function function : Create or obtain semaphore
②Function parameters :
The first parameter→key is the key value of the created or opened semaphore set, that is, the return value of fork(); the
second parameter→nsems is the specified need The number of semaphores is almost always 1;
the third parameter → semflg is a set of flags. When the semaphore does not exist, a new semaphore can be created, which can be bitwise ORed with the value IPC_CREAT ③ Function return
value : success, return The identifier of the semaphore set; on failure, return -1;

1.2.2 semctl()

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h>
int semctl(int semid, int semun, int cmd, ...);

①Function function : initialize signal set or delete signal set
②Function parameters :
the first parameter → semid is the semaphore key value returned by semget();
the second parameter → semun is the number of the operation signal in the signal set, the first The signal is 0;
the third parameter → cmd is to execute this command on the semaphore set specified by semid, the command is as follows:

SETVAL: Set the value of a single semaphore in the semaphore set, and the fourth parameter needs to be passed in at this time;
IPC_RMID: Delete the semaphore set from the system;
IPC_SEAT: Take the semid_ds structure for this set and store it in the arg In the structure pointed to by .buf;

The fourth parameter → optional, if this parameter is used, its type is semun, which is a combination of multiple specific command parameters. This combination is not defined in any system header file and needs to be defined in the code by itself

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

③ Function return value : success, return a positive number; failure, return -1;

1.2.3 smoop()

#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid,struct sembuf *sops,unsigned nsops);

① Function function : operate one or a group of signals, or called PV operation
② Function parameters :
the first parameter → semid is the semaphore key value returned by semget();
the second parameter → sops is a pointer, pointing to a semaphore To operate an array, the semaphore operation is represented by the structure sembuf structure as follows:

struct sembuf 
{
    
    
	unsigned short sem_um;//操作信号在信号集中的编号
 	short	sem_op;//操作为负(P操作),其绝对值大于信号的现有值,操作将会阻塞,直到信号值≥ |sem_op|。通常用于获取资源的使用权;
 			      //操作为正(V操作),其值会加到现有的信号内值上。通常用于释放所控制资源的使用权;					   									  
		  	     //操作为0:如sem_flg没有设置IPC_NOWAIT,则调用该操作的进程或线程将暂时睡眠,直到信号量的值为0;否则进程或线程会返回错误EAGAIN;
	short	sem_flg;//信号操作标识符,有如下两种选择:
				   //IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设置错误消息;
				  //SEM_UNDO:程序结束时(正常退出或异常终止),保证信号会被重设为semop()调用前的值。避免程序在异常情况下结束时未解锁锁定的资源,造成资源被永远锁定,造成死锁。
 };

The third parameter → nsops is the number of signal operation structures, always greater than or equal to 1;
③ Function return value : success, return 0; failure, return -1;

1.3 Semaphore programming

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define  FTOK_PATH  "/dev/zero"
#define  FTOK_PROJID    0x22

union semun
{
    
    
    int val;
    struct semid_ds  *buf;
    unsigned  short  *arry;
};

union semun sem_union;
int semid;

int semaphore_init(void);			//信号量初始化
int semaphore_p(int semid);			//父进程进行P操作
int semaphore_v(int semid);			//子进程进行V操作(优先子进程先运行)
void semaphore_term(int semid);

int main(int argc,char **argv)
{
    
    
    pid_t pid;
    int i;
    int semid;

    if((semid = semaphore_init()) < 0)
    {
    
    
		printf("semaphore initial failure:%s\n",strerror(errno));
		return -1;
    }

    if((pid = fork()) < 0)
    {
    
    
		printf("create child process failure:%s\n",strerror(errno));
		return -2;
    }
    else if(0 == pid)
    {
    
    
		printf("child process[%d] start running and do something now...\n",getpid());
		sleep(3);
		printf("child process done.\n");
		semaphore_v(semid);		//子进程进行V操作
		sleep(1);
		printf("child process exit now\n");
		exit(0);
    }
    printf("parent process P operator wait child process over.\n");
    semaphore_p(semid);		//子进程没进行v操作前,父进程在阻塞在此
    printf("parent process[%d] start running and do something now...\n",getppid());
    sleep(2);
    printf("parent process destroy semaphore and exit\n");
    semaphore_term(semid);
    return 0;
}
int semaphore_init(void)
{
    
    
    key_t   key;
    int  semid;
    union semun sem_union;
    sem_union.val = 0;		//将信号量的值设为0
    key = ftok(FTOK_PATH,FTOK_PROJID);		//获取IPC关键字key
    if(key < 0)
    {
    
    
        printf("ftok() get key failure:%s\n",strerror(errno));
        return -1;
    }
    printf("ftok() get key successfully!\n");

    semid = semget(key,1,IPC_CREAT|0644);		//创建或获取信号量,信号量不存在则创建
    if(semid < 0)
    {
    
    
		printf("semget() get semid  failure:%s\n",strerror(errno));
		return -2;
    }

    if(semctl(semid,0,SETVAL,sem_union) < 0)	//初始化信号集,设置信号量集中的一个单独的信号量的值,并使用该参数
    {
    
    
		printf("semctl() set initial value failure: %s\n", strerror(errno));		//设置初始化值失败
		return -3;
    }
    printf("semaphore get key_t[0x%x] and semid[%d]\n", key, semid);	//key数据类型为16进制整数
    return semid;	
}

void semaphore_term(int semid)
{
    
    
     if(semctl(semid,0,IPC_RMID,sem_union) < 0)
     {
    
    
		printf("semctl() delete semaphore ID failure:%s\n",strerror(errno));
     }
     return ;
}

int semaphore_p(int semid)
{
    
    
     struct sembuf  _sembuf;
     _sembuf.sem_num = 0;		//第一个信号编号为0,最后一个为nsems-1
     _sembuf.sem_op  =  -1;		//操作为负操作(P操作),semid ≥ |sem_op| 才能继续运行,否则阻塞
     _sembuf.sem_flg  =  SEM_UNDO;	//信号操作标识,程序结束时(正常退出或异常终止),保证信号值会被重设为semop()调用前的值。避免程序在异常情况下结束时未解锁锁定的资源,造成资源被永远锁定。造成死锁。
     
    if(semop(semid,&_sembuf,1) < 0)
    {
    
    
		printf("semop  P  operator   failure:%s\n",strerror(errno));
		return -1;
    }
    return 0;
}

int semaphore_v(int semid)
{
    
    
     struct sembuf  _sembuf;
     _sembuf.sem_num = 0;		
     _sembuf.sem_op  =  1;		
     _sembuf.sem_flg  =  SEM_UNDO;	
    if(semop(semid,&_sembuf,1) < 0)
    {
    
    
		printf("semop V  operator   failure:%s\n",strerror(errno));
		return -1;
    }
    return 0;
}

operation result:

ftok() get key successfully!
semaphore get key_t[0x22050005] and semid[1]
parent process P operator wait child process over.
child process[24213] start running and do something now...
child process do something over...
parent process[24161] start running and do something now...
child process exit now
parent process destroy semaphore and exit

Guess you like

Origin blog.csdn.net/xll102500/article/details/129465264