APUE学习笔记-15章进程间通信
IPC進程間通信
進程間通信的方式主要是有:
管道
半雙工方式是最常用的IPC形式(數據只能是在一個方向上流動)
#include <unistd.h>int pipe(int fd[2]);單個進程中的管道意義不大,通常是pipe之后調用fork來創建父子之間的ipc管道。
我們可以通過關閉父進程或者子進程的讀寫的端口來實現 控制數據流的方向
🌰:從父進程流向子進程的管道, 父進程關閉管道的讀端(fd[0]), 子進程關閉管道的寫端(fd[1])
如果想從子進程向父進程的流向,關閉的順序相反就行
🌰父進程到子進程的管道
#include "apue.h"int main() {int n;int fd[2];pid_t pid;char line[MAXLINE];if (pipe(fd) < 0) err_sys("pipe error");if ((pid = fork()) < 0) err_sys("fork error");else if (pid > 0) { //parentclose(fd[0]); //close read port write(fd[1], "hello from father\n", 18);} else { //child close(fd[1]);sleep(1);n = read(fd[0], line, n);write(STDOUT_FILENO, line, n); //將從管道讀取的數據寫到標準輸出中}exit(0); }🌰:使用兩個管道實現父子進程之間的同步
#include "apue.h"static int pfd1[2], pfd2[2];void TELL_WAIT() {if (pipe(pfd1) < 0 || pipe(pfd2) < 0)err_sys("pipe error"); }void TELL_PARENT(pid_t pid) {if (write(pfd2[1], "c", 1) != 1)err_sys("write error"); }void TELL_CHILD(pid_t pid) {if (write(pfd1[1], "p", 1) != 1)err_sys("write error"); }void WAIT_PARENT() {char c;if (read(pfd1[0], &c, 1) != 1) err_sys("read error");if (c != 'p')err_quit("wait parent"); }void WAIT_CHILD() {char c;if (read(pfd2[0], &c, 1) != 1) err_sys("read error");if (c != 'c')err_quit("wait parent"); }函數popen和pclose
常見的使用管道的方式是:創建一個管道同一個進程進行交互,向另一個進程的管道的端口進行交互
popen:創建管道,fork子進程,關閉未使用的管道端口,執行shell命令,等待命令終止
#include <stdio.h>FILE* popen(const char* cmdstring, const char *type);int pclose(FILE* fp);函數popen首先fork,然后調用exec執行cmdstring,返回一個標準IO文件指針。如果type 是 “r”, 文件指針連接到cmdstring 的標準輸出,為“w”則反之
🌰:創建過濾程序將用戶輸入的字符轉換成小寫
Trans.cpp
#include "apue.h" #include <ctype.h>int main() {int c;while ((c = getchar()) != EOF) {if (isupper(c))c = tolower(c);if (putchar(c) == EOF)err_sys("output error");if (c == '\n')fflush(stdout);}exit(0); }main.cpp
#include "apue.h" #include <sys/wait.h>int main() {char line[MAXLINE];FILE *fpin;if ((fpin = popen("./mytrans", "r")) == NULL) //將文件流連接到trans程序的標準輸出上面(也就是能夠讀取到字符轉換后的結果)err_sys("popen error");for (;;) {//下面都是父進程的工作,子進程調用mytrans,用戶就是同mytrans進行交互,父進程來進行調度fputs("prompt> ", stdout); //用戶的標準輸出上打印prompt >fflush(stdout);if (fgets(line, MAXLINE, fpin) == NULL) //從trans的標準輸出中讀取break;if (fputs(line, stdout) == EOF)err_sys("fputs error to pipe");}if (pclose(fpin) == -1) {err_sys("pclose error");}putchar('\n');exit(0); }解析:用戶的輸入是同trans程序進行交互的,父進程進行總體的調度
協同進程
當一個過濾的程序既產生某個過濾程序的輸入又讀取該過濾程序的輸出的時候,就變成了協同進程
🌰:協同進程的實例
協同進程從標準輸入讀取兩個數字,將和寫到標準輸出中(簡單實例,實際上協同進程做更加有意義的事情)
#include "apue.h" int main() {int n, num1, num2;char line[MAXLINE];while ((n = read(STDIN_FILENO, line, MAXLINE)) > 0) {line[n] = 0; //null terminate if (sscanf(line, "%d%d", &num1, &num2) == 2) {sprintf(line, "%d\n", num1 + num2);n = strlen(line);if (write(STDOUT_FILENO, line, n) != n) err_sys("write error");} else {if (write(STDOUT_FILENO, "invalid args\n", 13) != 13) err_sys("write error");}}exit(0); }進行協同進程的樣例演示,父子進程關閉不需要的端口, 子進程使用dup2讓管道描述符號移至標準輸入和標準輸出,最終調用execl
#include "apue.h"static void sig_pipe(int); /* our signal handler */int main(void) {int n, fd1[2], fd2[2];pid_t pid;char line[MAXLINE];if (signal(SIGPIPE, sig_pipe) == SIG_ERR)err_sys("signal error");if (pipe(fd1) < 0 || pipe(fd2) < 0)err_sys("pipe error");if ((pid = fork()) < 0) {err_sys("fork error");} else if (pid > 0) { /* parent */close(fd1[0]);close(fd2[1]);while (fgets(line, MAXLINE, stdin) != NULL) {n = strlen(line);if (write(fd1[1], line, n) != n)err_sys("write error to pipe");if ((n = read(fd2[0], line, MAXLINE)) < 0)err_sys("read error from pipe");if (n == 0) {err_msg("child closed pipe");break;}line[n] = 0; /* null terminate */if (fputs(line, stdout) == EOF)err_sys("fputs error");}if (ferror(stdin))err_sys("fgets error on stdin");exit(0);} else { /* child */close(fd1[1]);close(fd2[0]);if (fd1[0] != STDIN_FILENO) {if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO)err_sys("dup2 error to stdin");close(fd1[0]);}if (fd2[1] != STDOUT_FILENO) {if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO)err_sys("dup2 error to stdout");close(fd2[1]);}if (execl("./add2", "add2", (char *)0) < 0)err_sys("execl error");}exit(0); }static void sig_pipe(int signo) {printf("SIGPIPE caught\n");exit(1); }FIFO
fifo稱為命名管道,未命名的管道只能是在兩個相關的進程之間使用.通過fifo,不相關的進程之間也是可以進行交換數據
#include <sys/stat.h>int mkfifo(const char* path, mode_t mode);int mkfifoat(int fd, const char* path, mode_t mode);阻塞與非阻塞:(O_NOBLOCK)
- 沒有指定非阻塞的情況下, 只讀open要阻塞到 某個進程為了寫而打開這個fifo。只寫open類似
- 如果指定了非阻塞,那么open之后立即返回,如果沒有可讀就返回-1, 設置error : ENXIO
用途:
- shell命令使用FIFO將數據從一條管道傳送到另一條管道,無需創建中間臨時文件
- 客戶進程-服務器進程應用程序中,FIFO用作匯聚點,在兩者進程之間傳遞數據
信號量、消息隊列、共享內存
我看書上的解釋也不是太多,這里就貼上小林Coding的解析好了🤪地址
Socket
章節內容等到unp欄目再記筆記
總結
以上是生活随笔為你收集整理的APUE学习笔记-15章进程间通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【理论-Cisco】策略路由PBR
- 下一篇: 斗破苍穹java_安卓斗破苍穹游戏源码