Author: Wu te
After learning semaphores and shared memory, we can implement process synchronization and mutex. Here, the most typical example is the producer and consumer model. Next we will discuss with you how to implement this classic model step by step. The complete code can be downloaded here.
The following example shows how multiple producers and consumers access N buffers (N racks. Now, let's first think about how we wrote the previous pseudo code? Is it like this:
// Producer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
While (1)
{
P (semid, 1 );
Sleep (3 );
P (semid, 0 );
// Producer is producing a product
Goods = rand () % 10; // product a goods
Shmaddr [indexaddr [0] = goods; // The goods is placed on a shelf
Printf ("producer: % d produces a product [% d]: % d", getpid (), indexaddr [0], goods );
Indexaddr [0] = (indexaddr [0] + 1) % 10;
V (semid, 0 );
Sleep (3 );
V (semid, 2 );
}
// Consumer:
1
2
3
4
5
6
7
8
9
10
11
12
13
While (1)
{
P (semid, 2 );
Sleep (1 );
P (semid, 0 );
// Consumer is consuming a product
Goods = shmaddr [indexaddr [1]; // The goods on the shelf is taken down
Printf ("consumer: % d consumes a product [% d]: % d", getpid (), indexaddr [1], goods );
Indexaddr [1] = (indexaddr [1] + 1) % num;
V (semid, 0 );
Sleep (1 );
V (semid, 1 );
}
Maybe you are familiar with the above Code and confused, because it is not exactly the same as the code in the textbook. In fact, the above Code is the specific implementation of the pseudo-code linuxC language. From the above Code, we are looking for traces of pseudo code: p (semid, 0) and v (semid, 0) are used to allow processes to access the critical zone mutex. The data indexaddr [0], indexaddr [1], and shmaddr arrays in the critical section correspond to in, out, and buffer in the Pseudo Code respectively. P (semid, 1), v (semid, 2), p (semid, 2), and v (semid, 1) implement synchronization.
In addition, in the producer, the producer produces a commodity (goods = rand () % 10 ;), then put the goods on the shelf (shmaddr [indexaddr [0] = goods ;). In the consumer, consume and remove the goods from the shelf (goods = shmaddr [indexaddr [1];).
Now let's look at the code above. I think your ideas are clear.
After learning about the core code, we can't just complete the producer and consumer models, because some preparation work has to be done before the core code of the producer and consumer. Let's analyze what to prepare.
First, apply for a shared memory, which is used to store the goods produced by the producer. At the same time, we can see that the shared memory size is 10 bytes. Note that every production or consumer tries to allocate such a shared memory after running. If a process has already created this shared memory before the current process runs, the process will not be created (at this time, createshm will return-1 and the error code is EEXIST ), open the shared memory. After creation, add the shared memory to the address space of the current process.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Num = 10;
// Create a shared memory as goods buffer
If (shmid_goods = createshm (".", s, num) =-1)
{
If (errno = EEXIST)
{
If (shmid_goods = openshm (".", s) =-1)
{
Exit (1 );
}
}
Else
{
Perror ("create shared memory failed ");
Exit (1 );
}
}
// Attach the shared memory to the current process
If (shmaddr = shmat (shmid_goods, (char *) 0, 0) = (char *)-1)
{
Perror ("attach shared memory error ");
Exit (1 );
}
Next, we need to apply for a shared memory to store two integer variables in and out (in fact, applying for an array containing two integer variables ). They record the index of "shelves" when producing and consuming goods. As shown above, if another process has created this shared memory, the current process only opens it.
Note that the value of the initialization of the Two integer variables is 0.
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
// Create a shared memory as index
If (shmid_index = createshm (".", z, 8) =-1)
{
If (errno = EEXIST)
{
If (shmid_index = openshm (".", z) =-1)
{
Exit (1 );
}
}
Else
{
Perror ("create shared memory failed ");
Exit (1 );
}
}
Else
{
Is_noexist = 1;
}
// Attach the shared memory to the current process
If (indexaddr = shmat (shmid_index, (int *) 0, 0) = (int *)-1)
{
Perror ("attach shared memory error ");
Exit (1 );
}
If (is_noexist)
{
Indexaddr [0] = 0;
Indexaddr [1] = 0;
}
The next step is to create a semaphore set, which contains three semaphores. The mutex of the first semaphore, that is, the process's mutex access to the critical section. The remaining two functions achieve synchronization and coordinate the reasonable operation of producers and consumers, that is, when there is no space on the shelves, the producers no longer produce, and when there is no goods on the shelves, the consumers no longer consume.
Pay attention to the assignment of each semaphore. Of course, the initial value of mutex semaphores is 1. The sum of the synchronous semaphores cannot be greater than the num value.
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
// Create a semaphore set including 3 semaphores
If (semid = createsem (".", t, 3, 0) =-1)
{
If (errno = EEXIST)
{
If (semid = opensem (".", t) =-1)
{
Exit (1 );
}
}
Else
{
Perror ("semget error :");
Exit (1 );
}
}
Else
{
Union semun arg;
// Seting value for mutex semaphore
Arg. val = 1;
If (semctl (semid, 0, SETVAL, arg) =-1)
{
Perror ("setting semaphore value failed ");
Return-1;
}
// Set value for synchronous semaphore
Arg. val = num;
// The num means that the producer can continue to produce num products
If (semctl (semid, 1, SETVAL, arg) =-1)
{
Perror ("setting semaphore value failed ");
Return-1;
}
// The last semaphores value is default
// The default value 0 means that the consumer is not use any product now
}
Basically, this completes the preliminary work of producers and consumers. As we can see, in the core code, we only need to "pretend as a sample" to "let everyone" the Code. Of course, you need to understand the basic model of producer and consumer. In the following preparation code, we need to understand some basic functions about semaphores and shared memory.
In the end, we recommend that you first run a producer and a consumer and observe how the two work together. Then run only one producer or one consumer to check whether the producer is blocked. After learning about the above situation, you can run multiple producers and consumers at the same time.