<section style="font-size: 16px;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">0. 學(xué)習(xí)原因</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大多時候释移,高級別I/O函數(shù)工作良好,沒必要直接用Unix I/O鱼喉,為何需學(xué)習(xí)秀鞭?</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>了解Unix I/O將幫助理解其他系統(tǒng)概念趋观。I/O是系統(tǒng)操作不可或缺的部分扛禽,因此經(jīng)常遇到I/O和其他系統(tǒng)概念之間的循環(huán)依賴</p></li><li><p>有時必須用Unix I/O,用高級I/O不太可能或不合適皱坛,如標準I/O庫沒提供讀取文件元數(shù)據(jù)的方式编曼,此外I/O庫存在一些問題</p></li></ul><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">1. Unix I/O</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">輸入/輸出(I/O)是主存和外部設(shè)備之間復(fù)制數(shù)據(jù)的過程,在 Linux 中剩辟,文件就是字節(jié)的序列掐场。所有的 I/O 設(shè)備(如網(wǎng)絡(luò)、內(nèi)核贩猎、磁盤和終端等)都被模型化為文件熊户,而所有的輸入和輸出都被當作對相應(yīng)文件的讀和寫來執(zhí)行。這種將設(shè)備映射為文件的機制吭服,允許內(nèi)核引出簡單嚷堡、優(yōu)雅的應(yīng)用接口Unix I/O,使得所有輸入和輸出都以統(tǒng)一的方式執(zhí)行艇棕,如 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">open()</code>/<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">close()</code> 打開/關(guān)閉文件蝌戒,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">read()</code>/ <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">write()</code> 讀/寫文件。<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">seek()</code>改變當前文件位置沼琉。Unix I/O主要分為兩大類:</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img" data-ratio="0.3220910623946037" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbeNC6beVdfhK1lXVrCJicP9LVicKzrrbD50HMrMWDVOMzehdBmCA7Cuibw/640?wx_fmt=png" data-type="png" data-w="1186" style="display: block; margin-right: auto; margin-left: auto; width: 677px !important; height: auto !important; visibility: visible !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-dbaff84a3a0d5022.png" crossorigin="anonymous" alt="圖片" data-fail="0"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">為區(qū)分不同文件的類型北苟,會有一個 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">type</code> 來進行區(qū)別:</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>普通文件:包含任意數(shù)據(jù)</p></li><li><p>目錄:相關(guān)文件組的索引</p></li><li><p>Socket:用于另一臺機器上的進程通信</p></li></ul><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">還有一些特別的類型僅做了解:命名管道(FIFOs)、符號鏈接打瘪、字符和塊設(shè)備</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>普通文件</strong></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">普通文件包含任意數(shù)據(jù)友鼻,應(yīng)用程序通常需區(qū)分文本文件和二進制文件。前者只包含 ASCII 或 Unicode 字符闺骚。除此之外的都是二進制文件(對象文件, JPEG 圖片, 等等)彩扔。內(nèi)核不能區(qū)分出區(qū)別。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">文本文件是文本行的序列葛碧,每行以 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">\n</code> 結(jié)尾借杰,新行是 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">0xa</code>,和 ASCII 碼中LF一樣进泼。不同系統(tǒng)判斷行結(jié)束的符號不同(End of line, EOL)蔗衡,Linux & Mac OS是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">\n</code>(0xa)等價line feed(LF)纤虽,而Windows & 網(wǎng)絡(luò)協(xié)議是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">\r\n</code> (0xd 0xa)等價Carriage return(CR) followed by line feed(LF)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>目錄</strong></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">目錄包含一個鏈接(link)數(shù)組,且每個目錄至少包含兩記錄:<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">.</code>(dot) 當前目錄绞惦、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">..</code>(dot dot) 上層目錄</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">操作命令主要有 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">mkdir</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">ls</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">rmdir</code>逼纸。目錄以樹狀結(jié)構(gòu)組織,根目錄是 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">/</code>(slash)济蝉。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">內(nèi)核會為每個進程保存當前工作目錄(cwd, current working directory)杰刽,可用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">cd</code> 命令進行更改。通過路徑名來確定文件的位置王滤,分為絕對路徑和相對路徑贺嫂。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.4576719576719577" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbbctdqeg0vSg2rDaJx3B72ic7JZ82Zn1ryelXgxJomLndmZZoXRWK7NA/640?wx_fmt=png" data-type="png" data-w="756" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 301.775px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-f28f575f5aec1cb2.png" crossorigin="anonymous" alt="圖片"></p><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">2. 文件操作</h1><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.1 打開文件</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打開文件會通知內(nèi)核已準備好訪問該文件</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">int fd; // 文件描述符 file descriptor
if ((fd = open("/etc/hosts", O_RDONLY)) < 0)
{
perror("open");
exit(1);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">返回值是一個小的整型稱為文件描述符(file descriptor),若該值等于 -1 則說明發(fā)生錯誤雁乡。每個由 Linux shell創(chuàng)建的進程都會默認打開三個文件(注意這里的文件概念):</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>0: standard input(stdin)</p></li><li><p>1: standard output(stdout)</p></li><li><p>2: standar error(stderr)</p></li></ul><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.2 關(guān)閉文件</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">關(guān)閉文件會通知內(nèi)核已完成對該文件的訪問</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">int fd; // 文件描述符
int retval; // 返回值
int ((retval = close(fd)) < 0)
{
perror("close");
exit(1);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">關(guān)閉一個已經(jīng)關(guān)閉的文件是線程程序中的災(zāi)難(稍后會詳細介紹)第喳,所以一定要檢查返回值,哪怕是看似良好的函數(shù)如 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">close()</code></p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.3 讀取文件</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">讀取文件將字節(jié)從當前文件位置復(fù)制到內(nèi)存踱稍,然后更新文件位置</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">char buf[512];
int fd;
int nbytes
// 打開文件描述符曲饱,并從中讀取 512 字節(jié)的數(shù)據(jù)
if ((nbytes = read(fd, buf, sizeof(buf))) < 0)
{
perror("read");
exit(1);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">返回值是讀取的字節(jié)數(shù)量,是一個 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">ssize_t</code> 類型(其實就是一個有符號整型)珠月,如果 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">nbytes < 0</code> 那么表示出錯扩淀。<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">nbytes < sizeof(buf)</code> 這種情況(short counts) 是可能發(fā)生的,而且并不是錯誤啤挎。</p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.4 寫入文件</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">寫入文件將字節(jié)從內(nèi)存復(fù)制到當前文件位置驻谆,然后更新當前文件位置</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">char buf[512];
int fd;
int nbytes;
// 打開文件描述符,并向其寫入 512 字節(jié)的數(shù)據(jù)
if ((nbytes = write(fd, buf, sizeof(buf)) < 0)
{
perror("write");
exit(1);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">返回值是寫入的字節(jié)數(shù)量侵浸,如果 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">nbytes < 0</code> 表示出錯旺韭。<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">nbytes < sizeof(buf)</code> 這種情況(short counts) 是可能發(fā)生的,且不是錯誤掏觉。</p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.5 讀取目錄</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可用readdir系列函數(shù)讀取目錄的內(nèi)容区端,每次對readdir的調(diào)用返回的都是指向流dirp中下一個目錄項的指針,或沒有更多目錄項則返回NULL澳腹,每個目錄項都有結(jié)構(gòu)體:</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">struct dirent{
ino_t d_ino; /* inode number /
char d_name[256]; / filename */
};</code></pre><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">2.6 簡單Unix I/O 例子</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">拷貝文件到標準輸出织盼,一次一個字節(jié):</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">#include "csapp.h"
int main(int argc, char *argv[])
{
char c;
int infd = STDIN_FILENO;
if (argc == 2) {
infd = Open(argv[1], O_RDONLY, 0);
}
while(Read(infd, &c, 1) != 0)
Write(STDOUT_FILENO, &c, 1);
exit(0);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">前面提到的 short count 會在下面的情形下發(fā)生:</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>讀取的時遇到 EOF(end-of-file)</p></li><li><p>從終端中讀取文本行</p></li><li><p>讀和寫網(wǎng)絡(luò) sockets</p></li></ul><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但下面的情況下不會發(fā)生</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>從磁盤文件中讀取(除 EOF 外)</p></li><li><p>寫入到磁盤文件中</p></li></ul><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最好總是允許 short count酱塔,這樣就可以避免處理這么多不同的情況沥邻。</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">#include "csapp.h"
#define BUFSIZE 64
int main(int argc, char *argv[])
{
char buf[BUFSIZE];
int infd = STDIN_FILENO;
if (argc == 2) {
infd = Open(argv[1], O_RDONLY, 0);
}
while((nread = Read(infd, buf, BUFSIZE)) != 0)
Write(STDOUT_FILENO, buf, nread);
exit(0);
}</code></pre><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">3. 元數(shù)據(jù)</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">元數(shù)據(jù)是用來描述數(shù)據(jù)的數(shù)據(jù),由內(nèi)核維護羊娃,可以通過 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">stat</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fstat</code> 函數(shù)來訪問唐全,結(jié)構(gòu)是:</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">struct stat
{
dev_t st_dev; // Device
ino_t st_ino; // inode
mode_t st_mode; // Protection & file type
nlink_t st_nlink; // Number of hard links
uid_t st_uid; // User ID of owner
gid_t st_gid; // Group ID of owner
dev_t st_rdev; // Device type (if inode device)
off_t st_size; // Total size, in bytes
unsigned long st_blksize; // Blocksize for filesystem I/O
unsigned long st_blocks; // Number of blocks allocated
time_t st_atime; // Time of last access
time_t st_mtime; // Time of last modification
time_t st_ctime; // Time of last change
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">對應(yīng)的訪問例子:</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">int main (int argc, char **argv)
{
struct stat stat;
char *type, *readok;
Stat(argv[1], &stat);
if (S_ISREG(stat.st_mode)) // 確定文件類型
type = "regular";
else if (S_ISDIR(stat.st_mode))
type = "directory";
else
type = "other";
if ((stat.st_mode & S_IRUSR)) // 檢查讀權(quán)限
readok = "yes";
else
readok = "no";
printf("type: %s, read: %s\n", type, readok);
exit(0);
}</code></pre><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">3.1 共享文件</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可用許多不同的方式共享Linux文件。要理解文件共享就得理解三個表示打開文件的數(shù)據(jù)結(jié)構(gòu)。</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">描述符表:每個進程都有獨立的描述符表邮利,表項是進程打開的文件描述符索引弥雹,每個打開的描述符表指向文件表中的一個表項</p></li><li><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">文件表:打開文件的集合是由一張文件表表示,所有進程共享該表延届,每個文件表的表項組成包括當前文件位置剪勿、引用計數(shù)、指向v-node表中對應(yīng)表項的指針方庭。關(guān)閉描述符減少文件表項引用計數(shù)直到為0會刪除文件表項</p></li><li><p>v-node表:同文件表一樣所有進程共享該v-node表厕吉,每個表包含stat結(jié)構(gòu)中的大多數(shù)信息,包括st_mode和st_size成員</p></li></ul><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">兩個描述符引用兩個不同的打開文件械念。描述符 1(stdout)指向終端头朱,描述符 4 指向打開的磁盤文件</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.456369982547993" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbLYkweBFIyHRsbZMMNWWaOSJiaF4j51MRVSVZnp4NEKkJNLF2x3c7x3Q/640?wx_fmt=png" data-type="png" data-w="1146" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 300.922px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-35945d647ebf78ff.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">兩個不同的描述符通過兩個不同的打開文件表條目共享同一個磁盤文件,使用相同的文件名參數(shù)調(diào)用 open 兩次订讼,關(guān)鍵思想是每個描述符都有自己的文件位置髓窜,所以對不同描述符的讀操作可從文件不同位置獲取數(shù)據(jù)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.4589160839160839" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbEGXKNpngUm4ticjhcVrBbj0YJQFdfYGhuKeicOUJrRiaQuzXSicvXWBziaA/640?wx_fmt=png" data-type="png" data-w="1144" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 302.59px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-8277dd7fc61b20e5.png" crossorigin="anonymous" alt="圖片"></p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">3.2 進程如何共享文件:<code>fork</code></h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">子進程繼承其父進程的打開文件扇苞,注意:exec 函數(shù)不會改變情況(使用 fcntl 來改變)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.5889628924833492" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHboFrFlsd7iagicCDnxFmmSZglUjtmaOqj8wxRo6lkibsAeMIr7mMGyh3Ow/640?wx_fmt=png" data-type="png" data-w="1051" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 387.771px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-bd4bc0f9c4d7fe7e.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 fork 之后欺殿,子進程實際上和父進程的指向是一樣的,這里需要注意的是會把引用計數(shù)加 1</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.6781609195402298" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbmO9KPNu0hNyEYVVHyIbXgx2e4QsD8cFe7HZyb4Mzv3yzaKMuMYiaviaw/640?wx_fmt=png" data-type="png" data-w="1044" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 446.195px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-246fd26f9ba8218b.png" crossorigin="anonymous" alt="圖片"></p><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">4. I/O重定向</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">了解了這個鳖敷,就知道重定向如何實現(xiàn)脖苏。其實很簡單,只要調(diào)用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">dup2(oldfd, newfd)</code>函數(shù)即可定踱。改變文件描述符指向的文件棍潘,就完成重定向,下圖中我們把原來指向終端的文件描述符指向了磁盤文件崖媚,也就把終端上的輸出保存在了文件中:</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.6834817012858556" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbibCfzN6VWNK7dQacwAKGJhiaN0VpKBjXdR7kZDV3ar912Sib1Ay5o0YQg/640?wx_fmt=png" data-type="png" data-w="1011" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 449.681px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-386f08beb980335c.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">具體來說:</p><ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打開標準輸出應(yīng)該重定向到的文件亦歉,發(fā)生在執(zhí)行 shell 代碼的子進程中,在 exec 之前</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.49295774647887325" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbGYKQXmvPBtuiaTf5pwpUHq0Kezy5D2CnDJ5edlibBfNVNsbQPib4So6Mg/640?wx_fmt=png" data-type="png" data-w="1065" style="display: block; margin-right: auto; margin-left: auto; width: 632px !important; height: 312.563px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-ebfa5f9d551ad5ea.png" crossorigin="anonymous" alt="圖片"></p></li><li><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">調(diào)用<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">dup2(4,1)</code>畅哑,導(dǎo)致fd=1 (stdout) 引用 fd=4 指向的磁盤文件肴楷,注意引用計數(shù)的變化</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.4885215794306703" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbxQJZwupQzLeNsMHxZLFicDbMhg3BfMbEOLCBPrVPcIibyp54wxQMEwhw/640?wx_fmt=png" data-type="png" data-w="1089" style="display: block; margin-right: auto; margin-left: auto; width: 632px !important; height: 309.769px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-96172b2e47da69ae.png" crossorigin="anonymous" alt="圖片"></p></li></ol><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">4.1 I/O重定向的例子</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.6266318537859008" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHb9S3UISw6uvumibx3mhQKH1gzicpAhicv6fOHW9UwXD50vGB8E41cmYO0Q/640?wx_fmt=png" data-type="png" data-w="1149" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 412.444px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-4c0e10ca1ff51bbf.png" crossorigin="anonymous" alt="圖片"></p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">4.2 進程控制和I/O</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.7203463203463204" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHb7SVKTqQichHqeGd7iaTlVayB9mdmGCF3Dh6W49MOKiakX7OiaaNJrvAuCw/640?wx_fmt=png" data-type="png" data-w="1155" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 473.827px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-5055dda980af4293.png" crossorigin="anonymous" alt="圖片"></p><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">5. 標準I/O</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">C標準庫中包含一系列高層的標準 IO 函數(shù),比如</p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><p>打開和關(guān)閉文件: <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fopen</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fclose</code></p></li><li><p>讀取和寫入字節(jié): <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fread</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fwrite</code></p></li><li><p>讀取和寫入行: <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fgets</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fputs</code></p></li><li><p>格式化讀取和寫入: <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fscanf</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fprintf</code></p></li></ul><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">標準 IO 會用流的形式打開文件荠呐,所謂流(stream)實際是文件描述符和緩沖區(qū)(buffer)在內(nèi)存中的抽象赛蔫。C 程序一般以三個流開始,如下所示:</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">#include <stdio.h>
extern FILE stdin; // 標準輸入 descriptor 0
extern FILE stdout; // 標準輸出 descriptor 1
extern FILE stderr; // 標準錯誤 descriptor 2
int main()
{
fprintf(stdout, "Hello, Da Wang\n");
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">用緩沖區(qū)的原因泥张,程序經(jīng)常會一次讀/寫一個字符呵恢,比如 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">getc</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">putc</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">ungetc</code>,同時也會一次讀/寫一行媚创,比如 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">gets</code>, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">fgets</code>渗钉。如果用 Unix I/O 的方式來進行調(diào)用是非常昂貴的, <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">read</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">write</code> 因需內(nèi)核調(diào)用钞钙,需大于10000 個時鐘周期鳄橘。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">辦法是用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">read</code> 一次讀取一塊數(shù)據(jù)粤剧,再由高層的用戶輸入函數(shù),一次從緩沖區(qū)讀取一個字符(當緩沖區(qū)用完的時候需要重新填充)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.06766917293233082" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbEv1GBQhibH8VEvZIBLxUdYylJVZ6dYL952picdzYzo8gZLwzAyFHhsqQ/640?wx_fmt=png" data-type="png" data-w="931" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 46.3233px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-fb8365451924f586.png" crossorigin="anonymous" alt="圖片"></p><h2 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;">5.1 標準I/O中的緩沖</h2><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">標準I/O函數(shù)使用緩沖的I/O挥唠,緩沖區(qū)通過<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">\n</code>刷新到輸出fd抵恋,調(diào)用fflush或exit,或從main返回</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.7766990291262136" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbRbCNeiaa0zKM5uMZXgySdlADiajU0jcmd3AKIKOBAG0e5lGaWEugBb3g/640?wx_fmt=png" data-type="png" data-w="618" style="display: block; margin-right: auto; margin-left: auto; width: 618px !important; height: 480.447px !important;" _width="618px" src="https://upload-images.jianshu.io/upload_images/7483640-e3f748858b8f1f68.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可通過strace程序看緩沖如何起作用</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.4075091575091575" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbB6DbScHUWztGHTkHXm4Y39d4KJicalsa7YsktO7kFQFaVzBJbkyiaialg/640?wx_fmt=png" data-type="png" data-w="1092" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 268.918px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-b11df719537778fd.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="1.4473282442748092" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbVqaIKlViaSSP1E67nS8sHoQmiaTmmYF04Ylh2flY0jrzSgLtGQ7axCGQ/640?wx_fmt=png" data-type="png" data-w="655" style="display: block; margin-right: auto; margin-left: auto; width: 655px !important; height: 947.105px !important;" _width="655px" src="https://upload-images.jianshu.io/upload_images/7483640-7d5942efae44c5cc.png" crossorigin="anonymous" alt="圖片"></p><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">6. RIO</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">RIO和C準庫IO庫是在Unix I/O上構(gòu)建的兩個不兼容的庫宝磨,RIO提供了方便高效的IO訪問弧关,可以從一個描述符中讀二進制非緩存輸入和輸出、緩存的文本行和二進制數(shù)據(jù)(線程安全可在同一描述符上任意交叉使用)唤锉。有兩類輸入輸出函數(shù):</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.3231552162849873" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbiakaeVTr1AicEFV0tRE30YbJxcqkNAZ1w6nIVR1WnC9aX6fYeiaL8BURA/640?wx_fmt=png" data-type="png" data-w="1179" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 213.667px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-0bafec69a4c6c194.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>① 無緩沖輸入輸出:和Unix的read/write接口相同世囊,對網(wǎng)絡(luò)傳輸數(shù)據(jù)尤其有用</strong></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">對于同一個描述表,可以任意的交錯調(diào)用rio_readn和rio_wiriten</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">/
rio_readn - Robustly read n bytes (unbuffered)
/
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) / Interrupted by sig handler return /
nread = 0;/ and call read() again /
else
return -1;/ errno set by read() /
}
else if (nread == 0)
break;/ EOF /
nleft -= nread;
bufp += nread;
}
return (n - nleft);/ Return >= 0 */
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">從上面的代碼不難看出窿祥,如果程序的信號處理程序返回中斷株憾,這個函數(shù)會手動重啟read或者write。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>② 帶緩沖的輸入函數(shù)</strong></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">這個函數(shù)有一個好處是晒衩,它從內(nèi)部讀緩沖區(qū)拷貝的一行嗤瞎,能有效地從部分緩存在內(nèi)部內(nèi)存緩沖區(qū)中的文件中讀取文本行和二進制數(shù)據(jù),當緩沖區(qū)為空的時候听系,自動調(diào)用read填滿緩沖區(qū)贝奇,效率很高。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);</code>靠胜,rio_readlineb從文件fd中讀取最多maxlen字節(jié)的文本行掉瞳,并將其存儲在usrbuf中,當讀到maxlen字節(jié)浪漠、EOF發(fā)生陕习、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">\n</code>發(fā)生時停止。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">ssize_t rio_readnb(rio_t *rp, void usrbuf, size_t n);</code>址愿,rio_readnb從文件fd讀取最多n個字節(jié)该镣,當讀到n個字節(jié)、EOF發(fā)生時停止必盖,對rio_readlineb和rio_readnb的調(diào)用可以在同一個描述符上任意交錯</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">緩存IO實現(xiàn)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">讀文件時拌牲,文件有關(guān)聯(lián)的緩沖區(qū)來保存已經(jīng)從文件中讀取但還沒有被用戶代碼讀取的字節(jié)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.43993231810490696" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbdHjhMJmZ0DbaVTs8sCibUv5c5kicK2GLKJGeuu9MxAz9yB731ZvhfFFA/640?wx_fmt=png" data-type="png" data-w="1182" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 290.156px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-711d5e0325f5b6dd.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">緩存IO聲明</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">typedef struct {
int rio_fd;/ descriptor for this internal buf /
int rio_cnt;/ unread bytes in internal buf */
char rio_bufptr;/ next unread byte in internal buf /
char rio_buf[RIO_BUFSIZE]; / internal buffer */
} rio_t;</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.19401544401544402" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbksVbkbvObQwyXBa6LWVDrQz2NtsgibtbWXacAv89Tva4R6rebBc2vNQ/640?wx_fmt=png" data-type="png" data-w="1036" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 129.08px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-7ed82404416c3406.png" crossorigin="anonymous" alt="圖片"></p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">#include "csapp.h"
#define MLINE 1024
int main(int argc, char *argv[])
{
rio_t rio;
char buf[MLINE];
int infd = STDIN_FILENO;
ssize_t nread = 0;
if (argc == 2) {
infd = Open(argv[1], O_RDONLY, 0);
}
Rio_readinitb(&rio, infd);
while((nread = Rio_readlineb(&rio, buf, MLINE)) != 0)
Rio_writen(STDOUT_FILENO, buf, nread);
exit(0);
}</code></pre><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">復(fù)制文件到標準輸出,用mmap加載整個文件</p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;padding: 15px;background-color: rgb(240, 240, 240);overflow-x: scroll;">#include "csapp.h"
int main(int argc, char **argv)
{
struct stat stat;
if (argc != 2) exit(1);
int infd = Open(argv[1], O_RDONLY, 0);
Fstat(infd, &stat);
size_t size = stat.st_size;
char *bufp = Mmap(NULL, size, PROT_READ,MAP_PRIVATE, infd, 0);
Write(1, bufp, size);
exit(0);
}</code></pre><h1 style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;">7. 總結(jié)</h1><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Unix I/O 是最底層的歌粥,通過系統(tǒng)調(diào)用進行操作塌忽,C的標準I/O庫建立在此之上,對應(yīng)的函數(shù)為:</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.31191885038038886" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbL6ibAvlYibIRwNPXMqkUHxQy7zMmRG7jyESJBOkr859TLr01wlKlCXCg/640?wx_fmt=png" data-type="png" data-w="1183" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 206.307px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-67a5a863b5eb3207.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">###</p><table><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: center;background-color: rgb(240, 240, 240);min-width: 85px;">I/O</th><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: center;background-color: rgb(240, 240, 240);min-width: 85px;">優(yōu)點</th><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: center;background-color: rgb(240, 240, 240);min-width: 85px;">缺點</th></tr></thead><tbody style="border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">Unix I/O</td><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">最通用最底層的I/O方法失驶,異步信號安全土居,即可在信號處理器中調(diào)用,提供訪問文件元數(shù)據(jù)的功能</td><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">底層和基礎(chǔ),故需處理的情況多且易錯擦耀,高效率的讀寫需緩沖區(qū)棉圈,也易錯,這是標準I/o要解決的問題</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">標準I/O</td><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">緩沖通過減少讀和寫系統(tǒng)調(diào)用的數(shù)量來提高效率眷蜓,短計數(shù)即獲得的字符沒達到SIZE的問題</td><td style="border-color: rgb(204, 204, 204);text-align: center;min-width: 85px;">不提供文件元數(shù)據(jù)訪問功能分瘾,標準I/O非異步信號安全,不適合信號處理和網(wǎng)絡(luò)套接字輸入/出</td></tr></tbody></table><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">選擇I/O函數(shù)</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通用規(guī)則:盡量用高層的I/O函數(shù)吁系,但理解用的函數(shù)德召。當用磁盤或終端文件時,用<strong>標準I/O</strong>汽纤,在信號處理程序內(nèi)部上岗,極少數(shù)需要最高性能時用<strong>Unix I/O</strong></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.691470054446461" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHbpibRHzYseTjzC83ia3uKCzv9GTia5b6iaK5EslzmojWwyyNzCYdVfws47g/640?wx_fmt=png" data-type="png" data-w="1102" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 454.913px !important;" _width="677px" class="img_loading" src="https://upload-images.jianshu.io/upload_images/7483640-c9329e99f437a844.png" crossorigin="anonymous" alt="圖片"></p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">處理二進制文件</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">任意字節(jié)序列丑蛤,包含0x00的字節(jié)的二進制文件捌刮,永遠不要用面向文本的I/O遵堵,如fgets衡蚂、scanf等,而使用rio_readn或rio_readnb兵怯,以及字符串函數(shù)峭范,如strlen路召、strcpy续室、strcat等栋烤。</p><p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img img_loading" data-ratio="0.5461847389558233" data-src="https://mmbiz.qpic.cn/mmbiz_png/HCeAMgNq7tsusnFPmdYNQXYGADOEAfHb7SwZ99QUhLWU60kZceIqbyCdPs8UUuqzmq9blpyicYWVMfk2KUIxiapA/640?wx_fmt=png" data-type="png" data-w="996" style="display: block; margin-right: auto; margin-left: auto; width: 657px !important; height: 359.751px !important;" _width="677px" src="https://upload-images.jianshu.io/upload_images/7483640-e2c165ce6d8c386e.png" crossorigin="anonymous" alt="圖片"></p></section><blockquote><p>本文使用 <a href="http://www.reibang.com/p/5709df6fb58d" class="internal">文章同步助手</a> 同步</p></blockquote>
csapp之第10章:系統(tǒng)級I?O
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
- 文/潘曉璐 我一進店門亏推,熙熙樓的掌柜王于貴愁眉苦臉地迎上來学赛,“玉大人,你說我怎么就攤上這事吞杭≌到剑” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵芽狗,是天一觀的道長绢掰。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么滴劲? 我笑而不...
- 正文 為了忘掉前任攻晒,我火速辦了婚禮,結(jié)果婚禮上班挖,老公的妹妹穿的比我還像新娘鲁捏。我一直安慰自己,他們只是感情好萧芙,可當我...
- 文/花漫 我一把揭開白布碴萧。 她就那樣靜靜地躺著,像睡著了一般末购。 火紅的嫁衣襯著肌膚如雪破喻。 梳的紋絲不亂的頭發(fā)上,一...
- 文/蒼蘭香墨 我猛地睜開眼宅静,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了站欺?” 一聲冷哼從身側(cè)響起姨夹,我...
- 正文 年R本政府宣布驶忌,位于F島的核電站矛辕,受9級特大地震影響笑跛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聊品,卻給世界環(huán)境...
- 文/蒙蒙 一飞蹂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翻屈,春花似錦陈哑、人聲如沸。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厘贼,卻和暖如春界酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嘴秸。 一陣腳步聲響...
推薦閱讀更多精彩內(nèi)容
- 輸入/輸出(I/O)是在主存和外部設(shè)備之間拷貝數(shù)據(jù)的過程。 是主存纲酗,和外部設(shè)備(磁盤衰腌、終端、網(wǎng)絡(luò))之間的耕姊。 主存是...
- 3.1 引言 本章開始討論UNIX系統(tǒng)桶唐,先說明可用的文件I/O函數(shù)——打開文 件、讀文件茉兰、寫文件等。UNIX系統(tǒng)中...
- 文件描述符 非負整數(shù)欣簇,變化范圍(0~OPEN_MAX-1规脸,或許是63?) 取得描述符 #include<fcntl...
- 姓名:鄭煜爍 學(xué)號:19029100010 學(xué)院:電子工程學(xué)院 轉(zhuǎn)自:https://blog.csdn.net/...