Sự cố bạn đang gặp phải là sự hiểu lầm của chức năng sem_init()
. Khi bạn đọc những manual page bạn sẽ thấy điều này:
Đối số pshared cho biết semaphore này là để được chia sẻ giữa các chủ đề của một quá trình, hoặc giữa các quá trình.
Nếu bạn đọc xong đến thời điểm này, bạn sẽ nghĩ rằng giá trị khác không được chia sẻ sẽ thực hiện semaphore semaphore inter-process semaphore. Tuy nhiên, điều này là sai. Bạn nên tiếp tục đọc và bạn sẽ hiểu rằng bạn phải xác định vị trí semaphore trong vùng bộ nhớ dùng chung. Để làm điều đó, một số chức năng có thể được sử dụng như bạn có thể xem dưới đây:
Nếu pshared là khác không, sau đó semaphore được chia sẻ giữa các quá trình, và cần được nằm trong một khu vực của bộ nhớ chia sẻ (xem shm_open (3), mmap (2) và shmget (2)). (Vì trẻ được tạo bởi ngã ba (2) thừa hưởng ánh xạ bộ nhớ của cha mẹ, nó cũng có thể truy cập semaphore.)) vv
tôi thấy phương pháp này như một cách tiếp cận phức tạp hơn những người khác, do đó tôi muốn khuyến khích mọi người sử dụng sem_open()
thay vì sem_init()
.
Dưới đây bạn có thể thấy một chương trình hoàn toàn minh họa như sau:
- Làm thế nào để phân bổ bộ nhớ chia sẻ và sử dụng các biến chia sẻ giữa chia hai quy trình.
- Cách khởi tạo semaphore trong vùng bộ nhớ dùng chung và được sử dụng bởi nhiều quy trình.
- Cách chia nhỏ nhiều quy trình và làm cho phụ huynh đợi cho đến khi tất cả các con của nó xuất hiện.
#include <stdio.h> /* printf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/types.h> /* key_t, sem_t, pid_t */
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <errno.h> /* errno, ECHILD */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
int main (int argc, char **argv){
int i; /* loop variables */
key_t shmkey; /* shared memory key */
int shmid; /* shared memory id */
sem_t *sem; /* synch semaphore *//*shared */
pid_t pid; /* fork pid */
int *p; /* shared variable *//*shared */
unsigned int n; /* fork count */
unsigned int value; /* semaphore value */
/* initialize a shared variable in shared memory */
shmkey = ftok ("/dev/null", 5); /* valid directory name and a number */
printf ("shmkey for p = %d\n", shmkey);
shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
if (shmid < 0){ /* shared memory error check */
perror ("shmget\n");
exit (1);
}
p = (int *) shmat (shmid, NULL, 0); /* attach p to shared memory */
*p = 0;
printf ("p=%d is allocated in shared memory.\n\n", *p);
/********************************************************/
printf ("How many children do you want to fork?\n");
printf ("Fork count: ");
scanf ("%u", &n);
printf ("What do you want the semaphore value to be?\n");
printf ("Semaphore value: ");
scanf ("%u", &value);
/* initialize semaphores for shared processes */
sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value);
/* name of semaphore is "pSem", semaphore is reached using this name */
printf ("semaphores initialized.\n\n");
/* fork child processes */
for (i = 0; i < n; i++){
pid = fork();
if (pid < 0) {
/* check for error */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
printf ("Fork error.\n");
}
else if (pid == 0)
break; /* child processes */
}
/******************************************************/
/****************** PARENT PROCESS ****************/
/******************************************************/
if (pid != 0){
/* wait for all children to exit */
while (pid = waitpid (-1, NULL, 0)){
if (errno == ECHILD)
break;
}
printf ("\nParent: All children have exited.\n");
/* shared memory detach */
shmdt (p);
shmctl (shmid, IPC_RMID, 0);
/* cleanup semaphores */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
exit (0);
}
/******************************************************/
/****************** CHILD PROCESS *****************/
/******************************************************/
else{
sem_wait (sem); /* P operation */
printf (" Child(%d) is in critical section.\n", i);
sleep (1);
*p += i % 3; /* increment *p by 0, 1 or 2 based on i */
printf (" Child(%d) new value of *p=%d.\n", i, *p);
sem_post (sem); /* V operation */
exit (0);
}
}
OUTPUT
./a.out
shmkey for p = 84214791
p=0 is allocated in shared memory.
How many children do you want to fork?
Fork count: 6
What do you want the semaphore value to be?
Semaphore value: 2
semaphores initialized.
Child(0) is in critical section.
Child(1) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) is in critical section.
Child(3) is in critical section.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) is in critical section.
Child(5) is in critical section.
Child(4) new value of *p=4.
Child(5) new value of *p=6.
Parent: All children have exited.
Nó không phải là xấu để kiểm tra shmkey
kể từ khi ftok()
thất bại, nó sẽ trả về -1.Tuy nhiên, nếu bạn có nhiều biến được chia sẻ và nếu hàm ftok()
bị lỗi nhiều lần, các biến được chia sẻ có shmkey
với giá trị -1
sẽ nằm trong cùng một vùng của bộ nhớ dùng chung. Do đó việc thực hiện chương trình sẽ bị lộn xộn. Để tránh điều này, hãy kiểm tra xem nếu ftok()
trả về -1 hay không (tốt hơn là kiểm tra mã nguồn thay vì in trên màn hình như tôi đã làm, mặc dù tôi muốn hiển thị cho bạn các giá trị chính trong trường hợp có xung đột).
Chú ý đến cách semaphore được khai báo và khởi tạo. Nó khác với những gì bạn đã làm trong câu hỏi (sem_t sem
vs sem_t* sem
). Hơn nữa, bạn nên sử dụng chúng khi chúng xuất hiện trong ví dụ này. Bạn không thể xác định sem_t*
và sử dụng nó trong sem_init()
.
Vấn đề là một số trang người đàn ông không rõ ràng. Hãy xem SUSv2 (ví dụ): 'Nếu đối số được chia sẻ có một giá trị khác không, thì semaphore được chia sẻ giữa các quá trình; trong trường hợp này, bất kỳ quá trình nào có thể truy cập sem sem sem semore có thể sử dụng sem để thực hiện các thao tác sem_wait(), sem_trywait(), sem_post(), và sem_destroy(). Nó khó hiểu hơn khi sử dụng bộ nhớ chia sẻ! –
thật không may .. Nó đã cho tôi 2 bài tập về nhà để tìm ra :) Đây là những gì Linux thiếu trong quan điểm của tôi: họ cho rằng mọi người đã biết những thứ như thể họ là một phần của cộng đồng nhà phát triển. Một lần nữa, theo ý kiến của tôi, nó phải được giải thích, không giống như các hướng dẫn sử dụng. – Varaquilex
Tôi nghĩ rằng bạn nên tách bộ nhớ chia sẻ ra khỏi các tiến trình con cũng như, sau khi một ngã ba (2) đứa trẻ thừa kế các phân đoạn bộ nhớ chia sẻ đính kèm' (man shmdt). – Chris