在多線程程式設計中,共享內存配合信號量是實現線程間高效通訊的經典模式。本文透過一個完整的 C 語言範例,示範如何利用 System V 共享內存與信號量,建立一個「生產者-消費者」模型:子進程負責接收鍵盤輸入並寫入共享內存,父進程從共享內存讀取資料並寫入檔案。
#define MAXS (1024+1)
#define BUFFERSIZE 200
#define SEMID 251 // 信號量鍵值
#define SHMKEY 241 // 共享內存鍵值
#define SHMSIZE MAXS // 共享內存大小
#define FILENAME "abc.txt"
semget() 建立單一信號量,semctl() 初始化為 0。shmget() 申請,shmat() 掛載至進程位址空間。sem_op = 1(V 操作)。sem_op = -1(P 操作)等待,讀取共享內存一行,寫入檔案 abc.txt。Q 時,子進程在共享內存末尾寫入 -1 作為結束標記,父進程讀到後跳出迴圈。wait() 回收子進程,shmdt() 分離共享內存,shmctl(IPC_RMID) 刪除共享內存。$ gcc -std=gnu99 -o testshm testshm.c
$ ./testshm
deepfuture.javeye.com#line 1$deepfuture
deepfuture.javeye.com#line 2$javaeye
...
deepfuture.javeye.com#line 6$Q
退出....
$ cat abc.txt
deepfuture
javaeye
com
myhaspl
#include <stdio.h>
#include <unistd.h>
#include <linux/types.h>
#include <linux/shm.h>
#include <linux/sem.h>
#include <linux/ipc.h>
#define MAXS (1024+1)
#define BUFFERSIZE 200
#define SEMID 251
#define FILENAME "abc.txt"
#define SHMKEY 241
#define SHMSIZE MAXS
int main(void) {
char strbuf[MAXS], buf[BUFFERSIZE];
int sem_id, shm_id, pid, rc, res;
struct sembuf sem_op;
union semun sem_val;
char *cur, *shm_addr;
FILE *myfile;
int line = 1;
// 建立信號量
sem_id = semget(SEMID, 1, IPC_CREAT | 0600);
if (sem_id == -1) { perror("create sem"); exit(1); }
sem_val.val = 0;
if (semctl(sem_id, 0, SETVAL, sem_val) == -1) { perror("init sem"); exit(1); }
// 建立共享內存
shm_id = shmget(SHMKEY, SHMSIZE, IPC_CREAT | 0600);
if (shm_id == -1) { perror("create shm"); exit(1); }
shm_addr = (char *)shmat(shm_id, NULL, 0);
if (!shm_addr) { perror("shmat"); exit(1); }
memset(shm_addr, '\0', MAXS);
cur = shm_addr;
pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
else if (pid == 0) { // 子進程:輸入 -> 共享內存
int isend = 0;
printf("\ndeepfuture.javeye.com#line %d$", line);
while (!isend && fgets(buf, BUFFERSIZE, stdin)) {
line++;
printf("\ndeepfuture.javeye.com#line %d$", line);
if (buf[0] == 'Q' && strlen(buf) <= 2) {
isend++; printf("\n退出....\n");
} else {
memcpy(cur, buf, strlen(buf));
cur += strlen(buf);
}
sem_op.sem_num = 0; sem_op.sem_op = 1; sem_op.sem_flg = 0;
semop(sem_id, &sem_op, 1);
}
*cur = -1; exit(0);
}
else { // 父進程:共享內存 -> 檔案
while (1) {
sem_op.sem_num = 0; sem_op.sem_op = -1; sem_op.sem_flg = 0;
semop(sem_id, &sem_op, 1);
if (*cur == -1) break;
int i = 0;
for (; *cur != '\n'; cur++, i++) buf[i] = *cur;
cur++; buf[i] = '\n'; buf[++i] = 0;
FILE *fp = fopen(FILENAME, "ab");
res = fwrite(buf, strlen(buf), 1, fp);
if (res == -1) { perror("write"); exit(1); }
fclose(fp);
}
wait(&pid);
shmdt(shm_addr);
struct shmid_ds shm_desc;
shmctl(shm_id, IPC_RMID, &shm_desc);
exit(0);
}
}