Linux inter-process communication second semaphores and Instances

Source: Internet
Author: User
Tags define definition

Semaphores

Code Source: Embedded Linux application development standard tutorial

Semaphores

Http://www.cnblogs.com/hjslovewcl/archive/2011/03/03/2314341.html

When we use thread operations to write programs in a multi-user system, multi-process system, or a mixed system, we often find that we have a piece of critical code, here, we need to ensure that a process (or thread execution) needs to exclusively access a resource.
Semaphores have a complex programming interface. Fortunately, we can easily provide ourselves with a simplified interface that is efficient enough for most semaphore programming problems.
To prevent multiple programs from accessing a shared resource at the same time, we need to generate and use a tag to ensure that only one thread is executed at a time in the critical section. Thread-related methods, we can use mutex or semaphores to control a multi-threaded program's access to the critical section.


It is very difficult to write code for a general purpose to ensure a Program exclusive access to a specific resource, even though there is an algorithm solution called Dekker. Unfortunately, this algorithm depends on "Waiting" or "spin lock", that is, the continuous operation of a process needs to wait for a memory address to change. In a multitasking environment, such as Linux, this is a waste of CPU resources. If the hardware supports this, it is much easier, usually in the form of a specific CPU command to support exclusive access. The example supported by hardware can be that the Register value is added between the access command and the atomic mode, so that no other command is run between the Read/Add/write operations.

One solution we have learned is to use the o_excl mark to call the OPEN function to create a file, which provides atomic file creation. This allows a process to successfully obtain a tag: the newly created file. This method can be used for simple problems, but it is cumbersome and inefficient for complicated situations.

When the semaphore concept was introduced by semi STR, the field of parallel programming took a big step forward. As we discussed in chapter 12th, semaphores are a special variable, an integer, and only two operations can increase the value: Wait (wait) and signal (signal ). Because in Linux and Unix programming, "wait" and "signal" have special meanings, we will use the original concepts:
P used for waiting (wait) (semaphore variable)
V used for signal (semaphore variable)

These two letters are from Dutch that waits (passeren: pass, like the detection point before the critical section) and signals (vrjgeven: specify or release, like releasing control of the critical section. Sometimes we also encounter the terms "up" and "down" related to semaphores, from the use of signal markers.

Semaphore Definition
 

The simplest semaphore is a variable with only 0 and 1 values. It is a binary semaphore. This is the most common form. Semaphores with multiple positive values are called Universal semaphores. In the rest of this chapter, we will discuss binary semaphores.

The definitions of P and V are surprisingly simple. Assume that we have a semaphore variable Sv. The two operations are defined as follows:

P (SV) reduces SV if Sv is greater than 0. If the SV value is 0, the execution of this process is suspended. P is waiting in queue to use this resource.
V (SV) resumes execution if a process is suspended and waits for Sv. If no Sv is pending, increase the Sv. V is trying to release this shared resource.

Another way to understand semaphores is that when the critical section is available, the semaphore variable Sv is true. When the critical section is busy, the semaphores variable is reduced by P (SV) to false, when the critical section is available again, it is increased by V (SV. Note: it is not enough to simply have a common variable that we can reduce or increase, because we cannot express the signal generation in C, C ++ or other programming languages, perform an atomic test to determine whether the variable is true. If yes, convert it to false. This is what makes semaphore operations special.

A theoretical example 

We can use a simple theoretical example to learn how semaphores work. Suppose we have two processes, proc1 and proc2. These two processes will exclusive access to a database at a certain time they execute. We define a single binary semaphore, SV. Its initial value is 1 and can be accessed by two processes. The two processes then need to execute the same processing to access the critical code. In fact, these two processes can be different calls of the same program.

The two processes share the SV semaphore variable. Once a process has executed the P (SV) operation, the process can obtain the semaphore and enter the critical section. The second process is blocked for the critical section, because when he tries to execute P (SV), he will wait until the first process leaves the critical section and executes V (SV) to release the semaphore.

The process is as follows:

Semaphore SV = 1;
Loop forever {
P (SV );
Critical code section;
V (SV );
Noncritical code section;
}

This code is surprisingly simple, because P operations and V operations are very powerful. Figure 14-1 shows how P and V Operations become the threshold for code in the critical section.

Linux semaphores 

Now we know what semaphores are and how they work in theory. Now we can take a look at how these features are implemented in Linux. The semaphore function interface is designed very fine and provides more practical performance than is usually needed. All Linux semaphore functions operate on a universal semaphore array instead of a single binary semaphore. At first glance, this seems to make things more complicated, but when a process needs to lock Multiple resources, operations on the semaphore array will be a great advantage. In this chapter, we will focus on using a single semaphore, because in most cases, this is exactly what we need to use.

The semaphore function is defined as follows:

# Include <sys/SEM. h>
Int semctl (INT sem_id, int sem_num, int command ,...);
Int semget (key_t key, int num_sems, int sem_flags );
Int semop (INT sem_id, struct sembuf * sem_ops, size_t num_sem_ops );

In fact, to obtain the # define definition required for a specific operation, we need to include sys/SEM. the H file usually needs to contain sys/types. H and sys/IPC. h file. In some cases, this is not necessary.

Because we will understand every function in sequence, remember that these functions are designed to operate the array of signal values, which will be more complicated than the operations required by a single semaphore.

Note that the key function is similar to a file name, because it indicates that the program may use or cooperate with resources. Similarly, the identifier returned by semget and used by other shared memory functions is very similar to the file * returned by the fopen function because it is used by the process to access shared files. Similar to files, different processes have different semaphore identifiers, even though they point to the same semaphore. Key and identifier usage are common for all IPC programs discussed here, although each program uses an independent key and identifier.

Semget 

The semget function creates a new semaphore or obtains an existing semaphore key value.

Int semget (key_t key, int num_sems, int sem_flags );

The first parameter key is an integer that allows unrelated processes to access the same semaphore. All semaphores are indirectly accessed by providing a key for different programs. A semaphores identifier is generated for each semaphores system. The semaphore key value can only be obtained by semget. The semaphore identifier used by all other semaphore functions is returned by semget.

There is also a special semaphore key value, ipc_private (usually 0), which is used to create a semaphore that can only be accessed by the creation process. This is usually not useful, but fortunately, in some Linux systems, the manual page lists ipc_private as a bug because it does not prevent other processes from accessing semaphores.

The num_sems parameter is the number of required semaphores. This value is usually 1.

The sem_flags parameter is a tag set, which is similar to the open function tag. The Lower Nine bits are the signal permissions, which are similar to the file permissions. In addition, these tags can be used with ipc_creat or operated to create a new semaphore. Setting ipc_creat flag and specifying an existing semaphore key value is not an error. If not required, the ipc_creat mark is simply ignored. We can use the combination of ipc_creat and ipc_excl to ensure that we can obtain a new and unique semaphore. If the semaphore already exists, an error is returned.

If successful, the semget function returns a positive number, which is the identifier used for other semaphore functions. If it fails,-1 is returned.

Semop

The semop function is used to change the semaphore value:

Int semop (INT sem_id, struct sembuf * sem_ops, size_t num_sem_ops );

The first parameter, sem_id, is the semaphore identifier returned by the semget function. The second parameter, sem_ops, is a pointer to a structure array. Each structure contains at least the following members:

Struct sembuf {
Short sem_num;
Short sem_op;
Short sem_flg;
}

The first member, sem_num, is the number of semaphores, usually 0, unless we are using a semaphores array. The sem_op member is the variable value of the semaphore. (We can change the number value with any amount, not just 1) usually two values are used,-1 is our P operation, used to wait for a semaphore to become available, + 1 is our V operation, which is used to notify a semaphore that it is available.

The last member, sem_flg, is usually set to sem_undo. This will allow the operating system to track the changes made by the current process to the semaphore. if the process is terminated and the semaphore is not released, if the semaphore is occupied by this process, this flag allows the operating system to automatically release this semaphore. Setting sem_flg to sem_undo is a good habit Unless we need different rows. If we need a different value instead of sem_undo, consistency is very important. Otherwise, we will become very confused. When our process exits, does the kernel try to clear our semaphores.

The action used by semop works at the same time to avoid competition conditions caused by the use of multiple semaphores. We can learn more about semop processing on the album page.

Semctl 

The semctl function allows direct control of semaphore information:

Int semctl (INT sem_id, int sem_num, int command ,...);

The first parameter, sem_id, is the semaphore identifier obtained by semget. The sem_num parameter indicates the number of semaphores. This parameter is used when the semaphore array is used. Generally, if this is the first and only semaphore, the value is 0. The command parameter is the action to be executed. If an additional parameter is provided, it is the Union semun. According to the X/open specification, this parameter includes at least the following parameters:

Union semun {
Int val;
Struct semid_ds * Buf;
Unsigned short * array;
}

Many versions of Linux define semun union in header files (typically Sem. h), although x/open validation says we must define our own Union. If we find that we really need to define our own Union, we can refer to the semctl manual page to learn about the definition. In this case, we recommend that you use the definition provided on the manual page, although this definition is different from the previous one.

Multiple command values can be used for semctl. Here we describe two frequently used values. For more information about semctl, see the manual page.

The two common command values are:

Setval: used to initialize the semaphore to a known value. The required value is passed as the Val Member of the Union semun. You must set a semaphore before using it for the first time.
Ipc_rmid: Used to delete a semaphore ID when the semaphore is no longer needed.

The semctl function returns different values based on the command parameter. For setval and ipc_rmid, if successful, 0 is returned; otherwise,-1 is returned.

Use semaphores 

As we can see in the previous section, the semaphore operation is quite complex. This is unfortunate because it is very difficult to use a critical section for multi-process or multi-thread programming, and its own complex programming interfaces also increase the programming burden.

Fortunately, we can use the simplest binary semaphores to solve most of the problems that require semaphores. In our example, we will use all the programming interfaces to create a very simple P for Binary semaphores.
And V type interface. Then, we will use this simple interface to demonstrate how semaphores work.

To test semaphores, we will use a simple program, sem1.c, which we can call multiple times. We will use an optional parameter to identify whether the program is responsible for creating or destroying semaphores.

We use the output of two different characters to identify the entry and exit of the critical section. A program called with parameters will output an X when it enters and leaves its critical section, and another program call will output an O when it enters and leaves its critical section. Because only one process can enter its critical section at any specified time, all X and O characters are paired.




Instance: the parent process creates a sub-process. By default, resources are occupied. As long as the sub-process does not release resources, the parent process remains waiting for resources to be used.

/* sem_com.h */#ifndefSEM_COM_H#defineSEM_COM_H#include <sys/ipc.h>#include <sys/sem.h>union semun{int val;struct semid_ds *buf;unsigned short *array;};int init_sem(int, int);int del_sem(int);int sem_p(int);int sem_v(int); #endif /* SEM_COM_H */

/* sem_com.c */#include "sem_com.h"int init_sem(int sem_id, int init_value){union semun sem_union;sem_union.val = init_value;if (semctl(sem_id, 0, SETVAL, sem_union) == -1){perror("Initialize semaphore");return -1;}return 0;}int del_sem(int sem_id){union semun sem_union;if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1){perror("Delete semaphore");return -1; }}int sem_p(int sem_id){struct sembuf sem_b;sem_b.sem_num = 0; /*id*/sem_b.sem_op = -1; /* P operation*/sem_b.sem_flg = SEM_UNDO;if (semop(sem_id, &sem_b, 1) == -1) {perror("P operation");return -1;}return 0;}int sem_v(int sem_id){struct sembuf sem_b;sem_b.sem_num = 0; /* id */sem_b.sem_op = 1; /* V operation */sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) == -1){perror("V operation");return -1;}return 0;}

Application

/* Fork. C */# include <sys/types. h> # include <unistd. h> # include <stdio. h> # include <stdlib. h> # include <sys/types. h> # include <sys/IPC. h> # include <sys/SHM. h> # define delay_time3int main (void) {pid_t result; int sem_id; sem_id = semget (ftok (". ", 'A'), 1, 0666 | ipc_creat);/* Create a semaphore */init_sem (sem_id, 0 ); // set the initial value to 0. The resource is occupied./* call the fork function. The returned value is result */result = fork (); /* determine the return status of the fork function based on the result value. First, handle an error. */If (result =-1) {perror ("fork \ n ");} else if (result = 0)/* If the returned value is 0, it indicates the sub-process */{printf ("child process will wait for some seconds... \ n "); sleep (delay_time); printf (" the returned value is % d in the child process (pid = % d) \ n ", result, getpid ()); sem_v (sem_id); // release resources} else/* return value greater than 0 indicates parent process */{sem_p (sem_id); // wait for resources, if the child process is not released, it will always wait for printf ("the returned value is % d in the father process (pid = % d) \ n", result, getpid ()); sem_v (sem_id); // release the resource del_sem (sem_id); // Delete the semaphore} exit (0 );}

Instance 2

Server # include # define segsize 1024 # define readtime 1 Union semun {int val; struct semid_ds * Buf; unsigned short * array;} ARG; // generate the semaphore int sem_creat (key_t key) {Union semun SEM; int Semid; SEM. val = 0; Semid = semget (Key, 1, ipc_creat | 0666); If (-1 = Semid) {printf ("create semaphore error \ n "); exit (-1);} semctl (Semid, 0, setval, SEM); Return Semid;} // Delete the semaphore void del_sem (INT Semid) {Union semun SEM; SEM. val = 0; semctl (Semid, 0, ipc_rmid, SEM);} // pint P (INT Semid) {struct sembuf SOPs = {0, + 1, ipc_nowait }; return (semop (Semid, & SOPs, 1) ;}// Vint V (INT Semid) {struct sembuf SOPs = {0,-1, ipc_nowait }; return (semop (Semid, & SOPs, 1) ;}int main () {key_t key; int shmid, Semid; char * SHM; char MSG [7] = "-data-"; char I; struct semid_ds Buf; Key = ftok ("/", 0); shmid = shmget (Key, segsize, ipc_creat | 0604); If (-1 = shmid) {printf ("create shared memory error \ n"); Return-1;} SHM = (char *) shmat (shmid, 0, 0); If (-1 = (INT) SHM) {printf ("Attach shared memory error \ n"); Return-1 ;} semid = sem_creat (key); for (I = 0; I <= 3; I ++) {sleep (1); P (Semid); sleep (readtime ); MSG [5] = '0' + I; memcpy (SHM, MSG, sizeof (MSG); sleep (58); V (Semid);} shmdt (SHM ); shmctl (shmid, ipc_rmid, & BUF); del_sem (Semid); Return 0; // gcc-o shm. c-g} 2.2 Client # include # define segsize 1024 # define readtime 1 Union semun {int val; struct semid_ds * Buf; unsigned short * array;} ARG; // print the program execution time void out_time (void) {static long start = 0; time_t TM; If (0 = Start) {TM = Time (null ); start = (long) TM; printf ("now start... \ n ");} printf (" Second: % LD \ n ", (long) (Time (null)-Start );} // create the semaphore int new_sem (key_t key) {Union semun SEM; int Semid; SEM. val = 0; Semid = semget (Key, 0, 0); If (-1 = Semid) {printf ("create semaphore error \ n"); exit (-1 );} return Semid;} // wait until the semaphores become 0 void wait_v (INT Semid) {struct sembuf SOPs = {0, 0}; semop (Semid, & SOPs, 1 );} int main (void) {key_t key; int shmid, Semid; char * SHM; char MSG [100]; char I; Key = ftok ("/", 0 ); shmid = shmget (Key, segsize, 0); If (-1 = shmid) {printf ("create shared memory error \ n"); Return-1 ;} SHM = (char *) shmat (shmid, 0, 0); If (-1 = (INT) SHM) {printf ("Attach shared memory error \ n "); return-1;} Semid = new_sem (key); for (I = 0; I <3; I ++) {sleep (2); wait_v (Semid ); printf ("message geted is: % s \ n", SHM + 1); out_time ();} shmdt (SHM); Return 0; // gcc-O shmc. c-g}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.