WeHelp
C 語言可以直接呼叫作業系統提供的 System Call 且速度極快,用來開發基礎系統程式非常適合。
  1. C 系統程式
  2. C 檔案寫入、讀取
  3. Process 程序管理
  4. Thread 執行緒管理
  5. IPC 程序間通訊
  6. PIPE 程序間通訊
  7. FIFO 程序間通訊
  8. 共享記憶體通訊
  9. Socket 程序間通訊
  10. TCP 網路連線
PIPE 程序間通訊
## IPC PIPE 簡介 `PIPE`(管線)是一種常見的 IPC(Inter-Process Communication,行程間通訊)機制,主要用來讓具有親屬關係的行程,例如父行程與子行程,交換資料。 ### PIPE 的主要特性 1. **單向通訊** - 一個 Pipe 通常是單向的: - `fd[0]`:讀取端 - `fd[1]`:寫入端 - 若要雙向通訊,通常需要建立兩個 Pipe。 2. **以位元組串流傳輸** - Pipe 不保留訊息邊界,資料會以連續的 byte stream 形式傳送。 3. **通常用於相關行程** - 匿名 Pipe 通常在 `fork()` 之前建立,讓父子行程繼承檔案描述元。 - 若是不相關的行程,則可使用 FIFO(named pipe)。 4. **核心管理的緩衝區** - Pipe 的資料暫存在核心空間中。 - 寫入端沒有讀取者時,寫入可能失敗或收到 `SIGPIPE`。 - 當 Pipe 沒有資料時,`read()` 預設會阻塞等待。 - 當所有寫入端都關閉後,讀取端的 `read()` 會回傳 `0`,表示 EOF。 5. **具有阻塞特性** - `write()` 可能在 Pipe 緩衝區滿時阻塞。 - `read()` 可能在 Pipe 沒資料時阻塞。 - 可使用 `fcntl()` 設定為 non-blocking 模式。 6. **寫入的原子性** - 對於不超過 `PIPE_BUF` 的單次寫入,系統通常保證不會與其他寫入交錯。 --- ## C 如何透過 System Call 使用 PIPE 建立 Pipe 的核心系統呼叫為: ```c int pipe(int pipefd[2]); ``` 成功後: ```c pipefd[0] // 讀取端 pipefd[1] // 寫入端 ``` 常見流程如下: 1. 呼叫 `pipe()` 建立 Pipe。 2. 呼叫 `fork()` 建立子行程。 3. 父子行程分別關閉不使用的檔案描述元。 4. 使用: - `write()` 寫入資料 - `read()` 讀取資料 - `close()` 關閉 Pipe 5. 父行程可使用 `wait()` 等待子行程結束。 雖然 C 程式通常呼叫的是 `pipe()`、`read()`、`write()` 等函式,但這些函式一般是對 Linux System Call 的封裝。 --- ## 簡單範例:父行程透過 Pipe 傳資料給子行程 ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> int main(void) { int pipefd[2]; pid_t pid; char buffer[128]; /* * 建立 Pipe * pipefd[0]:讀取端 * pipefd[1]:寫入端 */ if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid == 0) { /* 子行程:負責讀取 */ close(pipefd[1]); // 子行程不需要寫入端 ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1); if (n == -1) { perror("read"); exit(EXIT_FAILURE); } buffer[n] = '\0'; printf("子行程收到資料:%s\n", buffer); close(pipefd[0]); exit(EXIT_SUCCESS); } else { /* 父行程:負責寫入 */ const char *message = "Hello from parent process!"; close(pipefd[0]); // 父行程不需要讀取端 if (write(pipefd[1], message, strlen(message)) == -1) { perror("write"); exit(EXIT_FAILURE); } close(pipefd[1]); // 關閉後,子行程之後可收到 EOF wait(NULL); // 等待子行程結束 } return 0; } ``` ### 編譯與執行 ```bash gcc pipe_example.c -o pipe_example ./pipe_example ``` 可能輸出: ```text 子行程收到資料:Hello from parent process! ``` ### 範例流程說明 ```text 父行程 子行程 | | | pipe() | | fork() -------------------->| | | | write(pipefd[1], ...) | |----------------------------->| | read(pipefd[0], ...) ``` 這個範例中,父行程透過 Pipe 的寫入端傳送字串,子行程則透過讀取端接收資料。關閉未使用的 Pipe 端非常重要,否則可能造成資源浪費,或使讀取端無法正確判斷 EOF。
相關學習地圖、教學課程
C 語言,系統程式
掌握基礎的 C 語法,以及進階的系統程式開發能力。