Linux高級系統編程 - 5 管道

復制文件描述符

dup函數

作用 : 文件描述符復制
語法
????????#include <unistd.h>
????????int dup(int oldfd);
參數 :
????????所需復制的文件描述符
返回值
????????復制得到的文件描述符
功能 : 從文件描述符表中 , 尋找一個最小可能的文件描述符(通過返回值返回)作為 oldfd復制
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int newFd = dup(1);write(newFd, "hello world", 11);return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int fd = open("a.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);close(1);dup(fd);printf("啦啦啦,德瑪西亞");close(fd);return 0;
}

dup2函數(推薦)

#include <unistd.h>
int dup2(int oldfd, int newfd);
參數 :
????????oldfd:原文件描述符
????????newfd:指定復制到的文件描述符 , 如果該文件描述符存在 , 那么將原有的關閉
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int fd = open("a.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);printf("123");return 0;
}

無名管道

又名管道 (pipe)
????????無名管道是一種特殊類型的文件,在應用層體現為兩個打開的文件描述符,1 個描述符寫 fd[1], 1 個描述符讀 fd[0]
核心 :0 1
特點:
????????1,管道不是普通的文件 , 不屬于某個文件系統 , 其只存在于內存中。
????????2,半雙工,數據在同一時刻只能在一個方向上流動
補充
????????單工: 指數據傳輸只支持數據在一個方向上傳輸
????????雙工: 指二臺通訊設備之間,允許有雙向的資料傳輸
????????全雙工: 允許二臺設備間同時進行雙向數據傳輸。一般的電話、手機就是全雙工的系統,因為在講話時同時也可以聽到對方的聲音。
????????半雙工: 允許二臺設備間進行雙向數據傳輸 , 但不能同時進行。因此同一 時間只允許一設備傳送資料,若另一設備要傳送資料,需等原來傳送資料的設備傳送完 成后再處理。
????????3,數據只能從管道的一端寫入,從另一端讀出。
????????4,寫入管道中的數據遵循先入先出的規則。
????????5,管道所傳送的數據是無格式的,這要求管道的讀出方與寫入方必須事先約定好數 據的格式,如多少字節算一個消息等
????????6,管道在內存中對應一個緩沖區。不同的系統其大小不一定相同。
????????7,從管道讀數據是一次性操作,數據一旦被讀走,它就從管道中被拋棄,釋放空間 以便寫更多的數據
????????8,管道沒有名字,只能在具有公共祖先的進程之間使用。
補充 :
????????管道可以用于任意兩個或更多相關進程之間的通信,只要在創建子進程 的系列調用之前通過一個共同的祖先進程創建管道即可。
????????如管道可用于一個進程和其子孫進程之間的通信。第一個進程創建管 道,然后創建子進程,接著子進程再創建第一個進程的孫子進程。
????????管道通常用于兩個兄弟進程之間的通信—— 它們的父進程創建了管道,并 創建兩個子進程。

pipe函數

作用 : 創建無名管道
語法
????????#include <unistd.h>
????????int pipe(int fd[2]);
參數:
????????fd 為 int 型數組的首元素地址,其存放了管道的文件描述符 fd[0] fd[1]
????????fd[0]為讀而打開, fd[1] 為寫而打開管道。
返回值:
????????成功:返回 0
????????失敗:返回-1
如:? ? ? ? int fd[2];
????????????????pipe(fd);
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{// 1,創建管道int fd[2];pipe(fd);// 2,創建子進程,子進程與父進程具有公共祖先int pid = fork();if (pid < 0){printf("創建子進程失敗");return 0;}else if (pid == 0){// 子進程,讀取父進程傳遞的數據// 因為子進程只讀取消息,所以寫入無用,可以關閉close(fd[1]);char buf[128];read(fd[0], buf, sizeof(buf));printf("子進程接收到的消息:%s\n", buf);// 讀取結束關閉讀close(fd[0]);// 關閉子進程_exit(-1);}else if (pid > 0){// 父進程,寫入數據// 因為父進程只寫入消息,所以讀無用,可以關閉close(fd[0]);write(fd[1], "hello gd", 8);printf("父進程發送消息完成\n");close(fd[1]);wait(NULL);}return 0;
}

讀寫特點

????????1、默認用 read 函數從管道中讀數據是阻塞的。
????????2、調用 write 函數向管道里寫數據,當緩沖區已滿時 write 也會阻塞。管道的緩沖區的大小: 64Kb
????????3、通信過程中,讀端口全部關閉后,寫進程向管道內寫數據時,寫進程會(收到 SIGPIPE 信號)退出。
????????4,從管道中讀數據的特點 編程時可通過 fcntl 函數設置文件的阻塞特性。設置為阻 塞:fcntl(fd, FSETFL,0); 設置為非阻塞: fcntl(fd, FSETFL, O_NONBLOCK);
示例 1 :緩沖區已滿時 write 也會阻塞。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);// 0讀1寫int pid = fork();if (pid == 0){close(fd[1]);sleep(2);close(fd[0]);_exit(0);}else if (pid > 0){close(fd[0]);int count = 0;for (int i = 1; i < 10000; i++){char buf[1024] = {0};write(fd[1], buf, 1024);count += 1024;printf("i=%d\tcount=%d\n", i, count);}close(fd[1]);wait(NULL);}return 0;
}
示例 2 :通信過程中,寫端關閉,讀端將解阻塞
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int pd[2];pipe(pd);int pid = fork();if (pid == 0){close(pd[1]);char buf[100] = {0};printf("等待寫入\n");read(pd[0], buf, 100);close(pd[0]);printf("子進程結束\n");_exit(0);}else if (pid > 0){close(pd[0]);printf("5秒后關閉寫\n");sleep(5);close(pd[1]);wait(NULL);}return 0;
}
示例 3: 通信過程中 讀端關閉 寫端將收到 SIGPIPE 信號 退出寫端進程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{// 創建一個無名管道int fd[2];// 創建無名管道pipe(fd);// 創建一個進程pid_t pid = fork();if (pid == 0) // 子進程 讀{// 寫端 無用 可刪除close(fd[1]);int i = 0;while (1){char buf[128] = "";int len = read(fd[0], buf, sizeof(buf));i++;printf("len=%d\n", len);if (i == 5)break;}// 通信完記得關閉讀端close(fd[0]);}else if (pid > 0) // 父進程 寫{// 讀端 無用 可刪除close(fd[0]);while (1){printf("父進程%u寫入數據\n", getpid());write(fd[1], "hello pipe", 10);sleep(1);}// 通信完記得關閉寫端close(fd[1]);wait(NULL); // 等待子進程結束}return 0;
}

綜合案例

要求:使用代碼實現ps -A | grep bush

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);int i = 0;for(i = 0; i < 2; i++){pid_t pid = fork();if(pid == 0){break;}}if(i == 0){close(fd[0]);dup2(fd[1],1);execl("/bin/ps","ps","-A",NULL);_exit(0);}else if(i == 1){close(fd[1]);dup2(fd[0],0);execl("/bin/grep","grep","bash",NULL);_exit(0);}else if(i == 2){while (1){int id = waitpid(-1,NULL,WNOHANG);if(id == -1){break;}}    }return 0;
}

有名管道

又名 : 命名管道 (FIFO)
特點 :
????????1、半雙工,數據在同一時刻只能在一個方向上流動。
????????2、寫入 FIFO 中的數據遵循先入先出的規則。
????????3、 FIFO 所傳送的數據是無格式的,這要求 FIFO 的讀出方與寫入方必須事先約 定好數據的格式,如多少字節算一個消息等。
????????4、 FIFO 在文件系統中作為一個特殊的文件而存在,但 FIFO 中的內容卻存放在 內存中。
????????5、管道在內存中對應一個緩沖區。不同的系統其大小不一定相同。
????????6、從 FIFO 讀數據是一次性操作,數據一旦被讀,它就從 FIFO 中被拋棄,釋放 空間以便寫更多的數據。
????????7、當使用 FIFO 的進程退出后, FIFO 文件將繼續保存在文件系統中以便以后使 用。
????????8、 FIFO 有名字,不相關的進程可以通過打開命名管道進行通信。

mkfifo函數

作用 : 創建有名管道
語法
????????#include <sys/types.h>
????????#include <sys/stat.h>
????????int mkfifo(const char *pathname, mode_t mode);
參數 :
????????pathname:文件名
????????mode:文件操作模式 , 一般用 0666( 所有用戶可讀可寫 )
返回值 :
????????成功:0
????????失敗:-1, 一般失敗是因為存在與 pathname 名相同的文件

讀寫特點

1open打開管道 不指定O_NONBLOCK (阻塞)

????????1、 open 以只讀方式打開 FIFO 時,要阻塞到某個進程為寫而打開此 FIFO
????????2、 open 以只寫方式打開 FIFO 時,要阻塞到某個進程為讀而打開此 FIFO
????????3、 open 以只讀、只寫方式打開 FIFO 時會阻塞,調用 read 函數從 FIFO 里讀數據 時 read 也會阻塞。
????????4、通信過程中若寫進程先退出了,則調用 read 函數從 FIFO 里讀數據時不阻塞;若 寫進程又重新運行,則調用 read 函數從 FIFO 里讀數據時又恢復阻塞。
????????5、通信過程中,讀進程退出后,寫進程向命名管道內寫數據時,寫進程也會(收到 SIGPIPE 信號)退出。
????????6、調用 write 函數向 FIFO 里寫數據,當緩沖區已滿時 write 也會阻塞。

2open打開管道 指定O_NONBLOCK (非阻塞)

????????1、先以只讀方式打開:如果沒有進程 , 已經為寫而打開一個 FIFO, 只讀 open 成功, 并且 open 不阻塞。
????????2、先以只寫方 式打開:如果沒有進程 , 已經為讀而打開一個 FIFO ,只寫 open 將出錯返回-1
????????3、 read write 讀寫命名管道中讀數據時不阻塞。
????????4、通信過程中,讀進程退出后, 寫進程向命名管道內寫數據時,寫進程也會(收到SIGPIPE 信號)退出。

3、 注意: open 函數以可讀可寫方式打開 FIFO 文件時的特點:

????????1、 open 不阻塞。
????????2、調用 read 函數從 FIFO 里讀數據時 read 會阻塞。
????????3、調用 write 函數向 FIFO 里寫數據 , 當緩沖區已滿時 write 也會阻塞

綜合案例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{// 1,創建有名管道,san發si接的管道mkfifo("sanToSi", 0666);// 2,創建有名管道,si發san接的管道mkfifo("siToSan", 0666);int i = 0;for (i = 0; i < 2; i++){// 創建進程int pid = fork();if (pid == 0){// pid==0說明是子進程進入的,子進程無需在創建進程break;}}if (i == 0){int fd;
// 子進程1發送消息
#ifdef USER1fd = open("siToSan", O_WRONLY);
#endif // DEBUG
#ifdef USER2fd = open("sanToSi", O_WRONLY);
#endif // DEBUGif (fd < 0){perror("打開發送管道失敗\n");_exit(-1);}while (1){char buf[128];fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;write(fd, buf, sizeof(buf));printf("my:%s\n", buf);if (strcmp(buf, "886") == 0){break;}}close(fd);_exit(-1);}else if (i == 1){// 子進程2,接收消息int fd = 0;
#ifdef USER1fd = open("sanToSi", O_RDONLY);
#endif // DEBUG
#ifdef USER2fd = open("siToSan", O_RDONLY);
#endif // DEBUGif (fd < 0){perror("打開接收管道失敗\n");_exit(-1);}while (1){char buf[128];int len = read(fd, buf, sizeof(buf));printf("讀取的字節數:%d\n", len);if (len > 0){printf("si:%s\n", buf);if (strcmp(buf, "886") == 0){break;}}}close(fd);_exit(-1);}else if (i == 2){printf("父進程%d正在執行\n", getpid());// 父進程while (1){pid_t id = waitpid(-1, NULL, WNOHANG);if (id > 0){printf("子進程%d被回收了\n", id);}else if (id == 0){continue;}else if (id < 0){break;}}}return 0;
}
// 命令代碼編譯gcc 文件名 -o 生成可執行文件名 -D USERX
//-D 相當于定義一個宏

總結

無名管道與有名管道的使用場景
????????1,無名管道應用與有血緣關系的進程中
????????2,有名管道應用與沒有血緣關系的進程中
無名管道與有名管道的區別
????????1,無名管道基于內存 , 無需文件管理系統
????????2,有名管道基于文件和內存 , 需要文件管理系統
dup2
????????作用: 復制文件描述
????????意義: 可以實現文件的重定向

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207536.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207536.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207536.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java--作用域,構造器,this

作用域基本使用 在Java編程中&#xff0c;主要的變量就是屬性&#xff08;成員變量&#xff09;和局部變量。 我們說的局部變量一般是指在成員方法中定義的變量 Java中作用域的分類 全局變量&#xff1a;也就是屬性&#xff0c;作用域為整個類體 局部變量&#xff1a;也就是除了…

RHEL8_Linux訪問NFS存儲及自動掛載

本章主要介紹NFS客戶端的使用 創建FNS服務器并通過NFS共享一個目錄在客戶端上訪問NFS共享的目錄自動掛載的配置和使用 1.訪問NFS存儲 前面介紹了本地存儲&#xff0c;本章就來介紹如何使用網絡上的存儲設備。NFS即網絡文件系統&#xff0c;所實現的是 Linux 和 Linux 之間的共…

新手搭建知識付費平臺必備攻略:如何以低成本實現高轉化?

我有才知識付費平臺 一、引言 隨著知識經濟的崛起&#xff0c;越來越多的知識提供者希望搭建自己的知識付費平臺。然而&#xff0c;對于新手來說&#xff0c;如何以低成本、高效率地實現這一目標&#xff0c;同時滿足自身需求并提高客戶轉化率&#xff0c;是一大挑戰。本文將…

SPA, SEO, SSR總結

SPA單頁面Web應用 SPA(Single page web application) 單頁面Web應用 Web不再是一張張頁面,而是一個整體的應用,一個由路由系統,數據系統,頁面(組件)系統等等,組成的應用程序, 讓用戶不需要每次與服務器進行頁面刷新來獲得新的內容, 從而提供了更快,跟流暢的用戶體驗, 在SPA中…

參與創作①周年啦~

寫在前面 今天看了消息才知道&#xff0c;原來開始創作已經一年了。此篇無干貨&#xff0c;純白話&#xff0c;純記錄。 機緣 參與CSDN創作已經一年有余&#xff0c;猶記得第一篇博文是為了整理好所學內容&#xff0c;方便自己復習。沒想到后面也陸陸續續發了些其他內容&…

關于read函數阻塞的問題

關于read函數阻塞的問題 上一篇文章IO多路轉接之select 末尾提到了一點&#xff0c;服務端讀取每次是讀取10個字節的&#xff0c;如果超過10個字節&#xff0c;需要讀取多次&#xff0c;但是客戶端只會read一次&#xff0c;第二次read的時候&#xff0c;直接阻塞了。 那么如何…

Windows server flask

1、Windows server 通過python的flask執行命令 from flask import Flask, request, abort import subprocess from flask_basicauth import BasicAuth app Flask(__name__) # 獲取url是進行賬號密碼認證&#xff0c;設置url的賬號密碼 app.config[BASIC_AUTH_USERNAME] 賬號…

12.8作業

1.頭文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QMovie>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nul…

spring-boot-starter-validation是什么Validation參數校驗使用概要

spring-boot-starter-validation是什么&Validation參數校驗使用概要 來源Valid和Validated的用法(區別)引入依賴Valid和Validated的用法 在日常的項目開發中&#xff0c;為了防止非法參數對業務造成的影響&#xff0c;需要對接口的參數做合法性校驗&#xff0c;例如在創建用…

基于Docker安裝Mysql:5.5

一、拉取鏡像 sudo docker pull mysql:5.5二、啟動mysql鏡像 1. 創建MySQL的conf目錄和data目錄 mkdir -p /home/docker/mysql/conf /home/docker/mysql/data2. 利用鏡像創建容器 sudo docker run --restartalways -d --name mysql -v /home/docker/mysql/conf/my.cnf:/etc…

系統設計-微服務架構

典型的微服務架構圖 下圖展示了一個典型的微服務架構。 負載均衡器&#xff1a;它將傳入流量分配到多個后端服務。CDN&#xff08;內容交付網絡&#xff09;&#xff1a;CDN 是一組地理上分布的服務器&#xff0c;用于保存靜態內容以實現更快的交付。客戶端首先在 CDN 中查找內…

methods

類型&#xff1a;{ [key: string]: Function } 詳細&#xff1a; methods 將被混入到 Vue 實例中。可以直接通過 VM 實例訪問這些方法&#xff0c;或者在指令表達式中使用。方法中的 this 自動綁定為 Vue 實例。 注意&#xff0c;不應該使用箭頭函數來定義 method 函數 (例如…

臨床骨科常用的肩關節疾病量表,醫生必備!

根據骨科醫生的量表使用情況&#xff0c;常笑醫學整理了臨床骨科常用的肩關節疾病量表&#xff0c;為大家分享臨床常見的肩關節疾病量表評估內容&#xff0c;均支持量表下載和在線使用&#xff0c;建議收藏&#xff01; 1.臂、肩、手功能障礙&#xff08;disabilites of the ar…

useradd 在Linux原生應用開發過程中的簡單應用

useradd命令是用于在Linux系統中創建新用戶的命令。它可以創建一個新用戶&#xff0c;并設置該用戶的屬性、家目錄、默認shell等。useradd命令實際上是一個包裝了一系列系統調用的高級命令。 在Linux系統中&#xff0c;用戶信息存儲在/etc/passwd文件中。當執行useradd命令時&…

flstudio21破解漢化版2024最新水果編曲使用教程

? 如果你一直夢想制作自己的音樂(無論是作為一名制作人還是藝術家)&#xff0c;你可能會想你出生在這個時代是你的幸運星。這個水果圈工作室和上一版之間的改進水平確實令人欽佩。這僅僅是FL Studio 21所提供的皮毛。你的音樂項目的選擇真的會讓你大吃一驚。你以前從未有過這…

ChatGPT的常識

什么是ChatGPT&#xff1f; ChatGPT是一個基于GPT模型的聊天機器人&#xff0c;GPT即“Generative Pre-training Transformer”&#xff0c;是一種預訓練的語言模型。ChatGPT使用GPT-2和GPT-3兩種模型來生成自然語言響應&#xff0c;從而與人類進行真實的對話。 ChatGPT的設計…

2023年全球軟件開發大會(QCon廣州站2023)-核心PPT資料下載

一、峰會簡介 本次峰會包含&#xff1a;泛娛樂時代的邊緣計算與通訊、穩定性即生命線、下一代軟件架構、出海的思考、現代數據架構、AGI 與 AIGC 落地、大前端技術探索、編程語言實戰、DevOps vs 平臺工程、新型數據庫、AIGC 浪潮下的企業出海、AIGC 浪潮下的效能智能化、數據…

【池式組件】線程池的原理與實現

線程池的原理與實現 線程池簡介1.線程池1.線程池2.數量固定的原因3.線程數量如何確定4.為什么需要線程池5.線程池結構 線程池的實現數據結構設計1.任務結構2.任務隊列結構3.線程池結構 接口設計 線程池的應用reactorredis 中線程池skynet 中線程池 線程池簡介 1.線程池 1.線程…

第7課 SQL入門之創建計算字段

文章目錄 7.1 計算字段7.2 拼接字段使用別名 7.3 執行算術計算 這一課介紹什么是計算字段&#xff0c;如何創建計算字段&#xff0c;以及如何從應用程序中使用別名引用它們。 7.1 計算字段 存儲在數據庫表中的數據一般不是應用程序所需要的格式&#xff0c;下面舉幾個例子。 …

前端Excel導出實用方案(完整源碼,可直接應用)

目錄 前言&#xff1a; 技術選型&#xff1a; 主要功能點&#xff1a; 核心代碼&#xff1a; 完整代碼&#xff1a; 開發文檔 前言&#xff1a; 在前后端分離開發為主流的時代&#xff0c;很多時候&#xff0c;excel導出已不再由后端主導&#xff0c;而是把導出的操作移…