Producer consumer problem: this problem describes two processes that share a fixed-size buffer-the so-called "producer" and "consumer"-problems that will occur during actual operation. The main function of the producer is to generate a certain amount of data into the buffer zone and repeat this process. At the same time, consumers consume the data in the buffer zone. The key to this problem is to ensure that the producer does not add data when the buffer zone is full, and the consumer does not consume data when the buffer zone is empty.
We can use semaphores to solve producer and consumer problems, such:
Define three semaphores. sem_full and sem_empty are used for synchronization between the producer process and the consumer process. That is, if the buffer zone is empty, it can be produced and consumed only when the buffer zone is not empty. Because the same buffer zone is shared, products cannot be produced or consumed during the production of a product, and products cannot be produced or consumed during the consumption of a product
Sem_mutex semaphores to constrain behaviors, that is, mutual exclusion between processes.
The following describes how to implement a first-in-first-out shared memory segment based on the producer and consumer model:
As shown in, two struct types are defined. The shmhead is the header of the shared memory segment, which stores the block size, number of blocks, and read/write indexes. Shmfifo saves the pointer to the shared memory header, the starting address of the payload, the shmid of the created shared memory segment, and three semaphores.
The following code encapsulates several functions:
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
|
# Include "shmfifo. H" # Include <assert. h>Shm1_o_t * shm1_o_init (INT key, int blksize, int blocks) { Shm1_o_t * FIFO = (shm1_o_t *) malloc (sizeof (shm1_o_t )); Assert (FIFO! = NULL ); Memset (FIFO, 0, sizeof (shm1_o_t )); Int shmid; Shmid = shmget (Key, 0, 0 ); Int size = sizeof (shmhead_t) + blksize * blocks; If (shmid =-1) { FIFO-> shmid = shmget (Key, size, ipc_creat | 0666 ); If (FIFO-> shmid =-1) Err_exit ("shmget "); FIFO-> p_shm = (shmhead_t *) shmat (FIFO-> shmid, null, 0 ); If (FIFO-> p_shm = (shmhead_t *)-1) Err_exit ("shmat "); FIFO-> p_payload = (char *) (FIFO-> p_shm + 1 ); FIFO-> p_shm-> blksize = blksize; FIFO-> p_shm-> blocks = blocks; FIFO-> p_shm-> rd_index = 0; FIFO-> p_shm-> wr_index = 0; FIFO-> sem_mutex = sem_create (key ); FIFO-> sem_full = sem_create (Key + 1 ); FIFO-> sem_empty = sem_create (Key + 2 ); Sem_setval (FIFO-> sem_mutex, 1 ); Sem_setval (FIFO-> sem_full, blocks ); Sem_setval (FIFO-> sem_empty, 0 ); } Else { FIFO-> shmid = shmid; FIFO-> p_shm = (shmhead_t *) shmat (FIFO-> shmid, null, 0 ); If (FIFO-> p_shm = (shmhead_t *)-1) Err_exit ("shmat "); FIFO-> p_payload = (char *) (FIFO-> p_shm + 1 ); FIFO-> sem_mutex = sem_open (key ); FIFO-> sem_full = sem_open (Key + 1 ); FIFO-> sem_empty = sem_open (Key + 2 ); } Return FIFO; } Void shm1_o_put (shm1_o_t * FIFO, const void * BUF) { Sem_p (FIFO-> sem_full ); Sem_p (FIFO-> sem_mutex ); Memcpy (FIFO-> p_payload + FIFO-> p_shm-> blksize * FIFO-> p_shm-> wr_index, Buf, FIFO-> p_shm-> blksize ); FIFO-> p_shm-> wr_index = (FIFO-> p_shm-> wr_index + 1) % FIFO-> p_shm-> blocks; Sem_v (FIFO-> sem_mutex ); Sem_v (FIFO-> sem_empty ); } Void shm1_o_get (shm1_o_t * FIFO, void * BUF) { Sem_p (FIFO-> sem_empty ); Sem_p (FIFO-> sem_mutex ); Memcpy (BUF, FIFO-> p_payload + FIFO-> p_shm-> blksize * FIFO-> p_shm-> rd_index, FIFO-> p_shm-> blksize ); FIFO-> p_shm-> rd_index = (FIFO-> p_shm-> rd_index + 1) % FIFO-> p_shm-> blocks; Sem_v (FIFO-> sem_mutex ); Sem_v (FIFO-> sem_full ); } Void shm1_o_destroy (shm1_o_t * FIFO) { Sem_d (FIFO-> sem_mutex ); Sem_d (FIFO-> sem_full ); Sem_d (FIFO-> sem_empty ); Shmdt (FIFO-> p_shm ); Shmctl (FIFO-> shmid, ipc_rmid, 0 ); Free (FIFO ); } |
1. shm1_o_init: allocate the shmfifo struct memory first. If you fail to enable the shared memory, the created shared memory segment size = shmhead size + block size X number of blocks will be created, then, shmat maps the shared memory field to the process address space, and then uses sem_create to create three semaphore sets. Each signal set has only one semaphore, that is, the three semaphores mentioned above, set the initial value of each semaphore. If the shared memory already exists, sem_open
Open it. For more information about sem_xxx encapsulation functions, see here.
2. shm1_o_put: refer to the figure of the first producer consumer. In addition to the sem_p and sem_v operations, memcpy of the Buf content to the corresponding buffer block and move wr_index.
3. shm1_o_get: similar to shm1_o_put, shm1_o_get performs the opposite operation.
4. shm1_o_destroy: Delete the three semaphore sets, detach the shared memory segments from the process address space, delete the shared memory segments, and release the memory of the shmfifo struct.
The following are producer and consumer programs:
Shm1_o_send.c
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
# Include "shmfifo. H" Typedef struct Stu { Char name [32]; Int age; } Stu; Int main (void) { Shm1_o_t * FIFO = shm1_o_init (1234, sizeof (Stu), 3 ); Stu S; Memset (& S, 0, sizeof (Stu )); S. name [0] = 'a '; Int I; For (I = 0; I <5; I ++) { S. Age = 20 + I; Shm1_o_put (FIFO, & S ); S. name [0] = S. name [0] + 1; Printf ("Send OK \ n "); } Return 0; } |
Shm1_o_recv.c
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
# Include "shmfifo. H" Typedef struct Stu { Char name [32]; Int age; } Stu; Int main (void) { Shm1_o_t * FIFO = shm1_o_init (1234, sizeof (Stu), 3 ); Stu S; Memset (& S, 0, sizeof (Stu )); Int I; For (I = 0; I <5; I ++) { Shm1_o_get (FIFO, & S ); Printf ("name = % s age = % d \ n", S. Name, S. Age ); } Shm1_o_destroy (FIFO ); Return 0; } |
Run the producer process first and output the following:
Simba @ Ubuntu :~ /Documents/code/linux_programming/UNP/system_v/shmfifo $./shm1_o_send
Send OK
Send OK
Send OK
Because there are only three blocks in the shared memory, P (semfull) blocks again after being sent three times, waiting for the consumer to read data, and now running the consumer Process
Simba @ Ubuntu :~ /Documents/code/linux_programming/UNP/system_v/shmfifo $./shm1_o_recv
Name = A age = 20
Name = B age = 21
Name = C age = 22
Name = d age = 23
Name = e age = 24
Because the producer has already created a shared memory, the consumer just opens it. After reading the first data, the producer will write it again and output the last two send OK messages in sequence, it can be inferred that D is the first block starting from re-writing to the shared memory, and E is the second block, similar to the ring queue.
From the output, we can see that the data is indeed first-in-first-out.
Reference: UNP