The semaphore value is related to the usage of the corresponding resource. When its value is greater than 0, it indicates the number of currently available resources. When its value is less than 0, the absolute value indicates the number of processes waiting to use the resource. The semaphore value can only be changed by PV operations.
In Linux, PV operations are implemented by calling the semop function.This function is defined in the header file SYS/SEM. h. The prototype is as follows: Int Semop (int Semid, struct sembuf * SOPs, size_t nsops ); The Semid parameter of the function is the identifier of the semaphore set. The SOPs parameter points to the first address of the struct array to be operated. The nsops parameter indicates the number of signals to be operated. If the semop function is called successfully, 0 is returned. If the call fails,-1 is returned. In the structure array directed by the second SOPs parameter of semop, each sembuf struct corresponds to a specific signal operation. Therefore, you must be familiar with the data structure when performing semaphore operations. The structure is defined in Linux/SEM. H, as shown below: Struct Sembuf { Unsigned short Sem_num; // Index the signal in the signal set. 0 indicates the first signal, and 1 indicates the second signal. Short Sem_op; // Operation Type Short Sem_flg; // Operation flag }; The following describes sembuf parameters in detail:Bytes --------------------------------------------------------------------------------------------------
Sem_op> 0Signal AdditionThe sem_op value indicates that the process releases the controlled resources;
Sem_op = 0If ipc_nowait is not set, the calling process enters the sleep state until the signalThe value of the number is 0; otherwise, the process will not go back to sleep and directly return
Eagain
Sem_op <0Signal AdditionSem_op value. That is, several signal values are required. If ipc_nowait is not set, process blocking is called. Until the resource is available. Otherwise, the process returns eagain directly.
This parameter can be set to ipc_nowait or sem_undo.. OnlySem_flg is specifiedAfter the sem_undo flag, semadj (the adjusted value of the specified semaphore for the calling process) will be updated. In addition,If sem_undo is specified for this operation, the count (semadj) of this traffic signal is revoked during system update ). This operation can be performed at any time-it will never force the waiting process. The calling process must have the permission to change the semaphore set. Sem_flgIt is recognized thatIpc_nowaitAndSem_undo. If the operation specifies sem_undo, it will automatically cancel the termination of the process.
Use semaphores to implement the signal lock:
Class tc_semmutex {public:/*** constructor */tc_semmutex ();/*** constructor * @ Param Ikey, key * @ throws tc_semmutex_exception */tc_semmutex (key_t Ikey);/*** initialize * @ Param Ikey, key * @ throws tc_semmutex_exception * @ return none */void Init (key_t Ikey);/*** obtain the shared memory key * @ return key_t, shared memory key */key_t getkey () const {return _ semkey;}/*** get shared memory ID * @ return int, shared memory ID */int getid () const {return _ Semid ;} /*** apply the read lock * @ return int */INT rlock () const;/*** interpret the lock * @ return int */INT unrlock () const; /*** try to read the lock * @ return bool: If the lock is successful, false is returned; otherwise, false */bool tryrlock () const is returned; /*** add write lock * @ return int */INT wlock () const;/*** unlock lock */INT unwlock () const; /*** try to write the lock * @ throws tc_semmutex_exception * @ return bool: If the lock is successful, false is returned; otherwise, false */bool trywlock () const is returned; /*** write lock * @ return int, 0 is correct */INT lock () const {return wlock () ;};/*** unlock lock */INT unlock () const {return unwlock () ;};/*** try to unlock * @ throws tc_semmutex_exception * @ return int, 0 is correct */bool trylock () const {return trywlock () ;}; protected:/*** semaphore ID */INT _ Semid;/*** semaphore key */key_t _ semkey ;};
Tc_semmutex: tc_semmutex () {} tc_semmutex: tc_semmutex (key_t Ikey) {Init (Ikey);} void tc_semmutex: Init (key_t Ikey) {# If defined (_ gnu_library __)&&! Defined (_ sem_semun_undefined)/* Union semun is defined by including <sys/SEM. h> */# else/* according to X/open we have to define it ourselves */Union semun {int val;/* value for setval */struct semid_ds * Buf; /* buffer for ipc_stat, ipc_set */unsigned short * array;/* array for getall, setall * // * Linux specific part: */struct seminfo * _ Buf; /* buffer for ipc_info */}; # endif int isemid; Union semun ARG; u_short array [2] = {0, 0}; // generates a semaphore set that contains two semaphores if (isemid = semget (Ikey, 2, ipc_creat | ipc_excl | 0666 ))! =-1) {Arg. array = & array [0]; // set the value of all semaphores to 0 if (semctl (isemid, 0, setall, ARG) =-1) {Throw tc_semmutex_exception ("[tc_semmutex: init] semctl error:" + String (strerror (errno);} else {// If (errno! = Eexist) {Throw tc_semmutex_exception ("[tc_semmutex: init] SEM has exist error:" + String (strerror (errno )));} // connection semaphores if (isemid = semget (Ikey, 2, 0666) =-1) {Throw tc_semmutex_exception ("[tc_semmutex: init] connect SEM error: "+ String (strerror (errno); }}_ semkey = Ikey; _ Semid = isemid;} int tc_semmutex: rlock () const {// enter the shared lock, the value of the second semaphore indicates the number of processes currently using the semaphore // wait until the first semaphore changes to 0 (the exclusive lock is not used) // occupies the second semaphore (second signal value + 1, used by shared locks) struct sembuf SOPs [2] = {0, 0, sem_undo}, {1, 1, sem_undo}; size_t nsops = 2; return semop (_ Semid, & SOPs [0], nsops);} int tc_semmutex: unrlock () const {// unlock the shared lock, A process has used the second semaphore // wait until the second semaphore can be used (the value of the second semaphore> = 1) struct sembuf SOPs [1] = {1,-1, sem_undo }}; size_t nsops = 1; return semop (_ Semid, & SOPs [0], nsops);} bool tc_semmutex: tryrlock () const {struct sembuf SOPs [2] = {0, 0, sem_undo | ipc_nowait}, {1, 1, sem_undo | ipc_nowait}; size_t nsops = 2; int iret = semop (_ Semid, & SOPs [0], nsops); If (iret =-1) {If (errno = eagain) {// unable to obtain lock return false;} else {Throw tc_semmutex_exception ("[tc_semmutex: tryrlock] semop error:" + String (strerror (errno )));}} return true;} int tc_semmutex: wlock () const {// enter the exclusive lock. Neither the first semaphore nor the second signal has been used (that is, neither lock is used) // wait until the first semaphore changes to 0 // wait until the second semaphore changes to 0 // release the first semaphore (the first semaphore + 1, indicates that a process uses the first semaphore.) struct sembuf SOPs [3] = {0, 0, sem_undo}, {1, 0, sem_undo}, {0, 1, sem_undo }}; size_t nsops = 3; return semop (_ Semid, & SOPs [0], nsops);} int tc_semmutex: unwlock () const {// unlock exclusive lock, A process has used the first semaphore // wait for the first semaphore (signal value> = 1) struct sembuf SOPs [1] = {0,-1, sem_undo }}; size_t nsops = 1; return semop (_ Semid, & SOPs [0], nsops);} bool tc_semmutex: trywlock () const {struct sembuf SOPs [3] = {0, 0, sem_undo | ipc_nowait}, {1, 0, sem_undo | ipc_nowait}, {0, 1, sem_undo | ipc_nowait }}; size_t nsops = 3; int iret = semop (_ Semid, & SOPs [0], nsops); If (iret =-1) {If (errno = eagain) {// unable to obtain lock return false;} else {Throw tc_semmutex_exception ("[tc_semmutex: trywlock] semop error: "+ String (strerror (errno) ;}} return true ;}
In this lock implementation, two semaphores are involved: semaphores 1: The number of exclusive locks. Each read Lock operation requires that the semaphore be set to 0. Each write Lock operation requires that the semaphore be set to 0 and added once. Each write unlock operation will subtract 1. Semaphores 2: Number of shared lock processes. Each read lock increases by 1, and each read unlock minus 1. At the same time, write lock requires that the semaphore be 0. Because the OPT arrays are all atomic operations, ensuring sem_undo can ensure consistency and rollback. This is the implementation principle of the signal lock.