如何理解io?
IO指的是計算機系統(tǒng)中輸入(input)和輸出(Output)的過程,以計算機本身為參照物,是計算機與外部設(shè)備進行數(shù)據(jù)交互的機制。
輸入(Input)是向計算機發(fā)送數(shù)據(jù)的過程,而輸出(Output)是從計算機向外部設(shè)備發(fā)送數(shù)據(jù)的過程。
IO操作主要包括等待和拷貝兩個步驟:
IO = 等待 + 拷貝(等待是主要矛盾)
在等待外部設(shè)備準備好數(shù)據(jù)后,CPU通過針腳發(fā)送中斷信號通知操作系統(tǒng)。操作系統(tǒng)進入內(nèi)核態(tài),進行數(shù)據(jù)拷貝。
因此,IO操作基本可以概括為等待和拷貝兩個動作。
高效IO
在IO操作中,時間主要消耗在等待上,因為拷貝的時間相對于等待來說要短得多。
高效的IO操作意味著減少等待時間,從而增加拷貝操作在整個過程中的比重。
第一種IO模型:阻塞IO模型
阻塞IO模型的特點是在系統(tǒng)調(diào)用時,如果外部設(shè)備未準備好數(shù)據(jù),進程會一直等待數(shù)據(jù)準備就緒。一旦數(shù)據(jù)準備好,數(shù)據(jù)就會從內(nèi)核空間拷貝到用戶空間,并返回成功標識符。
ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
第二種IO模型:非阻塞IO模型
非阻塞IO模型與阻塞IO模型的最大區(qū)別在于,當沒有數(shù)據(jù)可用時,非阻塞IO會立即返回,并返回EWOULDBLOCK錯誤碼。
#include <iostream> #include <fcntl.h> #include <unistd.h> using namespace std; void SetNoBlock(int fd){ int info=fcntl(fd,F_GETFL); if(info
第三種IO模型:信號驅(qū)動IO模型
在信號驅(qū)動IO模型中,當內(nèi)核準備好數(shù)據(jù)時,會使用SIGIO信號通知應(yīng)用程序進行IO操作。
與非阻塞IO不同的是,信號驅(qū)動IO不會循環(huán)檢測數(shù)據(jù)是否準備好,而是當數(shù)據(jù)真正可用時發(fā)送SIGIO信號,確保IO操作成功。
第四種IO模型:IO多路轉(zhuǎn)接模型
IO多路轉(zhuǎn)接模型包括select、poll和epoll等方法。通過允許單個線程同時監(jiān)控多個文件描述符的I/O狀態(tài),可以避免為每個I/O操作創(chuàng)建單獨線程,從而減少資源消耗和上下文切換開銷。
雖然圖示上看起來與阻塞IO模型類似,但IO多路轉(zhuǎn)接的關(guān)鍵在于可以一次性等待多個文件的狀態(tài)變化(一切皆文件)。
第五種IO模型:異步IO模型
前四種模型都是同步IO,因為它們參與了等待或拷貝的過程。阻塞IO參與了等待和拷貝,非阻塞IO至少參與了拷貝,IO多路轉(zhuǎn)接可以管理多個文件并設(shè)置為非阻塞。
異步IO則直接返回,等待和拷貝的過程都由系統(tǒng)完成。處理完成后,系統(tǒng)通過事件、信號等方式通知應(yīng)用程序。
同步通信和異步通信
同步通信需要參與等待和拷貝的過程,參與其中任何一個步驟即為同步。
異步通信雖然也需要等待和拷貝,但這兩個過程完全由系統(tǒng)完成,調(diào)用者無需關(guān)心這兩個過程。系統(tǒng)處理完畢后會通知調(diào)用者。
需要注意的是,線程同步和線程互斥與此概念不同。它們是為了保護臨界資源,確保資源的一致性。
阻塞和非阻塞
阻塞調(diào)用會在調(diào)用成功之前掛起線程,直到得到結(jié)果后才返回。
非阻塞調(diào)用會立即返回,不會阻塞線程。
fcntl函數(shù)
文件描述符默認是阻塞的。
函數(shù)原型
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
cmd是命令,指定操作類型。主要操作類型包括:
- 獲取、設(shè)置文件狀態(tài)信息:cmd=F_GETFL,F(xiàn)_SETFL。
- 復(fù)制現(xiàn)有描述符:cmd=F_DUPFD。
- 獲取、設(shè)置文件描述符標志:cmd=F_GETFD,F(xiàn)_SETFD。
- 獲取、設(shè)置異步IO所有權(quán):cmd=F_GETOWN,F(xiàn)_SETOWN。
- 獲取、設(shè)置記錄鎖:cmd=F_GETLK,F(xiàn)_SETLK,F(xiàn)_SETLKW。
cmd | 功能 |
---|---|
F_GETFL,F(xiàn)_SETFL | 獲取、設(shè)置文件狀態(tài)信息 |
F_DUPFD | 復(fù)制現(xiàn)有描述符 |
F_GETFD,F(xiàn)_SETFD | 獲取、設(shè)置文件描述符標志 |
F_GETOWN,F(xiàn)_SETOWN | 獲取、設(shè)置異步IO所有權(quán) |
F_GETLK,F(xiàn)_SETLK,F(xiàn)_SETLKW | 獲取、設(shè)置記錄鎖 |
根據(jù)cmd的不同,可能需要傳遞額外的參數(shù)。例如,設(shè)置類的操作需要加入文件狀態(tài)信息,如O_NONBLOCK用于設(shè)置非阻塞模式。
F_DUPFD功能
在F_DUPFD中,返回新的文件描述符,可以設(shè)置新描述符的最小值。如果該值已被占用,則生成一個比該值更大的描述符。
#include <iostream> #include <unistd.h> #include <fcntl.h> using namespace std; int main() { int fd = open("example.txt", O_RDONLY|O_CREAT); if (fd == -1) { perror("open"); return 1; } int new_fd = fcntl(fd, F_DUPFD, 20); // 復(fù)制文件描述符,新描述符值大于等于10 if (new_fd == -1) { perror("fcntl"); close(fd); return 1; } printf("Original fd: %d, New fd: %dn", fd, new_fd); cout
F_GETFL和F_SETFL功能
使用F_GETFL和F_SETFL命令可以獲取和設(shè)置文件狀態(tài)標志。文件狀態(tài)標志包括O_APPEND、O_NONBLOCK等。
非阻塞:O_NONBLOCK。
#include <iostream> #include <fcntl.h> #include <unistd.h> using namespace std; void SetNoBlock(int fd){ int info=fcntl(fd,F_GETFL); if(info
結(jié)束語
至此,我們已經(jīng)深入探討了計算機科學(xué)中的諸多重要概念,包括I/O操作、線程同步與互斥、文件描述符管理等。理解這些概念對于編寫高效、可靠的程序至關(guān)重要。希望本文能夠為您的學(xué)習(xí)和實踐提供有益的參考。如果您在實際編程中遇到任何問題,建議查閱相關(guān)文檔或進一步研究具體案例。感謝您的閱讀,祝您在編程的道路上不斷進步!