日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux -- read(), write()

發(fā)布時(shí)間:2023/12/19 linux 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux -- read(), write() 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

read()函數(shù)??

2011-03-23 16:28:37|??分類(lèi):?linux?|??標(biāo)簽:?|字號(hào)?訂閱


read
函數(shù)從打開(kāi)的設(shè)備或文件中讀取數(shù)據(jù)。

#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); 返回值:成功返回讀取的字節(jié)數(shù),出錯(cuò)返回-1并設(shè)置errno,如果在調(diào)read之前已到達(dá)文件末尾,則這次read返回0 參數(shù) count 是請(qǐng)求讀取的字節(jié)數(shù),讀上來(lái)的數(shù)據(jù)保存在緩沖區(qū)buf中,同時(shí)文件的當(dāng)前讀寫(xiě)位置向后移。注意這個(gè)讀寫(xiě)位置和使用C標(biāo)準(zhǔn)I/O庫(kù)時(shí)的讀寫(xiě)位置有可能不同,這個(gè)讀寫(xiě)位置是記在內(nèi)核中的,而使用C標(biāo)準(zhǔn)I/O庫(kù)時(shí)的讀寫(xiě)位置是用戶(hù)空間I/O緩沖區(qū)中的位置。比如用fgetc讀一個(gè)字節(jié),fgetc有可能從內(nèi)核中預(yù)讀1024個(gè)字節(jié)到I/O緩沖區(qū)中,再返回第一個(gè)字節(jié),這時(shí)該文件在內(nèi)核中記錄的讀寫(xiě)位置是1024,而在FILE結(jié)構(gòu)體中記錄的讀寫(xiě)位置是1。注意返回值類(lèi)型是ssize_t,表示有符號(hào)的size_t,這樣既可以返回正的字節(jié)數(shù)、0(表示到達(dá)文件末尾)也可以返回負(fù)值-1(表示出錯(cuò))。 read函數(shù)返回時(shí),返回值說(shuō)明了buf中前多少個(gè)字節(jié)是剛讀上來(lái)的。有些情況下,實(shí)際讀到的字節(jié)數(shù)(返回值)會(huì)小于請(qǐng)求讀的字節(jié)數(shù)count,例如:讀常規(guī)文件時(shí),在讀到count個(gè)字節(jié)之前已到達(dá)文件末尾。例如,距文件末尾還有30個(gè)字節(jié)而請(qǐng)求讀100個(gè)字節(jié),則read返回30,下次read將返回0。

從終端設(shè)備讀,通常以行為單位,讀到換行符就返回了。

從網(wǎng)絡(luò)讀,根據(jù)不同的傳輸層協(xié)議和內(nèi)核緩存機(jī)制,返回值可能小于請(qǐng)求的字節(jié)數(shù),后面socket編程部分會(huì)詳細(xì)講解。


write
函數(shù)向打開(kāi)的設(shè)備或文件中寫(xiě)數(shù)據(jù)。

#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); 返回值:成功返回寫(xiě)入的字節(jié)數(shù),出錯(cuò)返回-1并設(shè)置errno寫(xiě)常規(guī)文件時(shí),write的返回值通常等于請(qǐng)求寫(xiě)的字節(jié)數(shù) count,而向終端設(shè)備或網(wǎng)絡(luò)寫(xiě)則不一定。

讀常規(guī)文件是不會(huì)阻塞的,不管讀多少字節(jié),read一定會(huì)在有限的時(shí)間內(nèi)返回。從終端設(shè)備或網(wǎng)絡(luò)讀則不一定,如果從終端輸入的數(shù)據(jù)沒(méi)有換行符,調(diào)用read讀終端設(shè)備就會(huì)阻塞,如果網(wǎng)絡(luò)上沒(méi)有接收到數(shù)據(jù)包,調(diào)用read從網(wǎng)絡(luò)讀就會(huì)阻塞,至于會(huì)阻塞多長(zhǎng)時(shí)間也是不確定的,如果一直沒(méi)有數(shù)據(jù)到達(dá)就一直阻塞在那里。同樣,寫(xiě)常規(guī)文件是不會(huì)阻塞的,而向終端設(shè)備或網(wǎng)絡(luò)寫(xiě)則不一定。

現(xiàn)在明確一下阻塞(Block)這個(gè)概念。當(dāng)進(jìn)程調(diào)用一個(gè)阻塞的系統(tǒng)函數(shù)時(shí),該進(jìn)程被置于睡眠(Sleep)狀態(tài),這時(shí)內(nèi)核調(diào)度其它進(jìn)程運(yùn)行,直到該進(jìn)程等待的事件發(fā)生了(比如網(wǎng)絡(luò)上接收到數(shù)據(jù)包,或者調(diào)用sleep指定的睡眠時(shí)間到了)它才有可能繼續(xù)運(yùn)行。與睡眠狀態(tài)相對(duì)的是運(yùn)行(Running)狀態(tài),在Linux內(nèi)核中,處于運(yùn)行狀態(tài)的進(jìn)程分為兩種情況:


正在被調(diào)度執(zhí)行。CPU處于該進(jìn)程的上下文環(huán)境中,程序計(jì)數(shù)器(eip)里保存著該進(jìn)程的指令地址,通用寄存器里保存著該進(jìn)程運(yùn)算過(guò)程的中間結(jié)果,正在執(zhí)行該進(jìn)程的指令,正在讀寫(xiě)該進(jìn)程的地址空間。


就緒狀態(tài)。該進(jìn)程不需要等待什么事件發(fā)生,隨時(shí)都可以執(zhí)行,但CPU暫時(shí)還在執(zhí)行另一個(gè)進(jìn)程,所以該進(jìn)程在一個(gè)就緒隊(duì)列中等待被內(nèi)核調(diào)度。系統(tǒng)中可能同時(shí)有多個(gè)就緒的進(jìn)程,那么該調(diào)度誰(shuí)執(zhí)行呢?內(nèi)核的調(diào)度算法是基于優(yōu)先級(jí)和時(shí)間片的,而且會(huì)根據(jù)每個(gè)進(jìn)程的運(yùn)行情況動(dòng)態(tài)調(diào)整它的優(yōu)先級(jí)和時(shí)間片,讓每個(gè)進(jìn)程都能比較公平地得到機(jī)會(huì)執(zhí)行,同時(shí)要兼顧用戶(hù)體驗(yàn),不能讓和用戶(hù)交互的進(jìn)程響應(yīng)太慢。


下面這個(gè)小程序從終端讀數(shù)據(jù)再寫(xiě)回終端。


例 28.2. 阻塞讀終端


#include <unistd.h> #include <stdlib.h> int main(void) { char buf[10]; int n; n = read(STDIN_FILENO, buf, 10); if (n < 0) { perror("read STDIN_FILENO"); exit(1); } write(STDOUT_FILENO, buf, n); return 0; }

執(zhí)行結(jié)果如下:

$ ./a.out hello(回車(chē)) hello $ ./a.out hello world(回車(chē)) hello worl$ d bash: d: command not found

第一次執(zhí)行a.out的結(jié)果很正常,而第二次執(zhí)行的過(guò)程有點(diǎn)特殊,現(xiàn)在分析一下:


Shell進(jìn)程創(chuàng)建a.out進(jìn)程,a.out進(jìn)程開(kāi)始執(zhí)行,而Shell進(jìn)程睡眠等待a.out進(jìn)程退出。



a.out調(diào)用read時(shí)睡眠等待,直到終端設(shè)備輸入了換行符才從read返回,read只讀走10個(gè)字符,剩下的字符仍然保存在內(nèi)核的終端設(shè)備輸入緩沖區(qū)中。


a.out
進(jìn)程打印并退出,這時(shí)Shell進(jìn)程恢復(fù)運(yùn)行,Shell繼續(xù)從終端讀取用戶(hù)輸入的命令,于是讀走了終端設(shè)備輸入緩沖區(qū)中剩下的字符d和換行符,把它當(dāng)成一條命令解釋執(zhí)行,結(jié)果發(fā)現(xiàn)執(zhí)行不了,沒(méi)有d這個(gè)命令。


如果在open一個(gè)設(shè)備時(shí)指定了O_NONBLOCK標(biāo)志,read/write就不會(huì)阻塞。以read為例,如果設(shè)備暫時(shí)沒(méi)有數(shù)據(jù)可讀就返回-1,同時(shí)置errno為EWOULDBLOCK(或者EAGAIN,這兩個(gè)宏定義的值相同),表示本來(lái)應(yīng)該阻塞在這里(would block,虛擬語(yǔ)氣),事實(shí)上并沒(méi)有阻塞而是直接返回錯(cuò)誤,調(diào)用者應(yīng)該試著再讀一次(again)。這種行為方式稱(chēng)為輪詢(xún)(Poll),調(diào)用者只是查詢(xún)一下,而不是阻塞在這里死等,這樣可以同時(shí)監(jiān)視多個(gè)設(shè)備:

while(1) {
非阻塞read(設(shè)備1);
if(設(shè)備1有數(shù)據(jù)到達(dá))
? 處理數(shù)據(jù);
非阻塞read(設(shè)備2);
if(設(shè)備2有數(shù)據(jù)到達(dá))
? 處理數(shù)據(jù);
...
}

如果
read(設(shè)備1)
是阻塞的,那么只要設(shè)備1沒(méi)有數(shù)據(jù)到達(dá)就會(huì)一直阻塞在設(shè)備1的
read
調(diào)用上,即使設(shè)備2有數(shù)據(jù)到達(dá)也不能處理,使用非阻塞I/O就可以避免設(shè)備2得不到及時(shí)處理。

非阻塞I/O有一個(gè)缺點(diǎn),如果所有設(shè)備都一直沒(méi)有數(shù)據(jù)到達(dá),調(diào)用者需要反復(fù)查詢(xún)做無(wú)用功,如果阻塞在那里,操作系統(tǒng)可以調(diào)度別的進(jìn)程執(zhí)行,就不會(huì)做無(wú)用功了。在使用非阻塞I/O時(shí),通常不會(huì)在一個(gè)while循環(huán)中一直不停地查詢(xún)(這稱(chēng)為T(mén)ight Loop),而是每延遲等待一會(huì)兒來(lái)查詢(xún)一下,以免做太多無(wú)用功,在延遲等待的時(shí)候可以調(diào)度其它進(jìn)程執(zhí)行。

while(1) { 非阻塞read(設(shè)備1); if(設(shè)備1有數(shù)據(jù)到達(dá)) 處理數(shù)據(jù); 非阻塞read(設(shè)備2); if(設(shè)備2有數(shù)據(jù)到達(dá)) 處理數(shù)據(jù); ... sleep(n); }

這樣做的問(wèn)題是,設(shè)備1有數(shù)據(jù)到達(dá)時(shí)可能不能及時(shí)處理,最長(zhǎng)需延遲n秒才能處理,而且反復(fù)查詢(xún)還是做了很多無(wú)用功。以后要學(xué)習(xí)的select(2)函數(shù)可以阻塞地同時(shí)監(jiān)視多個(gè)設(shè)備,還可以設(shè)定阻塞等待的超時(shí)時(shí)間,從而圓滿(mǎn)地解決了這個(gè)問(wèn)題。

以下是一個(gè)非阻塞I/O的例子。目前我們學(xué)過(guò)的可能引起阻塞的設(shè)備只有終端,所以我們用終端來(lái)做這個(gè)實(shí)驗(yàn)。程序開(kāi)始執(zhí)行時(shí)在0、1、2文件描述符上自動(dòng)打開(kāi)的文件就是終端,但是沒(méi)有O_NONBLOCK標(biāo)志。所以就像例 28.2 “阻塞讀終端”一樣,讀標(biāo)準(zhǔn)輸入是阻塞的。我們可以重新打開(kāi)一遍設(shè)備文件/dev/tty(表示當(dāng)前終端),在打開(kāi)時(shí)指定
O_NONBLOCK標(biāo)志。


例 28.3. 非阻塞讀終端


#include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <stdlib.h> #define MSG_TRY "try again\n" int main(void) { char buf[10]; int fd, n; fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); if(fd<0) { perror("open /dev/tty"); exit(1); } tryagain: n = read(fd, buf, 10); if (n < 0) { if (errno == EAGAIN) { sleep(1); write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY)); goto tryagain; } perror("read /dev/tty"); exit(1); } write(STDOUT_FILENO, buf, n); close(fd); return 0; }

以下是用非阻塞I/O實(shí)現(xiàn)等待超時(shí)的例子。既保證了超時(shí)退出的邏輯又保證了有數(shù)據(jù)到達(dá)時(shí)處理延遲較小。


例 28.4. 非阻塞讀終端和等待超時(shí)


#include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <stdlib.h> #define MSG_TRY "try again\n" #define MSG_TIMEOUT "timeout\n" int main(void) { char buf[10]; int fd, n, i; fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); if(fd<0) { perror("open /dev/tty"); exit(1); } for(i=0; i<5; i++) { n = read(fd, buf, 10); if(n>=0) break; if(errno!=EAGAIN) { perror("read /dev/tty"); exit(1); } sleep(1); write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY)); } if(i==5) write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT)); else write(STDOUT_FILENO, buf, n); close(fd); return 0; }

轉(zhuǎn)載于:https://www.cnblogs.com/tianzhiyi/p/5430575.html

總結(jié)

以上是生活随笔為你收集整理的linux -- read(), write()的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。