Linux系统函数read()/write()/pread()/pwrite()的区别
轉(zhuǎn)載地址:https://blog.csdn.net/u013525455/article/details/52661313
在Linux和UNIX中有很多的輸入輸出函數(shù),有時(shí)真是讓想跟它攀點(diǎn)關(guān)系的菜鳥們束手無策。先來看看都有哪些函數(shù),通過解析與總結(jié),看看能不能讓大家能這些函數(shù)有個(gè)理性的認(rèn)識(shí),哦,原來是這么回事,也就算我沒白花這份閑。
內(nèi)核文件I/O->標(biāo)準(zhǔn)庫I/O->高級(jí)I/O->IPC中
1.?????????read()/write();
2.?????????pread()/pwrite();
3.?????????getc()/putc();
4.?????????fgetc()/fputc();
5.?????????getchar()/putchar();
6.?????????ferror()/feof();
7.?????????fgets()/fputs();
8.?????????gets()/puts();
9.?????????fread()/fwrite();
10.?????scanf()/fscanf()/sscanf()/vscanf()/vfscanf()/vsscanf()
11.?????printf()/fprintf()/sprintf()/snprintf()/vprintf()/vfprintf()/vsprintf()/vsanprintf()
12.?????readv()/writev()
13.?????read()/written()
14.?????msgrcv()/msgsnd()
15.?????revc()/recvfrom()/recvmsg()
16.?????send()/sendto()/sendmsg()
17.?????recv_fd()/send_fd()/send_err()
??粗略總結(jié)了下,有如上邊所示的17個(gè)大類,咋一看,的確讓人頭有點(diǎn)小暈。但是大師們都說存在的就是合理的,下邊讓我們看看,是怎么樣的深入淺出,讓這些函數(shù)有了存在的理由。要理解這些,先要知道系統(tǒng)在輸入輸出時(shí)所要經(jīng)過的邏輯處理模塊是怎樣。如下圖示
?
以上的用戶空間的應(yīng)用程序利用系統(tǒng)調(diào)用完成文件的讀寫過程,說明如下:
(1)???????用戶空間與內(nèi)核空間;這一組關(guān)系不用說明了。
(2)???????讀與寫:都將cpu或是內(nèi)存或是用戶程序看成主體,則讀,內(nèi)存<-文件;寫,內(nèi)存->文件;因?yàn)橹黧w是用戶程序,所以在讀或是寫是,對(duì)讀,要確定從什么讀,對(duì)寫,向什么寫。
(3)???????應(yīng)用程序利用系統(tǒng)服務(wù)有三條路:通過shell命令等直接實(shí)現(xiàn);利用庫函數(shù)實(shí)現(xiàn);直接調(diào)用系統(tǒng)調(diào)用的函數(shù),如read,write等命令。在這里可以將庫函數(shù)與系統(tǒng)調(diào)用的關(guān)系看清楚了。系統(tǒng)調(diào)用是最基本的了,任何想要獲得系統(tǒng)服務(wù)的都要經(jīng)過它,這是個(gè)關(guān)卡。
(4)???????文件I/O與標(biāo)準(zhǔn)I/O:前者是指在用戶空間中不需要其實(shí)進(jìn)程明確提供一個(gè)緩沖(如圖中的bf2),其實(shí)就是進(jìn)程在用戶空間直接調(diào)用read/write等函數(shù),但是,在內(nèi)核空間中都是要有緩沖的。這一般稱為文件I/O。標(biāo)準(zhǔn)I/O:提供了一種對(duì)不用緩沖I/O的函數(shù)(這些函數(shù)即可以用于不用緩沖的I/O函數(shù),也可以有于帶有緩沖的I/O函數(shù))的帶緩沖的接口。這一般是庫函數(shù)在用戶空間建立的(這些緩沖由庫函數(shù)完成,不需用戶自己管理,是封裝在庫函數(shù)中的),如BUF2,可能是庫函數(shù)想將對(duì)從上層接收過來的數(shù)據(jù)做個(gè)預(yù)處理,如格式變換等。使用標(biāo)準(zhǔn)I/O函數(shù)可以無需擔(dān)心如何選取最佳的緩沖區(qū)大小(由庫函數(shù)為你完成),還有一個(gè)是簡(jiǎn)化了對(duì)輸入行的處理。標(biāo)準(zhǔn)I/O函數(shù)庫提供了使我們能夠控制該庫使用的緩沖風(fēng)格的函數(shù)。
(5)???????BUF1/BUF2/BUF3:BUF1,其實(shí)是用戶空間的一些字符串,變量等,理解為數(shù)據(jù)即可。有時(shí)也定義為名稱BUF的形式,如char buf[MAXLINE];,但此時(shí)BUF只是名稱叫BUF而矣,區(qū)別于真正的緩沖區(qū)的概念。BUF2,這是庫函數(shù)為您老在用戶空間建立的,不用您親自管理,您只要一聲令下,如調(diào)用個(gè)庫函數(shù)中某個(gè)函數(shù),自有人為你服務(wù),這個(gè)BUF2,我們稱之為真正的緩沖區(qū)。BUF3,不論您是選擇文件I/O的形式還是標(biāo)準(zhǔn)I/O的形式,不論是哪一種,在內(nèi)核中的都要用到緩沖區(qū)BUF3(這是怎么樣都免不了的),但是這個(gè)也不要用戶來親力親為,由內(nèi)核代為管理。
(6)???????流(stream):這是標(biāo)準(zhǔn)為I/O中用到的,流是文件的邏輯代表,將文件I/O的: 進(jìn)程->fd->文件,改變?yōu)?#xff1a;進(jìn)程->fp(FILE對(duì)象)->流/緩沖->文件。原來對(duì)文件的操作,現(xiàn)在用戶只用處理:進(jìn)程->流之間的操作,而流->文件之間的操作將由庫函數(shù)為你完成。流的邏輯表示就是FILE對(duì)象,而流的實(shí)體就是流使用的緩沖區(qū),這些緩沖區(qū)相對(duì)于應(yīng)用進(jìn)程來說就是文件的代表。流=FILE + 緩沖。標(biāo)準(zhǔn)I/O庫提供緩沖的的是盡可能減少使用read?和write的次數(shù)。
好,暫此做以上四點(diǎn)說明吧,待有想法時(shí)再添加。下邊進(jìn)入正題,看看上邊的這些函數(shù),是什么形式的,為什么要有這些函數(shù)的存在,都為系統(tǒng)做些什么,怎么做的。
1.???????文件I/O相關(guān)(進(jìn)程->fd->文件)(文件fd, buf):
(1)???????read()
形式:#include<unistd.h>
??????ssize_t??read (int filedes,??void *buf,??size_t??nbytes );
??????成功:返回讀到的字節(jié)數(shù);出錯(cuò):返回-1;文件尾:返回0;
原因:基本系統(tǒng)調(diào)用功能;
實(shí)現(xiàn):文件(由filedes所指)-讀nbytes字節(jié)->內(nèi)存buf中。
補(bǔ)充:有多種情況可使實(shí)際讀到的字節(jié)數(shù)少于要求讀的字節(jié)數(shù):
當(dāng)從普通文件讀時(shí),在讀到要求字節(jié)數(shù)之前已到達(dá)了文件尾端。
當(dāng)從終端設(shè)備讀時(shí),通常一次最多讀一行。
當(dāng)從網(wǎng)絡(luò)讀時(shí),網(wǎng)絡(luò)中緩沖機(jī)構(gòu)可能造成返回值小于所要求讀的字節(jié)數(shù)。
當(dāng)從管道或FIFO讀時(shí),如若管道包含的字節(jié)少于所需的數(shù)量,那么只返回實(shí)際用
的字節(jié)數(shù)。
當(dāng)從某些面向記錄的設(shè)備讀時(shí),一次最多返回一個(gè)記錄。
當(dāng)某一信號(hào)造成中斷,而已經(jīng)讀了部分?jǐn)?shù)據(jù)量時(shí)。
讀操作從文件的當(dāng)前偏移量處開始,在成功返回之前,該偏移量將增加實(shí)際讀到的字節(jié)數(shù)。常用的unix系統(tǒng)shell都提供一種方法,它在標(biāo)準(zhǔn)輸入上打開一個(gè)文件,在標(biāo)準(zhǔn)輸出上追尋或重寫一個(gè)文件,這使得程序不必自行打開輸入和輸出文件。
(2)???????write()
形式:#include<unistd.h>
??????ssize_t??write (int filedes,??const void *buf,??size_t??nbytes );
??????成功:返回已寫的字節(jié)數(shù);出錯(cuò):返回-1;
原因:基本系統(tǒng)調(diào)用功能;
實(shí)現(xiàn):文件(由filedes所指)<-寫nbytes字節(jié)-內(nèi)存buf中。
補(bǔ)充:write出錯(cuò)的一個(gè)常見的原因是:磁盤已寫滿,或者超過了一個(gè)給定進(jìn)程的文件長(zhǎng)度限制。對(duì)于普通文件,寫操作從文件的當(dāng)前偏移量處開始。如果在打開該文件時(shí),指定了O_APPEND選項(xiàng),則在每次寫操作之前,將文件偏移量設(shè)置在文件的當(dāng)前結(jié)尾處。在一次成功寫之后,該文件偏移量增加實(shí)際寫的字節(jié)數(shù)。
(3)???????pread()
形式:#include<unistd.h>
??????ssize_t??pread (int filedes,???void *buf,??size_t??nbytes,??off_t??offset );
??成功:返回讀到的字節(jié)數(shù);出錯(cuò):返回-1;到文件結(jié)尾:返回0
原因:由于lseek和read?調(diào)用之間,內(nèi)核可能會(huì)臨時(shí)掛起進(jìn)程,所以對(duì)同步問題造成了問題,調(diào)用pread相當(dāng)于順序調(diào)用了lseek?和 read,這兩個(gè)操作相當(dāng)于一個(gè)捆綁的原子操作。
實(shí)現(xiàn):文件(由filedes所指)-讀nbytes字節(jié)->內(nèi)存buf中。
補(bǔ)充:調(diào)用pread時(shí),無法中斷其定位和讀操作,另外不更新文件指針。
(4)???????pwrite()
形式:#include<unistd.h>
??????ssize_t??pwrite (int filedes,???const void *buf,??size_t??nbytes,??off_t??offset );
??成功:返回已寫的字節(jié)數(shù);出錯(cuò):返回-1;
原因:由于lseek和write?調(diào)用之間,內(nèi)核可能會(huì)臨時(shí)掛起進(jìn)程,所以對(duì)同步問題造成了問題,調(diào)用pwrite相當(dāng)于順序調(diào)用了lseek?和 write,這兩個(gè)操作相當(dāng)于一個(gè)捆綁的原子操作。
實(shí)現(xiàn):文件(由filedes所指)<-寫nbytes字節(jié)-內(nèi)存buf中。
補(bǔ)充:調(diào)用pwrite時(shí),無法中斷其定位和讀操作,另外不更新文件指針。
?
2.???????流(stream)或標(biāo)準(zhǔn)I/O( 進(jìn)程->fp->流(FILE+緩沖)->文件)(內(nèi)存buf,?流fp):
每次輸入一個(gè)字符:
(1)???????getc();
格式:#include <stdio.h>
??????int getc(FILE *fp);
??????成功:返回下一個(gè)字符;出錯(cuò):返回EOF;文件尾:EOF;
實(shí)現(xiàn):內(nèi)存 <-讀一個(gè)字符c- 流(由fp所指的流,是文件的邏輯代表)
原因:在標(biāo)準(zhǔn)I/O中用,將流看成文件的邏輯代表,將對(duì)進(jìn)程->文件的操作,現(xiàn)轉(zhuǎn)換為進(jìn)程->流(也就是相當(dāng)于文件)的操作。
補(bǔ)充:函數(shù)在返回下一個(gè)字符時(shí),會(huì)將其unsigned char類型轉(zhuǎn)換為int類型。為不帶符號(hào)的理由是,如果最高位是1也不會(huì)使返回值為負(fù)。要求整形返回值的理由是,這樣就可以返回所有可能的字符值再加上一個(gè)已出錯(cuò)或已到達(dá)文件尾端的指示值。即字符值變?yōu)檎膇nt值,負(fù)的值就是出錯(cuò)或是到達(dá)文件尾端。(負(fù)值表特殊意義),同時(shí)不論是出錯(cuò)還是到達(dá)文件尾端,這三個(gè)函數(shù)都返回同樣的值即都是-1。由于每個(gè)流在FILE對(duì)象中維持了兩個(gè)標(biāo)志,即出錯(cuò)標(biāo)志和文件結(jié)束標(biāo)志,為了區(qū)分其不同,必須調(diào)用ferror或feof。
(2)???????fgetc();
格式:#include <stdio.h>
??????int fgetc(FILE *fp);
??????成功:返回下一個(gè)字符;出錯(cuò):返回EOF;文件尾:EOF;
?
實(shí)現(xiàn):同getc
原因:同getc
補(bǔ)充:同getc
(3)???????getchar();
格式:#include <stdio.h>
??????int getchar(void);
成功:返回下一個(gè)字符;出錯(cuò):返回EOF;文件尾:EOF;
實(shí)現(xiàn):內(nèi)存 <-讀一個(gè)字符c- 流(由stdin所指的流,是標(biāo)準(zhǔn)輸入文件的邏輯代表),所以getchar=getc(stdin);
原因:同getc
補(bǔ)充:同getc
?
每次輸入一行:
(4)???????fgets();
格式:#include <stdio.h>
??????char *fgets(char *restrict buf,??Int n,??FILE *restrict??fp);
??????成功:返回buf;出錯(cuò):返回NULL;?文件結(jié)尾:NULL;
實(shí)現(xiàn):內(nèi)存buf <-從fp所指的流中取一行字符- 流(由fp所指)
原因:在標(biāo)準(zhǔn)I/O中用,將流看成文件的邏輯代表,將對(duì)進(jìn)程->文件的操作,現(xiàn)轉(zhuǎn)換為進(jìn)程->流(也就是相當(dāng)于文件)的操作。
補(bǔ)充:必須指定用戶進(jìn)程緩沖區(qū)的長(zhǎng)度n,即buf的大小,此函數(shù)從流中一直讀到下一個(gè)換行符為止,但是不超過n-1個(gè)字符,讀入的字符被送入用戶緩沖區(qū)buf中。該緩沖區(qū)以null字符結(jié)尾。如若該行包括最后換行符的字?jǐn)?shù)大于n-1,則其只返回一個(gè)不完整的行,但是緩沖區(qū)buf總是以null字符結(jié)尾,對(duì)此函數(shù)的調(diào)用會(huì)繼續(xù)讀該行。緩沖區(qū)buf中的內(nèi)容為:(字符+換行符)+null。所以字符+換行符<=n-1,因?yàn)橐欢ㄒ粢粋€(gè)NULL字符來標(biāo)識(shí)緩沖區(qū)的結(jié)束;
(5)???????gets();
格式:#include <stdio.h>
??????char *gets(char * buf);
??????成功:返回buf;出錯(cuò):返回NULL;?文件結(jié)尾:NULL;
實(shí)現(xiàn):內(nèi)存buf <-從stdin所指的流中取1行字符-標(biāo)準(zhǔn)輸入流(由fp=stdin所指)
原因:同上;
補(bǔ)充:不推薦使用,問題是調(diào)用者在使用gets時(shí),不能指定緩沖區(qū)buf(用戶進(jìn)程)的長(zhǎng)度,這樣可能造成緩沖區(qū)溢出。
?
每次輸出一個(gè)字符:
(6)???????putc();
格式:#include <stdio.h>
??????int putc(int c ,FILE *fp);
??????成功:返回c;出錯(cuò):返回EOF;
實(shí)現(xiàn):內(nèi)存中整形變量c-寫字符C->流(由fp所指)。至于流什么時(shí)候?qū)寫入文件中,這個(gè)由庫函數(shù)來實(shí)現(xiàn),不用用戶操心;
原因:
補(bǔ)充:
?
(7)???????fputc();
格式:#include <stdio.h>
??????int fputc(int c ,FILE *fp);
??????成功:返回c;出錯(cuò):返回EOF;
實(shí)現(xiàn):內(nèi)存中整形變量c-寫字符C->流(由fp所指)。至于流什么時(shí)候?qū)寫入文件中,這個(gè)由庫函數(shù)來實(shí)現(xiàn),不用用戶操心;
原因:
補(bǔ)充:
?
(8)???????putchar();
格式:#include <stdio.h>
??????int putchar(int c);
??????成功:返回c;出錯(cuò):返回EOF;
實(shí)現(xiàn):內(nèi)存中整形變量c-寫字符C->流(由fp=stdout所指)。至于流什么時(shí)候?qū)寫入標(biāo)準(zhǔn)輸出文件中,這個(gè)由庫函數(shù)來實(shí)現(xiàn),不用用戶操心;
原因:
補(bǔ)充:putchar(c)=putc(c,stdout);
?
每次輸出一行:
(9)???????fputs();
格式:#include <stdio.h>
??????int fputs(const char *restrict??str, FILE??*restrict??fp);
成功:返回非負(fù)值;出錯(cuò):返回EOF;
實(shí)現(xiàn):內(nèi)存中字符數(shù)組str-寫字符數(shù)組str->流(由fp所指)。
原因:
補(bǔ)充:將一個(gè)以null符終止的字符串(相當(dāng)于用戶空間buf,肯定有null,對(duì)應(yīng)于fgets的buf中一定要有個(gè)null來標(biāo)識(shí)緩沖區(qū)buf的結(jié)束。)寫到指定的流,尾端的終止符null不寫進(jìn)流中。注意,這并不一定是每次輸出一行,因?yàn)樗⒉灰笤趎ull之前一定是換行符,buf中有就有,沒有就沒有,通常,在空字符之前是一個(gè)換行符,但并不要求總是如此。用戶空間buf:字符(+換行符)+null;流中的buf:字符+換行符。
?
(10)???puts();
格式:#include <stdio.h>
??????int puts(const char * str);
成功:返回非負(fù)值;出錯(cuò):返回EOF;
實(shí)現(xiàn):內(nèi)存中字符數(shù)組str-寫字符數(shù)組str->標(biāo)準(zhǔn)輸出流(由fp=stdout所指)。
原因:
補(bǔ)充:將一個(gè)以null結(jié)尾的字符串寫到標(biāo)準(zhǔn)輸出上,相當(dāng)于進(jìn)程->流->標(biāo)準(zhǔn)輸出文件。終止符不寫出,但是puts然后又將一個(gè)換行符寫到標(biāo)準(zhǔn)輸出。應(yīng)當(dāng)少用,以免需要記住它在最后是否添加了一個(gè)換行符。而fgets和fputs在處理換行符,本著實(shí)事求是的態(tài)度,有就有,沒有就沒有,不會(huì)在用戶buf和流緩沖以及文件中自己添加,只是在數(shù)據(jù)經(jīng)過流緩沖時(shí),增加或是過濾到null字符。當(dāng)fgets時(shí)會(huì)在用戶buf中增加一個(gè)null以標(biāo)識(shí)用戶buf的結(jié)束,而fputs時(shí),以null為終止字符,但是尾端的null并不寫在流中。
?
二進(jìn)制I/O:
(11)???fread()
格式:#include <stdio.h>
??????ssize_t??fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict??fp);
??????成功:讀到的對(duì)象數(shù)。
實(shí)現(xiàn):內(nèi)存始址ptr<-讀N個(gè)對(duì)象- 流(由fp所指)
原因:以上有一次一個(gè)字符或是一次一行的方式進(jìn)行I/O操作,當(dāng)我們讀或?qū)懸粋€(gè)結(jié)構(gòu)時(shí),對(duì)于一次一個(gè)字符的方式,必須循環(huán)通過整個(gè)結(jié)構(gòu),每次循環(huán)處理一個(gè)字節(jié),一次讀或?qū)懸粋€(gè)字節(jié),這會(huì)很煩。而對(duì)于一次一行的方式,當(dāng)每次結(jié)構(gòu)體中有null字符時(shí),fputs就會(huì)停止,所以也不能用它實(shí)現(xiàn)讀結(jié)構(gòu),同時(shí)fgets中包含有null字節(jié)或換行符,其也不能正常工作。所以要并實(shí)現(xiàn)結(jié)構(gòu)體作為一個(gè)整體的讀或?qū)憽?/p>
補(bǔ)充:使用二進(jìn)制的基本問題是:它只能用于讀在同一系統(tǒng)上已寫的數(shù)據(jù)。其原
因是:在結(jié)構(gòu)中,同一成員偏移量可能因?yàn)榫幾g器和系統(tǒng)而異,另外,用來存儲(chǔ)多字節(jié)整數(shù)和浮點(diǎn)值的二進(jìn)制格式在不同的機(jī)器體系結(jié)構(gòu)之間也可能不同。
?
?
(12)???fwrite()
格式:#include <stdio.h>
??????ssize_t??fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict??fp);
??????成功:寫的對(duì)象數(shù)。
?
實(shí)現(xiàn):內(nèi)存始址ptr-寫N個(gè)對(duì)象-> 流(由fp所指)
原因:
補(bǔ)充:
?
格式化輸入:文件-流->格式轉(zhuǎn)換->內(nèi)存變量中
(13)???scanf();
格式:#include <stdio.h>
??????int scanf(const char *restrict format,…)
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):標(biāo)準(zhǔn)輸入流->格式轉(zhuǎn)換->內(nèi)存變量中。用于分析輸入字符串,并將字符序列轉(zhuǎn)換成指定類型的變量。格式之后的各個(gè)參數(shù)包含了變量的地址,以用轉(zhuǎn)換結(jié)果初始化這些變量。
原因:要在流中做格式轉(zhuǎn)換,再將結(jié)果放到內(nèi)存變量中
補(bǔ)充:
?
(14)???fscanf();
格式:#include <stdio.h>
??????int fscanf(FILE *restrict fp, const char *restrict format,…)
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):輸入流->格式轉(zhuǎn)換->內(nèi)存變量中
原因:
補(bǔ)充:
?
(15)???sscanf();
格式:#include <stdio.h>
??????int sscanf(const char *restrict buf, const char *restrict format,…)
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):內(nèi)存buf->格式轉(zhuǎn)換->內(nèi)存變量中。
原因:
補(bǔ)充:對(duì)于scanf(),?從標(biāo)準(zhǔn)輸入流中輸入;fscanf,從流中輸入;?sscanf,這個(gè)比較特殊,不是從流中輸入,而是內(nèi)存的一個(gè)buf相當(dāng)于string中輸入。
?
(16)???vscanf();
格式:#include <stdio.h>
??????int vscanf(const char *restrict format, va_list??arg);
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):標(biāo)準(zhǔn)輸入流->格式轉(zhuǎn)換->內(nèi)存變量中。用于分析輸入字符串,并將字符序列轉(zhuǎn)換成指定類型的變量。格式之后的各個(gè)參數(shù)包含了變量的地址,以用轉(zhuǎn)換結(jié)果初始化這些變量。同于scanf,只是將原來的可變參數(shù)…換成了arg;
原因:要在流中做格式轉(zhuǎn)換,再將結(jié)果放到內(nèi)存變量中
補(bǔ)充:
?
(17)???vfscanf();
格式:#include <stdio.h>
??????int vfscanf(FILE *restrict fp, const char *restrict format, va_list??arg)
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):輸入流->格式轉(zhuǎn)換->內(nèi)存變量中,?同于fscanf,只是將原來的可變參數(shù)…,換成了arg;
原因:
補(bǔ)充:
?
(18)???vsscanf();
格式:#include <stdio.h>
??????int vsscanf(const char *restrict buf, const char *restrict format, va_list??arg)
成功:指定的輸入項(xiàng)數(shù);出錯(cuò):返回EOF;輸入出錯(cuò)或在任意變換前已到達(dá)文件結(jié)尾:EOF;
實(shí)現(xiàn):內(nèi)存buf->格式轉(zhuǎn)換->內(nèi)存變量中。同于sscanf,只是將原來的可變參數(shù)…,換成了arg;
原因:
補(bǔ)充:對(duì)于scanf(),?從標(biāo)準(zhǔn)輸入流中輸入;fscanf,從流中輸入;?sscanf,這個(gè)比較特殊,不是從流中輸入,而是內(nèi)存的一個(gè)buf相當(dāng)于string中輸入。
?
?
格式化輸出:文件-流<-格式字符串<-內(nèi)存變量
(19)???printf();
格式:#include <stdio.h>
??????int??printf(const char *restrict format, …);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):標(biāo)準(zhǔn)輸出流<-格式字符串<-內(nèi)存變量
原因:要將內(nèi)存變量的數(shù)據(jù)做格式變換,再將變換的結(jié)果放入流中
補(bǔ)充:
?
(20)???fprintf();
格式:#include <stdio.h>
??????int??fprintf(FILE *restrict fp,const char *restrict format, …);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):文件-輸出流<-格式字符串<-內(nèi)存變量
原因:
補(bǔ)充:
?
(21)???sprint();
格式:#include <stdio.h>
??????int??sprintf(char *restrict buf, const char *restrict format, …);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):內(nèi)存字符串buf<-格式字符串<-內(nèi)存變量,就是將格式化的字符串送入數(shù)組buf而不是指定的流中。在數(shù)組的尾端自動(dòng)加一個(gè)null字節(jié),但該字節(jié)不包括在返回值中。
原因:
補(bǔ)充:
?
(22)???snprintf();
格式:#include <stdio.h>
??????int??snprintf(char *restrict buf, size_t n , const char *restrict format, …);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):內(nèi)存字符串buf<-格式字符串<-內(nèi)存變量,就是將格式化的字符串送入數(shù)組buf而不是指定的流中。在數(shù)組的尾端自動(dòng)加一個(gè)null字節(jié),但該字節(jié)不包括在返回值中。只能輸入n-1個(gè)字符,超過的任何字條都會(huì)被丟棄。
原因:
補(bǔ)充:
?
(23)???vprintf();
格式:#include <stdarg.h>
??????#include <stdio.h>
??????int??vprintf(const char *restrict format, va_list??arg);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):標(biāo)準(zhǔn)輸出流<-格式字符串<-內(nèi)存變量,同于printf,只是將原來的可變參數(shù)…換成了arg;
原因:要將內(nèi)存變量的數(shù)據(jù)做格式變換,再將變換的結(jié)果放入流中
補(bǔ)充:
?
(24)???vfprintf();
格式:#include <stdarg.h>
??????#include <stdio.h>
??????int??vfprintf(FILE *restrict fp,const char *restrict format, va_list??arg);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):輸出流<-格式字符串<-內(nèi)存變量,同于fprintf,只是將原來的可變參數(shù)…換成了arg;
原因:要將內(nèi)存變量的數(shù)據(jù)做格式變換,再將變換的結(jié)果放入流中
補(bǔ)充:
(25)???vsprintf();
格式:#include <stdarg.h>
??????#include <stdio.h>
??????int??vsprintf(char *restrict buf, const char *restrict format, va_list??arg);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):內(nèi)存數(shù)組buf<-格式字符串<-內(nèi)存變量,同于sprintf,只是將原來的可變參數(shù)…換成了arg;?就是將格式化的字符串送入數(shù)組buf而不是指定的流中。在數(shù)組的尾端自動(dòng)加一個(gè)null字節(jié),但該字節(jié)不包括在返回值中。
原因:要將內(nèi)存變量的數(shù)據(jù)做格式變換,再將變換的結(jié)果放入流中
補(bǔ)充:
?
(26)???vsnprintf();
格式:#include <stdio.h>
??????int??vsnprintf(char *restrict buf, size_t n , const char *restrict format, va_list arg);
??????成功:返回輸出字符數(shù);出錯(cuò):返回負(fù)值;
實(shí)現(xiàn):內(nèi)存字符串buf<-格式字符串<-內(nèi)存變量,?同于snprintf,只是將原來的可變參數(shù)…換成了arg;?就是將格式化的字符串送入數(shù)組buf而不是指定的流中。在數(shù)組的尾端自動(dòng)加一個(gè)null字節(jié),但該字節(jié)不包括在返回值中。只能輸入n-1個(gè)字符,超過的任何字條都會(huì)被丟棄。
原因:
補(bǔ)充:
?
3.???????高級(jí)I/O:(文件(fd),?內(nèi)存buf )
(1)???????readv()
格式:#include <sys/uio.h>
??????ssize_t??readv(int filedes, const??struct iovec *iov, int iovcnt);
??????成功:返回已讀的字節(jié)數(shù);出錯(cuò):返回-1;
實(shí)現(xiàn):文件(fd)->內(nèi)存向量中
原因:在一次函數(shù)調(diào)用中讀、寫多個(gè)非連續(xù)緩沖區(qū),但是這些緩沖區(qū)已經(jīng)用iovec表示好了。減少了系統(tǒng)調(diào)用的次數(shù)。
補(bǔ)充:
(2)???????writev()
格式:#include <sys/uio.h>
??????ssize_t??writev(int filedes, const??struct iovec *iov, int iovcnt);
??????成功:返回已讀的字節(jié)數(shù);出錯(cuò):返回-1;
實(shí)現(xiàn):文件(fd)<-內(nèi)存向量
原因:在一次函數(shù)調(diào)用中讀、寫多個(gè)非連續(xù)緩沖區(qū),但是這些緩沖區(qū)已經(jīng)用iovec表示好了。減少了系統(tǒng)調(diào)用的次數(shù)。
補(bǔ)充:
?
(3)???????readn()
格式:#include <sys/uio.h>
??????ssize_t??readn(int filedes, void *bug, size_t??nbytes);
??????成功:返回已讀的字節(jié)數(shù);出錯(cuò):返回-1;
實(shí)現(xiàn):文件(fd)->內(nèi)存buf中
原因:管道、FIFO以及某些設(shè)備,特別是終端、網(wǎng)絡(luò)和STREAMS設(shè)備有下列兩種性質(zhì):一是,一次read操作所返回的數(shù)據(jù)可能少于所要求的數(shù)據(jù),即使還沒達(dá)到文件尾端也可能是這樣的。這不是一個(gè)錯(cuò)誤,應(yīng)當(dāng)繼續(xù)讀該設(shè)備。二是,一次write操作所返回的值也可能少于所指定輸出的字節(jié)數(shù),這可能是由若干因素造成的。這些也不是錯(cuò)誤,也應(yīng)當(dāng)繼續(xù)寫余下的數(shù)據(jù)至該設(shè)備。通常只對(duì)非阻塞描述符,或捕捉到一個(gè)信號(hào)時(shí),才發(fā)生這種write的中途返回。但是在讀寫磁盤時(shí),很少遇到這樣的情況。所以這個(gè)函數(shù)其實(shí)是按需要多次調(diào)用read?和write直至讀、寫了N個(gè)字節(jié)數(shù)據(jù),即我們稱之為:直到集齊了再返回。
補(bǔ)充:
?
(4)???????written()
格式:#include <sys/uio.h>
??????ssize_t??writen(int filedes, void *bug, size_t??nbytes);
??????成功:返回已讀的字節(jié)數(shù);出錯(cuò):返回-1;
實(shí)現(xiàn):文件(fd)<-內(nèi)存buf中
原因:管道、FIFO以及某些設(shè)備,特別是終端、網(wǎng)絡(luò)和STREAMS設(shè)備有下列兩種性質(zhì):一是,一次read操作所返回的數(shù)據(jù)可能少于所要求的數(shù)據(jù),即使還沒達(dá)到文件尾端也可能是這樣的。這不是一個(gè)錯(cuò)誤,應(yīng)當(dāng)繼續(xù)讀該設(shè)備。二是,一次write操作所返回的值也可能少于所指定輸出的字節(jié)數(shù),這可能是由若干因素造成的。這些也不是錯(cuò)誤,也應(yīng)當(dāng)繼續(xù)寫余下的數(shù)據(jù)至該設(shè)備。通常只對(duì)非阻塞描述符,或捕捉到一個(gè)信號(hào)時(shí),才發(fā)生這種write的中途返回。但是在讀寫磁盤時(shí),很少遇到這樣的情況。所以這個(gè)函數(shù)其實(shí)是按需要多次調(diào)用read?和write直至讀、寫了N個(gè)字節(jié)數(shù)據(jù),即我們稱之為:直到集齊了再返回。
補(bǔ)充:
4.???????IPC中:
消息隊(duì)列中:
(1)???????msgrcv()
格式:#include <sys/msg.h>
??????ssize_t??msgrcv(int??msqid, void *ptr, size_t nbytes, long type, int flag);
??????成功:返回消息的數(shù)據(jù)部分長(zhǎng)度;出錯(cuò):-1;
實(shí)現(xiàn):消息隊(duì)列->內(nèi)存消息結(jié)構(gòu)體(由ptr指向)
原因:
補(bǔ)充:nbytes說明數(shù)據(jù)緩沖區(qū)的長(zhǎng)度。用來構(gòu)造mymesg。若返回的消息大于nbytes,而且在flag中設(shè)置了MSG_NOERROR,則該消息被截短。如果沒有設(shè)置這一標(biāo)志,而消息又太長(zhǎng),則出錯(cuò)返回E2BIG(消息仍留在隊(duì)列中。參數(shù)type我們可以指定想要哪一種消息。可以指定flag值為IPC_NOWAIT,使操作不阻塞。這使得如果沒有所指定類型的消息,則msgrcv返回-1,errno設(shè)置為ENOMSG。
?
(2)???????msgsnd()
格式:#include <sys/msg.h>
??????int??msgsnd(int??msqid, const void *ptr, size_t nbytes, long type, int flag);
??????成功:返回0;出錯(cuò):-1;
實(shí)現(xiàn):消息隊(duì)列<-內(nèi)存消息結(jié)構(gòu)體(由ptr指向)
原因:
補(bǔ)充:每個(gè)消息都由三部分組成,它們是:正長(zhǎng)整型類型字段、實(shí)際數(shù)據(jù)字節(jié)(這兩個(gè)對(duì)就myseq結(jié)構(gòu)體)、非負(fù)長(zhǎng)度(nbytes)。消息總是放在隊(duì)列尾端。ptr參數(shù)指向一個(gè)長(zhǎng)整型數(shù),它包含了正的整型消息類型,在其后緊跟著消息數(shù)據(jù)。可以定義如下結(jié)構(gòu):struct myseq{ long mtype; char mtex[512];}??于是ptr就是一個(gè)指向mymesg結(jié)構(gòu)的指針。接收者可以用消息類型以非先進(jìn)先出的次序取消息。
?
?
?
?
SOCKET中:
(1)???????revc()
格式:#include <sys/socket.h>
??????ssize_t??recv(int sockfd, void *buf, size_t??nbytes, int flags);
??????成功:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度;出錯(cuò):-1;無可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束:0;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd-取消息msg->內(nèi)存buf中。
原因:
補(bǔ)充:
(2)???????recvfrom()
格式:#include <sys/socket.h>
??????ssize_t??recvfrom( int sockfd, void *restrict??buf, size_t??len, int flags, struct sockaddr *restrict addr, socklen_t *restrict??addrlen);
??????成功:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度;出錯(cuò):-1;無可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束:0;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd-取消息msg->內(nèi)存buf中。
原因:
補(bǔ)充:如果addr非空,它將包含數(shù)據(jù)發(fā)送者的套接字端點(diǎn)地址,當(dāng)調(diào)用recvfrom時(shí),需要設(shè)置addrlen參數(shù)指向一個(gè)包含addr所指的套接字緩沖區(qū)字節(jié)大小的整數(shù)。返回時(shí),該整數(shù)設(shè)為該地址的實(shí)際字節(jié)大小。因?yàn)榭梢垣@得發(fā)送者的地址,recvfrom通常用于無連接套接字。
?
(3)???????recvmsg()
格式:#include <sys/socket.h>
??????ssize_t??recvmsg( int sockfd, struct msghdr *msg, int flags);
??????成功:以字節(jié)計(jì)數(shù)的消息長(zhǎng)度;出錯(cuò):-1;無可用消息或?qū)Ψ揭呀?jīng)按序結(jié)束:0;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd-取消息msg->內(nèi)存buf中。
原因:
補(bǔ)充:結(jié)構(gòu)msghdr被recvmsg用于指定接收數(shù)據(jù)的輸入緩沖區(qū)。
?
(4)???????send()
格式:#include <sys/socket.h>
??????ssize_t??send(int sockfd, const??void *buf, size_t??nbytes, int flags);
??????成功:返回發(fā)送的字節(jié)數(shù);出錯(cuò):-1;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd<-取消息msg-內(nèi)存buf中。
原因:
補(bǔ)充:如果send成功,并不必然表示連接另一端的進(jìn)程接收數(shù)據(jù)。所保證的僅是當(dāng)send成功返回時(shí),數(shù)據(jù)已經(jīng)無錯(cuò)誤的發(fā)送到網(wǎng)絡(luò)上。
(5)???????sendto()
格式:#include <sys/socket.h>
??????ssize_t??sendto( int sockfd, const void *restrict??buf, size_t nbytes, int flags,??const struct sockaddr *dest addr, socklen_t * addrlen);
成功:返回發(fā)送的字節(jié)數(shù);出錯(cuò):-1;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd<-取消息msg-內(nèi)存buf中。
原因:
補(bǔ)充:適用于無連接的套接字,不能使用send,除非調(diào)用connect時(shí)預(yù)先設(shè)定了目標(biāo)地址,或者采用了sendto來提供另外一種報(bào)文發(fā)送方式。
?
(6)???????sendmsg()
格式:#include <sys/socket.h>
??????ssize_t??sendmsg( int sockfd,??const struct msghdr *msg, int flags);
成功:返回發(fā)送的字節(jié)數(shù);出錯(cuò):-1;
實(shí)現(xiàn):網(wǎng)絡(luò)sockfd<-取消息msg-內(nèi)存buf中。
原因:
補(bǔ)充:可以調(diào)用帶有msghdr結(jié)構(gòu)的sendmsg來指定多重緩沖區(qū)傳輸數(shù)據(jù),和writev很像。
?
傳送文件描述符(略,自行實(shí)現(xiàn))
(1)???????recv_fd()
(2)???????send_fd()
(3)???????send_err()
?
總算整差不多了,雖然花了點(diǎn)時(shí)間,但是希望能對(duì)大家有幫助,最后謝謝閱讀!
總結(jié)
以上是生活随笔為你收集整理的Linux系统函数read()/write()/pread()/pwrite()的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【slighttpd】基于lighttp
- 下一篇: linux 其他常用命令