System v ipc (2)-semaphore, ipc
I. Overview
The System V semaphore is different from the System V message queue. It is not used to transmit data between processes. It is mainly used to synchronize the action of a process.
1. A semaphore is an integer maintained by the kernel. The value is limited to greater than or equal to 0.
2. You can add or subtract a number on the semaphore.
3. When a reduce operation reduces the semaphore to less than 0, the kernel will block the calling process. The blocking will not be removed until another operation restores the signal.
4. Common semaphores are binary semaphores. Operation 0 and 1 control the critical section.
Ii. Function Interfaces
1. Create or open a semaphore
1 #include <sys/sem.h>2 3 int semget(key_t key, int nsems, int semflg);
Key and semflg are the same as message queue. We will not introduce them here. The article connection of message queue is provided above.
Nsems: specifies the number of semaphores, which are generally 1.
2. Control semaphores
1 #include <sys/sem.h>2 3 int semctl(int semid, int semnum, int cmd, ...);
Semid: the semaphore identifier returned by semget.
Semnum: the semaphore number. If it is a group of semaphores, it must be used; otherwise, it is 0.
Cmd: command used to control semaphores. SETVAL initializes a value and IPC_RMID deletes the semaphore.
The fourth parameter is a union semun structure. If the header file of some Linux versions does not have this structure, you need to define it yourself:
1 union semun {2 int val; /* Value for SETVAL */3 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */4 unsigned short *array; /* Array for GETALL, SETALL */5 struct seminfo *__buf; /* Buffer for IPC_INFO6 (Linux-specific) */7 };
3. Change the semaphore Value
1 #include <sys/sem.h>2 3 int semop(int semid, struct sembuf *sops, size_t nsops);
Sops: points to a sembuf structure, which has the following members:
Unsigned short sem_num: semaphore number. If it is not a set of signals, it is generally 0.
Short sem_op: addition and subtraction of semaphores, for example,-1, +.
Short sem_flg: usually set to SEM_UNDO. If the semaphore is not released when the process is terminated, the kernel releases it.
Iii. Simple examples
We encapsulate a simple binary semaphore and write two small programs. One prints only the base number and the other prints only the even number. The binary semaphore controls them to print 1-10 in order.
1. encapsulate binary semaphores
1/** 2 * @ file binary_sem.h 3 */4 5 # ifndef _ BINARY_SEM_H _ 6 # define _ BINARY_SEM_H _ 7 8 union semun {9 int val; /* Value for SETVAL */10 struct semid_ds * buf;/* Buffer for IPC_STAT, IPC_SET */11 unsigned short * array;/* Array for GETALL, SETALL */12 struct seminfo * _ buf;/* Buffer for IPC_INFO13 (Linux-specific) */14 }; 15 16/* Set semaphores */17 int sem_set (int sem_id); 18/* increase semaphores */19 int sem_up (int sem_id ); 20/* reduce semaphores */21 int sem_down (int sem_id); 22/* Delete semaphores */23 int sem_delete (int sem_id); 24 25 # endif
1/** 2 * @ file binary_sem.c 3 */4 5 # include <stdio. h> 6 # include <stdlib. h> 7 # include <string. h> 8 # include <sys/sem. h> 9 10 # include "binary_sem.h" 11 12 static union semun sem_union; 13 static struct sembuf sem_buf; 14 15 16/* Set semaphore */17 int sem_set (int sem_id) 18 {19 sem_union.val = 1; 20 if (semctl (sem_id, 0, SETVAL, sem_union) =-1) 21 return-1; 22 return 0; 23} 24 25/* change the semaphore to 1 */26 int sem_up (int sem_id) 27 {28 sem_buf.sem_num = 0; 29 sem_buf.sem_flg = SEM_UNDO; 30 sem_buf.sem_op = 1; 31 if (semop (sem_id, & sem_buf, 1) =-1) 32 return-1; 33 return 0; 34} 35 36/* semaphore minus 1 */37 int sem_down (int sem_id) 38 {39 sem_buf.sem_num = 0; 40 sem_buf.sem_flg = SEM_UNDO; 41 sem_buf.sem_op =-1; 42 if (semop (sem_id, & sem_buf, 1) =-1) 43 return-1; 44 return 0; 45} 46 47/* Delete semaphores */48 int sem_delete (int sem_id) 49 {50 if (semctl (sem_id, 0, IPC_RMID, sem_union) =-1) 51 return-1; 52 return 0; 53}
2. Program for printing the base
1/** 2 * @ file sem1.c 3 */4 5 # include <stdio. h> 6 # include <string. h> 7 # include <stdlib. h> 8 # include <sys/sem. h> 9 # include <unistd. h> 10 11 # include "binary_sem.h" 12 13 # define SEM_KEY 778814 15 void err_exit (const char * err_msg) 16 {17 printf ("error: % s \ n ", err_msg); 18 exit (1); 19} 20 21 int main (int argc, const char * argv []) 22 {23 int sem_id, I; 24 25/* Create signal */26 if (sem_id = semget (SEM_KEY, 1, 0666 | IPC_CREAT) =-1) 27 err_exit ("semget ()"); 28 29/* The initial signal is 1 */30 if (sem_set (sem_id) =-1) 31 err_exit ("sem_set ()"); 32 33 for (I = 1; I <10; I + = 2) 34 {35/* signal minus 1 */36 if (sem_down (sem_id) =-1) 37 err_exit ("sem_down () "); 38 39 sleep (3); 40 printf (" % s: % d \ n ", argv [0], I ); 41 42/* signal plus one */43 if (sem_up (sem_id) =-1) 44 err_exit ("sem_up ()"); 45 46 sleep (1 ); 47} 48 return 0; 49}
The code between the addition and subtraction operations on semaphores is the critical section. The sleep () of program 39th rows is to have enough time to allow the second program, and the sleep () of 46 rows is to try to let other programs get the semaphore.
3. Print only even programs
1/** 2 * @ file sem2.c 3 */4 5 # include <stdio. h> 6 # include <string. h> 7 # include <stdlib. h> 8 # include <unistd. h> 9 # include <sys/sem. h> 10 11 # include "binary_sem.h" 12 13 # define SEM_KEY 778814 15 void err_exit (const char * err_msg) 16 {17 printf ("error: % s \ n ", err_msg); 18 exit (1); 19} 20 21 int main (int argc, const char * argv []) 22 {23 int sem_id, I; 24 25/* Create signal */26 if (sem_id = semget (SEM_KEY, 1, 0666 | IPC_CREAT) =-1) 27 err_exit ("semget ()"); 28 29 for (I = 2; I <= 10; I + = 2) 30 {31/* signal minus one */32 if (sem_down (sem_id) =-1) 33 err_exit ("sem_down ()"); 34 35 printf ("% s: % d \ n", argv [0], I ); 36 37/* signal plus one */38 if (sem_up (sem_id) =-1) 39 err_exit ("sem_up ()"); 40 41 sleep (1 ); 42} 43 return 0; 44}
Iv. Experiment
1. For the convenience of testing, the keys of the above two programs are uniformly specified using a macro with a value of 7788.
2. Processes executed by two programs: sem1 initializes a semaphores with a value of 1. After Entering the loop, the sem1 executes a minus sign and enters the critical section. After three seconds of sleep, I run the second program sem2. After the sem2 enters the loop, I also perform the minus operation. However, the signal has been reduced to 0 by sem1, and the kernel will block the sem1 operation, because the subtraction is less than 0. Return to sem1. After 3 seconds, print the base number, set the signal to 1, leave the critical section, and sleep for 1 second. At this time, the kernel finds that the signal is 1. You can perform the subtraction operation. The kernel will allow sem2, and sem2 will enter the critical section to print even numbers. The sem1 that just sleeps for 1 second enters the critical section again and finds that the semaphore is reduced to 0 by sem2. At this time, it is blocked by the kernel until sem2 resets the semaphore. So many times !!!
Finally: in fact, the semantics and API of System V semaphores are complicated, and there is a chance to explore POSIX semaphores that are simpler than it.