LInux多程序開發II
阿新 • • 發佈:2021-08-09
1.程序間通訊。
注意:實現管道通訊,一般只會有一個方向的通訊,從程序a傳資料到程序b,或者從程序b傳資料到程序a。因為如果程序a,b互相傳資料的話,可能會造成後果:a寫入管道的資料又被a自己讀去了,b寫入管道的資料也可能被b程序讀去了。所以在實現管道時,往管道寫入資料的程序會關閉讀端close(pipefd[0]);而往管道讀資料的程序會關閉管道寫端close(pipefd[1])。
5.管道的讀寫特點:使用管道時,有以下幾種特殊情況(假設都是阻塞IO操作)
- 程序是獨立的資源分配單元,不同程序之間的資源是獨立的,沒有關聯,不能在一個程序中直接訪問另一個程序的資源。
- 程序之間可以進行資訊的互動和狀態的傳遞,稱為程序間通訊(IPC:Inter Processes Communication)。
- 程序間通訊:資料傳輸、通知時間、資源共享、程序控制。
- 同一主機程序通訊。匿名管道、有名管道、訊號、訊息佇列、共享記憶體、訊號量。
- 不同主機(網路)通訊。Socket。
- Unix系統程序間通訊。
- 管道是在核心記憶體中維護的緩衝區,不同的作業系統大小不同。
- 管道擁有檔案的特質:讀寫操作。匿名管道沒有檔案實體;有名管道有檔案實體,但不儲存資料。管道的兩端可以分別用兩個檔案描述符表示。可以用操作檔案的方式對管道進行操作。
- 一個管道是一個位元組流,使用管道不存在訊息或訊息邊界的概念,從管道讀取資料的程序可以讀取任意大小的資料塊,而不管寫入程序寫入管道的資料塊大小是多少。
- 通過管道傳遞的資料是順序的,從管道讀取出來的位元組的順序和被寫入管道的順序完全一致。
- 管道的資料傳遞方向是單向的,一端用於寫入,一端用於讀取,管道是半雙工的。
- 從管道讀資料是一次性操作,資料一旦被讀走就被從管道中拋棄並釋放空間可以寫入更多的資料。在管道中無法使用lseek()來隨機訪問資料。
- 匿名管道只能在具有公共祖先的程序之間使用。子程序通過fork()創建出來與父程序共享檔案描述符,因此可以根據相同的檔案描述符進行通訊。
- 實現:迴圈陣列。由兩個檔案描述符標誌分別讀寫位置。
#include <unistd.h> int pipe(int pipedfd[2]); - 作用:建立一個匿名管道,用來程序間通訊。 - pipefd[2]: 傳出引數。pipefd[0],對應管道的讀端;pipefd[1],對應管道的寫端。 - 返回值:成功返回0,失敗返回1。 注意: 1.匿名管道只用於具有關係的程序之間的通訊。 2.管道預設是阻塞的,如果管道中沒有資料,read阻塞;如果管道滿了,write阻塞。 # 檢視管道緩衝大小命令 $ ulimit -a # 檢視管道緩衝大小函式 #include <unistd.h> long fpathconf(int fd, int name); long size = fpathcong(pipefd[0], _PC_PIPE_BUF);
- 所有指向管道寫端的檔案描述符都關閉了(管道寫端引用計數為0),有程序從管道的讀端讀取資料,那麼管道中剩餘的資料被讀取之後,再次read會返回0,就像讀到檔案末尾一樣。
- 如果有指向管道寫端的檔案描述符沒有關閉(管道寫端引用計數大於0),而持有管道寫端的程序沒有往管道中寫資料,這個時候若有程序從管道中讀取資料,那麼管道中剩餘的資料被讀取之後,再次read會阻塞,直到管道中有資料可以讀了才讀取資料並返回。
- 所有指向管道讀端的檔案描述符都關閉了(管道讀端引用計數為0),這個時候有程序往管道中寫資料,那麼該程序會受到一個訊號SIGPIPE,通常會導致程序終止。
- 如果有指向管道讀端的檔案描述符沒有關閉(管道讀端引用技術大於0),而持有管道讀端的程序也沒有從管道讀資料,這時有程序向管道寫資料,那麼管道在被寫滿的時候再次write阻塞,直到管道中有空位置能再次寫入資料並返回。
int flags = fcntl(fd[0], F_GETFL); # 獲取原來的flag flags |= O_NONBLOCK; # 修改flag的值 fcntl(fd[0], F_SETFL, flags); # 設定新的flag7.有名管道(FIFO)。
- 有名管道提供了一個路徑名與之關聯(即:有一個實體檔案),以FIFO的檔案形式存在與檔案系統中。其開啟方式與開啟普通檔案一樣,因此通過訪問該路徑即可通過FIFO通訊。
- 與匿名管道的區別:1. FIFO有檔案實體,但是其內容存放於記憶體中;2.當使用FIFO的程序退出後,FIFO檔案繼續儲存在檔案系統中之後可以繼續使用;3.FIFO有名字,不相關的程序可以通過開啟有名管道進行通訊。
# 建立管道命令 mkfifo 名字 # 通過函式建立,建立FIFO之後檔案IO函式(open、read、write、close)都可用於FIFO #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); - pathname: 管道名稱的路徑 - mode: 檔案的許可權 - 返回值: 成功返回0,失敗返回-1並設定errno注意: 1.一個為開啟管道的只讀程序會阻塞,直到另一個只寫程序開啟管道; 2.一個為開啟管道的只寫程序會阻塞,直到另一個只讀程序開啟管道; 讀管道: 管道中有資料:read返回實際讀到的位元組數; 管道中無資料:a.管道寫端被全部關閉,read返回0;b.寫端沒有全部被關閉,read阻塞等待 寫管道: 管道讀端被全部關閉,程序異常終止(收到一個SIGPIPE訊號); 管道讀端沒有全部關閉:a.管道已滿,write阻塞;b.管道沒滿,write寫入資料,並返回實際寫入的位元組數 9.記憶體對映。將磁碟檔案的資料對映到記憶體中,程式可以通過修改記憶體就能修改磁碟檔案。
- 同一個檔案可以對映到不同程序的虛擬記憶體空間,因此某個程序修改記憶體後,系統會同步檔案的修改,則另一個將記憶體對映到檔案的程序即可收到訊息。
- 可以實現程序間通訊:1.有關係的程序(父子程序);2.沒有關係的程序間通訊。
- 記憶體對映區通訊是非阻塞的。
#include <sys/mman.h> void mmap(coid *addr, size_t length, int prot, int flags, int fd, off_t offset); - 作用:將一個檔案或者裝置的資料對映到記憶體中 - addr: NULL,由核心指定 - length: 要對映的資料的長度,這個值不能為0,建議使用檔案的長度,使用stat或者lseek獲取檔案的長度 - prot: 對申請的記憶體對映區的操作許可權,PROT_EXEC 可執行許可權;PROT_READ 讀許可權;PROT_WRITE 寫許可權;PROT_NONE 沒有許可權 - flags: MAP_SHARED 對映區的資料會自動和磁碟檔案進行同步,程序間通訊,必須設定這個選項;MAP_PRIVATE 不同步,記憶體對映區的資料改變了,對原檔案不會修改,會建立一個新的檔案(copy on write) - fd: 需要對映的檔案的檔案描述符,通過open()得到,檔案大小要>0 - offset: 偏移量,4k的整數倍,0表示不偏移 - 返回值:成功返回建立的記憶體的首地址,失敗返回MAP_FAILED ((void *) -1) int munmap(void *addr, size_t length); - 作用:釋放記憶體對映 - addr: 要釋放的記憶體地址 - length: 要釋放的記憶體的大小,和mmap引數的length一樣
10.使用記憶體對映實現程序間通訊:
- 有關係的程序(父子程序)
- 沒有關係的程序間通訊
- 如果對mmap的返回值(ptr)做++操作(ptr++),munmap是否能成功?
void *ptr = mmap(...); ptr++; // 可以操作 munmap(ptr, len); // 錯誤,應該儲存使用之前的地址
- 如果open時O_RDONLY,mmap時prot引數指定PROT_READ|PROT_WRITE會怎樣?
錯誤,返回MAP_FAILD open()函式的許可權要大於等於prot的許可權。 e.g.: open()讀寫,prot可以讀寫/只讀/只寫 open()只讀,prot只能只讀
- 如果檔案偏移量是1000會怎樣?
返回錯誤MAP_FAILED,偏移量只能是4k的整數倍
- mmap什麼情況下會呼叫失敗?
1. 第二個該引數length = 0 2. 第三個引數 prot 只有寫許可權 3. 第三個引數有 PROT_READ | PROT_WRITE 許可權,而第五個引數fd通過open開啟時的許可權是O_RDONLY / O_WRONLY(記憶體許可權大於檔案許可權)
- 可以open的時候O_CREAT一個新檔案來建立對映區嗎?
可以,但是建立的檔案大小為0不行,需要對新檔案進行擴充套件( lseek() / truncate() )
- mmap後關閉檔案描述符,對mmap對映有沒有影響?
int fd = open("xxx"); mmap(,,,,fd,0); close(fd); 對映區還存在,建立營社群的fd被關閉,沒有影響。
- 對ptr越界操作會怎麼樣?
void *ptr = mmap(NULL,100,,,,); 越界操作操作的是非法記憶體,造成段錯誤12.匿名對映:不需要檔案實體進行一個記憶體對映。只能用於父子程序間通訊。
mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARD | MAP_ANONYMOUS, -1, 0); 檔案描述符 fd = -1 prot 要指定為 PROT_ANONYMOUS