Semaphores are primitives used to provide synchronization between different threads of different processes or a given process. There are three types: POSIX-known semaphores, using POSIX IPC name identifiers, POSIX memory-based semaphores, stored in shared memory areas, and System v semaphores, maintained in the kernel. These three semaphores can be used for synchronization between processes or threads.
Figure 1 A two-value semaphore used by two processes
Figure 2 A POSIX-known binary semaphore used by two processes
Figure 3 Memory-based semaphores shared by two threads in a process
Three operations that a process can perform on a semaphore:
1, create a semaphore, which requires the caller to specify the initial value, for the binary semaphore, it is usually 1, but also 0.
2, waiting for a semaphore, the operation will test the value of this semaphore, if less than 0, blocking. Also known as the P operation.
3. Hang out a semaphore, which adds 1 to the value of the semaphore, also known as the V operation.
Three differences between semaphores, mutexes, and condition variables:
1. The mutex must always give it a locked line threads unlocked, and the semaphore's hang-out does not have to be performed by the same thread that performed the wait operation.
2, the mutual exclusion lock is either locked or untied.
3, since the semaphore has a state associated with it, then the semaphore hanging operation is always remembered. When a signal is sent to a condition variable, however, if no thread waits on the condition variable, the signal is lost.
POSIX provides two types of semaphores: well-known semaphores and memory-based semaphores (also known as Nameless semaphores). Use the function as follows:
- #include <semaphore.h>
- /*sem_open Create a new well-known semaphore or open a known semaphore that already exists, the value parameter specifies the initial value of the semaphore, the return value is a pointer to a sem_t data type, and is used as a parameter to other functions */
- sem_t *sem_open (const char *name, int oflag, ... /*mode_t mode, unsigned int value*/);
- int Sem_close (sem_t *sem); / * When a process terminates, the kernel automatically performs a close operation on all semaphores that are still open on it * /
- int Sem_unlink (const char *name); /*sem_unlink function: When the reference count is greater than 0 o'clock, name can be removed from the file system, but the destructor of the semaphore will wait until the last sem_close occurs.
- int sem_wait (sem_t *sem); / * Test the value of the specified semaphore, greater than 0, minus 1 and return, equal to 0, call the thread to hibernate until the value is greater than 0, subtract 1, and the function then returns * /
- int sem_trywait (sem_t *sem); / * Specifies a semaphore value of 0 o'clock, does not hibernate, but returns a eagain error * /
- int Sem_post (sem_t *sem);
- int Sem_getvalue (sem_t *sem, int *valp); /* Returns the current value of the specified semaphore from the integer pointed to by Valp. */
Semcreate Program:
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #define File_mode (S_IRUSR | S_IWUSR | S_irgrp | S_iroth)
- Int
- Main (int argc, char **argv)
- {
- int C, flags;
- Sem_t *sem;
- unsigned int value;
- Flags = O_RDWR | O_creat;
- Value = 1;
- While ((c = getopt (argc, argv, "ei:")))! =-1) {
- switch (c) {
- case ' e ':
- Flags |= O_EXCL;
- case ' i ':
- Value = Atoi (Optarg);
- Break ;
- }
- }
- if (optind! = argc-1) {
- printf ("usage:semcreate [-e] [-I initalvalue] <name>\n");
- return-1;
- }
- SEM = Sem_open (Argv[optind], flags, file_mode, value);
- Sem_close (SEM);
- Exit (0);
- }
Semunlink Program:
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- Int
- Main (int argc, char **argv)
- {
- if (argc! = 2) {
- printf ("Usage:semunlink <name>.\n");
- return-1;
- }
- Sem_unlink (argv[1]);
- Exit (0);
- }
Semgetvalue Program:
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- Int
- Main (int argc, char **argv)
- {
- Sem_t *sem;
- int val;
- if (argc! = 2) {
- printf ("Usage:semgetvalue <name>.\n");
- return-1;
- }
- SEM = Sem_open (argv[1], 0);
- Sem_getvalue (SEM, &val);
- printf ("value =%d\n", Val);
- Exit (0);
- }
Semwait Program:
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- Int
- Main (int argc, char **argv)
- {
- Sem_t *sem;
- int val;
- if (argc! = 2) {
- printf ("usage:semwait <name>");
- return-1;
- }
- SEM = Sem_open (argv[1], 0);
- Sem_wait (SEM);
- Sem_getvalue (SEM, &val);
- printf ("pid%ld has semaphore, value =%d\n", (Long) getpid (), Val);
- Pause (); /*block until killed*/
- Exit (0);
- }
Sempost Program:
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- Int
- Main (int argc, char **argv)
- {
- Sem_t *sem;
- int val;
- if (argc! = 2) {
- printf ("Usage:sempost <name>\n");
- return-1;
- }
- SEM = Sem_open (argv[1], 0);
- Sem_post (SEM);
- Sem_getvalue (SEM, &val);
- printf ("value =%d\n", Val);
- Exit (0);
- }
POSIX memory-based semaphores, where the application allocates a semaphore's memory space (that is, allocates a memory space of the sem_t data type), and then the system initializes their values.
- #include <stmaphore.h>
- int Sem_init (sem_t *sem, int shared, unsigned int value); / * ERROR returning -1*/
- int Sem_destroy (sem_t *sem); <span style="White-space:pre" > </span>/* successfully returned 0, error returned -1*/
The memory-based semaphore is initialized by Sem_init, and the SEM parameter points to the sem_t variable that the application must allocate. If shared is 0, the amount of semaphore to initialize is shared among the threads of the same process, otherwise the semaphore is shared between processes.
When you do not need to use the name associated with a known semaphore, you can use memory-based semaphores instead. A well-known semaphore is typically used when the semaphore is used by different processes that are not related to each other. Its name is the means by which each process identifies the semaphore. Memory-based semaphores have at least process continuity, but their true durability depends on the type of memory area in which the semaphore is stored. This semaphore persists as long as the memory area containing a memory semaphore remains valid.
Shared semaphores between processes
The rules for sharing memory-based semaphores between processes are simple: the semaphore itself must reside in a memory area shared by all the processes that want to share it, and the second parameter of Sem_init must be 1.
Known semaphores, different processes always have access to the same known semaphore, as long as they specify the same name when calling Sem_open.
Semaphore limit
POSIX defines two semaphore limits:
Sem_nsems_max maximum number of signals that a process can open simultaneously
Sem_value_max the maximum value of a semaphore
These two constant values are defined in the <unistd.h> header file and can be obtained at run time through the sysconf function.
The number of Linux Posix semaphores