Last chapter , Tells the story of SYSTEM V Semaphore , It mainly runs between processes , This chapter mainly introduces POSIX Semaphore : Famous semaphore 、 Unknown semaphore .
 Insert picture description here

POSIX Semaphore

POSIX The semaphore process is 3 Kind of IPC(Inter-Process Communication) One of the mechanisms ,3 Kind of IPC Mechanism comes from POSIX.1 Real time extension of .Single UNIX Specification take 3 Species mechanism ( Message queue , Semaphores and shared memory ) In the optional section . stay SUSv4 Before ,POSIX Semaphore interface has been included in semaphore options . stay SUSv4 in , These interfaces have been moved to the basic specification , Message queuing and shared storage interfaces are still optional .

POSIX The semaphore interface is designed to solve XSI Several defects of semaphore interface .

  1. Compared with XSI Interface ,POSIX The semaphore interface allows for higher performance implementation .

  2. POSIX Semaphores are easier to use : There is no semaphore set , After familiar file system operation, some interfaces are patterned . Although there is no requirement to implement it in the file system , But some systems do .

  3. POSIX Semaphores perform better when they are deleted . recall , When one XSI When semaphore is deleted , Operations that use this semaphore identifier fail , And will errno Set to EIDRM. Use POSIX Semaphore time , The operation can continue until the last reference of the semaphore is released .

classification

POSIX The semaphore is a sem_t Variable of type , but POSIX There are two implementation mechanisms for semaphores : Nameless semaphores and named semaphores .
Anonymous semaphores can only be used in shared memory , For example, to realize mutual exclusion and synchronization between threads in the process , So anonymous semaphores are also called memory based semaphores ; Named semaphores are usually used when memory is not shared , Like interprocess communication .

meanwhile , When creating semaphores , According to the different value of semaphore ,POSIX Semaphores can also be divided into :

  • Binary semaphore : The value of the semaphore is just 0 and 1, It's very similar to mutex , If resources are locked , The value of the semaphore is 0, If resources are available , Then the value of the semaphore is 1;

  • Count the semaphore : The value of the semaphore is in 0 To a greater than 1 Between the limits of , The count represents the number of resources available .

difference

The difference between a named semaphore and an unnamed semaphore is in the form of creation and destruction , But it's the same with other jobs .

Anonymous semaphores can only exist in memory , The process using semaphore must be able to access the memory where semaphore is located , So anonymous semaphores can only be applied between threads in the same process ( Shared process memory ), Or threads in different processes that have mapped the same memory content to their address space ( That is, the memory of semaphore is shared by the communicating process ). This means that anonymous semaphores can only be accessed through shared memory .

contrary , Well known semaphores can be accessed by name , So it can be used by any thread in the process that knows their name .

Use... In a single process POSIX Semaphore time , Anonymous semaphores are simpler .
Use between multiple processes POSIX Semaphore time , Well known semaphores are simpler .

contact

Whether it's a known semaphore or a nameless semaphore , You can operate the semaphore value through the following functions .

wait(P)

wait Subtract one from the semaphore value , There are three functions , The function prototype is as follows :

#include <semaphore.h>int sem_wait(sem_t *sem);int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);Link with -pthread. This sentence means  gcc  Compile time , To add  -pthread. Return value :
     If successful , return  0 ; If something goes wrong , return -1

  • sem_wait The role of is , if sem Less than 0 , The thread is blocked by semaphores sem , until sem Greater than 0 ; Otherwise, the semaphore value decreases 1.

  • sem_trywait The functions and sem_wait identical , It's just that this function doesn't block threads , If sem Less than 0, Return an error directly ( Error set to EAGAIN ).

  • sem_timedwait It also works with sem_wait identical , The second parameter represents the blocking time , If sem Less than 0 , It will block , Parameter specifies the length of blocking time .abs_timeout Point to a structure , This structure is constructed from 1970-01-01 00:00:00 +0000 (UTC) The starting seconds and nanoseconds constitute .

The structure is defined as follows :

struct timespec {time_t tv_sec;      /* Seconds */long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */};

If the specified blocking time is up , however sem Still less than 0 , It will return an error ( Error set to ETIMEDOUT ).

post(V)

post Add one to the semaphore value , The function prototype is as follows :

#include <semaphore.h>int sem_post(sem_t *sem);Link with -pthread. Return value :
    If successful , return  0 ; If something goes wrong , return -1

Unknown semaphore

The interface function

The function of the semaphore is represented by sem_ start , The basic signaling functions used in threads are 4 individual , They all claim to be in the header file semaphore.h in , This header file defines... For semaphore operation sem_t type :

sem_init

This function is used to create semaphores , The prototype is as follows :

int sem_init(sem_t *sem, int pshared, unsigned int value);

function :
This function is initialized by sem The signal object pointed to , Set its sharing options , And give it an initial integer value .pshared Control the type of semaphore , If it's worth 0, It means that the semaphore is the local semaphore of the current process , Otherwise, semaphores can be shared among multiple processes ,value by sem The initial value of the .
Return value :
The function call successfully returns 0, Failure to return -1.

sem_destroy

This function is used to clean up used semaphores , Its prototype is as follows :

int sem_destroy(sem_t *sem);

Return value :

Successfully returns 0, Failure to return -1.

sem_getvalue function

This function returns the value of the current semaphore , adopt restrict The output parameter returns . If the current semaphore is locked ( The synchronization object is not available ), So the return value is zero 0, Or negative , Its absolute value is the number of threads waiting for the semaphore to be unlocked .

int sem_getvalue(sem_t *restrict, int *restrict);

Using examples

【 example 1】:

#include <time.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>sem_t sem;#define handle_error(msg)   do { \
                                perror(msg); \
                                exit(EXIT_FAILURE); \
                            }while (0)static void handler(int sig){write(STDOUT_FILENO, "sem_post() from handler\n", 24);if(sem_post(&sem) == -1){write(STDERR_FILENO, "sem_post() failed\n", 18);_exit(EXIT_FAILURE);}}int main(int argc, char *argv[]){int s;struct timespec ts;struct sigaction sa;if (argc != 3){ fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]);exit(EXIT_FAILURE);
   }if (sem_init(&sem, 0, 0) == -1)handle_error("sem_init");/* Establish SIGALRM handler; set alarm timer using argv[1] */sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGALRM, &sa, NULL) == -1)handle_error("sigaction");alarm(atoi(argv[1]));/* Calculate relative interval as current time plus
      number of seconds given argv[2] */if (clock_gettime(CLOCK_REALTIME, &ts) == -1)handle_error("clock_gettime");
    ts.tv_sec += atoi(argv[2]);printf("main() about to call sem_timedwait()\n");while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)continue;       /* Restart if interrupted by handler *//* Check what happened */if (s == -1){if (errno == ETIMEDOUT)printf("sem_timedwait() timed out\n");elseperror("sem_timedwait");}else{printf("sem_timedwait() succeeded\n");}exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);}

【 example 2】:

#include <time.h>#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>sem_t sem;void *func1(void *arg){sem_wait(&sem);int *running = (int *)arg;printf("thread func1 running : %d\n", *running);pthread_exit(NULL);}void *func2(void *arg){printf("thread func2 running.\n");sem_post(&sem);pthread_exit(NULL);}int main(void){int a = 3;sem_init(&sem, 0, 0);pthread_t thread_id[2];pthread_create(&thread_id[0], NULL, func1, (void *)&a);printf("main thread running.\n");sleep(10);pthread_create(&thread_id[1], NULL, func2, (void *)&a);printf("main thread still running.\n");pthread_join(thread_id[0], NULL);pthread_join(thread_id[1], NULL);sem_destroy(&sem);return 0;}

Famous semaphore

Sometimes called named semaphores , It's called named semaphore , Because it has a name 、 A user ID、 A group ID And authority . These are interfaces that are provided to processes that do not share memory to use named semaphores . The name of a named semaphore is a string that follows the rules of pathname construction .

The interface function

sem_open function

This function is used to create or open a named semaphore , Its prototype is as follows :

sem_t *sem_open(const char *name, int oflag);sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);

Parameters

  • name Is a string that identifies the semaphore .

  • oflag Used to determine whether to create a semaphore or connect an existing semaphore .
    oflag The parameter of can be 0,O_CREAT or O_EXCL: If 0, Open an existing semaphore ; If O_CREAT, It means to create a semaphore if it doesn't exist , If it exists, the open is returned , here mode and value All need to specify ; If O_CREAT|O_EXCL, Indicates that if the semaphore exists, an error is returned .

  • mode
    Permission bits used to specify semaphores when creating semaphores , and open The function is the same , Include :S_IRUSR、S_IWUSR、S_IRGRP、S_IWGRP、S_IROTH、S_IWOTH.

  • value
    When creating semaphores , Initial value of semaphore .

sem_close function

This function is used to turn off named semaphores :

int sem_close(sem_t *);

function :
A single program can use sem_close Function to turn off named semaphores , But this doesn't remove the semaphore from the system , Because named semaphores are persistent beyond the execution of a single program . When the process calls _exit、exit、exec Or from main return , Named semaphores opened by processes are also closed .

sem_unlink function
function :
sem_unlink The function is used after all processes have turned off the named semaphore , Remove semaphores from the system :

int sem_unlink(const char *name);

Semaphore operation function
Like anonymous semaphores .

Using examples

#include <time.h>#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <semaphore.h>#define SEM_NAME " /sem_name"sem_t *p_sem;void *testThread(void *ptr){sem_wait(p_sem);sleep(2);pthread_exit(NULL);}int main(void){int i = 0;pthread_t pid;int sem_val = 0;p_sem = sem_open(SEM_NAME, O_CREAT, 0555, 5);if(p_sem == NULL){printf("sem_open %s failed!\n", SEM_NAME);sem_unlink(SEM_NAME);return -1;}for(i = 0; i < 7; i++){pthread_create(&pid, NULL, testThread, NULL);sleep(1);// pthread_join(pid, NULL);  // not needed, or loopsem_getvalue(p_sem, &sem_val);printf("semaphore value : %d\n", sem_val);}sem_close(p_sem);sem_unlink(SEM_NAME);return 0;}

The persistence of named and unnamed semaphores

Named semaphores persist with the kernel . When named semaphores are created , Even if no process is currently opening a semaphore , Its value remains , Until the kernel reboots or calls sem_unlink() Delete the semaphore .

The duration of anonymous semaphore depends on the location of semaphore in memory :

If the anonymous semaphore is in the data space within a single process , That is, semaphores can only be shared among threads within a process , So semaphores vary with the duration of the process , When the process stops, he disappears ;

If the anonymous semaphore is in the shared memory area of different processes , So as long as the shared memory area still exists , The semaphore will always be there ; So the anonymous semaphore depends on the persistence of the kernel .

Semaphore - The mutex - Condition variables,

A lot of the time, semaphores 、 Both mutex and conditional variables can be used in some applications , What are the differences among the three ? Here's a list of the differences between the three :

  • Mutex must be unlocked by the thread that locked it ; The semaphore doesn't need to be hung out by the thread waiting for it , You can hang out in other processes ;

  • Mutex is either locked , Or be untied , There are only two states ; The semaphore value can support multiple processes / The thread is running successfully wait operation ;

  • Semaphore hanging out operation is always remembered , Because the semaphore has a count value , The hang out operation always adds the count to 1, However, when a conditional variable sends a signal , If there is no thread waiting in the condition variable , Then the signal will be lost .