首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C 指標原理(91):線程池共享內存與信號量實戰

     編輯:關於C

前言

在多線程程式設計中,共享內存配合信號量是實現線程間高效通訊的經典模式。本文透過一個完整的 C 語言範例,示範如何利用 System V 共享內存與信號量,建立一個「生產者-消費者」模型:子進程負責接收鍵盤輸入並寫入共享內存,父進程從共享內存讀取資料並寫入檔案。

程式架構解析

關鍵資料結構與常數定義

#define MAXS (1024+1)
#define BUFFERSIZE 200
#define SEMID 251        // 信號量鍵值
#define SHMKEY 241       // 共享內存鍵值
#define SHMSIZE MAXS     // 共享內存大小
#define FILENAME "abc.txt"

核心流程

  1. 建立信號量集:semget() 建立單一信號量,semctl() 初始化為 0。
  2. 建立共享內存:shmget() 申請,shmat() 掛載至進程位址空間。
  3. fork 子進程:
    • 子進程:迴圈讀取標準輸入,寫入共享內存,每寫一行對信號量 sem_op = 1(V 操作)。
    • 父進程:迴圈對信號量 sem_op = -1(P 操作)等待,讀取共享內存一行,寫入檔案 abc.txt。
  4. 結束條件:輸入單獨字元 Q 時,子進程在共享內存末尾寫入 -1 作為結束標記,父進程讀到後跳出迴圈。
  5. 資源清理: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);
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved