打開文件。html
應用程序向內核發出請求→要求內核打開相應的文件→內核返回文件描述符git
關閉文件。shell
1.open函數緩存
(1)函數定義:安全
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(char *filename, int flags, mode_t mode);
(2)參數解析:網絡
flags:指明進程打算如何訪問這個文件,主要參數:數據結構
O_RDONLY:只讀
O_WRONLY:只寫
O_RDWR:可讀可寫app
O_CREAT:文件不存在,就建立新文件
O_TRUNC:若是文件存在,就截斷它
O_APPEND:寫操做前設置文件位置到結尾處
這些值能夠用或鏈接起來。
mode:指定了新文件的訪問權限位,符號名稱以下:
socket
讀和寫文件
1.讀 read函數
(1)函數原型:
#include <unistd.h> ssize_t read(int fd, void *buf, size_t n);
(2)參數:
返回值:成功則返回讀的字節數,EOF返回0,出錯返回-1。返回值爲有符號數。
fd:文件描述符
buf:存儲器位置
n:最多從當前文件位置拷貝n個字節到存儲器位置buf
2.寫 write
(1)函數原型:
#include <unistd.h> ssize_t write(int fd, void *buf, size_t n);
(2)參數:
返回值:成功則返回寫的字節數,出錯返回-1。返回值爲有符號數。
fd:文件描述符
buf:存儲器位置
n:最多從存儲器位置buf拷貝n個字節到當前文件位置
PS:read和write在正常狀況下返回值是實際傳送的字節數量。
3.經過lseek函數能夠顯式的修改當前文件的位置。
> ssize_t和size_t有什麼區別? ![](http://images2017.cnblogs.com/blog/1071510/201712/1071510-20171217231718421-1075649569.png)
4.不足值
在某些狀況下,read和write傳送的字節比應用程序要求的要少,緣由以下:
讀和寫socket
RIO,Robust I/O,自動處理不足值。
1.RIO的無緩衝的輸入輸出函數。
這些函數的做用是直接在存儲器和文件之間傳送數據,常適用於網絡和二進制數據之間。
rio_readn函數和rio_writen定義:
#include "csapp.h" ssize_t rio_readn(int fd, void *usrbuf, size_t n); ssize_t rio_writen(int fd, void *usrbuf, size_t n);
參數:
fd:文件描述符
usrbuf:存儲器位置
n:傳送的字節數
返回值:
rio_readn成功則返回傳送的字節數,EOF爲0(一個不足值),出錯爲-1
rio_writen成功則返回傳送的字節數,出錯爲-1,沒有不足值。
2.RIO的帶緩衝的輸入函數
能夠高效的從文件中讀取文本行和二進制數據。
一個文本行就是一個由換行符結尾的ASCII碼字符序列。
使用RIO函數實現一次一行的從標準輸入複製一個文本文件到標準輸出。
#include "csapp.h" int main(int argc, char **argv) { int n; rio_t rio; char buf[MAXLINE]; Rio_readinitb(&rio, STDIN_FILENO);//鏈接標準輸入和rio地址 while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //當成功返回時,將rio中的內容拷貝到存儲器位置buf中,最多讀maxline-1 Rio_writen(STDOUT_FILENO, buf, n);//把存儲器位置中的數據拷貝到標註輸出中。 exit(0); }
讀緩衝區格式,並初始化其rio_readinitb函數。
#define RIO_BUFSIZE 8192 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; void rio_readinitb(rio_t *rp, int fd) { rp->rio_fd = fd; rp->rio_cnt = 0; rp->rio_bufptr = rp->rio_buf; }
RIO讀程序的核心是rio_read函數
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { int cnt; while (rp->rio_cnt <= 0) { /* 若是緩存區爲空,調用read填滿它 */ rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); if (rp->rio_cnt < 0) { if (errno != EINTR) /* 出錯返回-1*/ return -1; } else if (rp->rio_cnt == 0) /* EOF返回0 */ return 0; else rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */ } /* 一旦緩存區非空,就從讀緩存區拷貝n和rp->rio_cnt中較小值個字節到用戶緩存區,而且返回拷貝的字節數 */ cnt = n; if (rp->rio_cnt < n) cnt = rp->rio_cnt; memcpy(usrbuf, rp->rio_bufptr, cnt); rp->rio_bufptr += cnt; rp->rio_cnt -= cnt; return cnt; }
rio_readnb函數
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { size_t nleft = n; ssize_t nread; char *bufp = usrbuf; while (nleft > 0) { if ((nread = rio_read(rp, bufp, nleft)) < 0) { if (errno == EINTR) nread = 0; /* 調用read填充 */ else return -1; /* 錯誤,返回-1 */ } else if (nread == 0) break; /* EOF */ nleft -= nread; bufp += nread; } return (n - nleft); /* 返回成功傳送的字節數*/ }
rio_readlineb函數
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { int n, rc; char c, *bufp = usrbuf; for (n = 1; n < maxlen; n++) { //最可能是maxlen-1個 if ((rc = rio_read(rp, &c, 1)) == 1) { *bufp++ = c; if (c == '\n')//找到換行符,就退出 break; } else if (rc == 0) { if (n == 1) return 0; /* EOF,而且沒有讀到數據 */ else break; /* EOF,有數據,出現不足值 */ } else return -1; /* 錯誤,返回-1 */ } *bufp = 0; return n;//返回成功傳送的字節數 }
stat須要輸入文件名,而fstat須要輸入的是文件描述符。
在stat數據結構中須要注意的有兩個變量,st_mode和st_size。
st_size:包含文件的字節數大小
st_mode:包編碼文件訪問許可位和文件類型。
許可位在Unix文件類型以下,有對應的宏指令,含義均爲「是xx嗎」,這些宏在sys/stat.h中定義:
類型 | 做用 | 函數 |
---|---|---|
普通文件 | 二進制或文本文件(對內核沒差) | S_ISREG() |
目錄文件 | 關於其餘文件的信息 | S_ISDIR() |
套接字 | 經過網絡與其餘進程通訊的文件 | S_ISSOCK() |
#include "csapp.h" int main (int argc, char **argv) { struct stat stat; char *type, *readok; Stat(argv[1], &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)) /* 檢查閱讀權限 */ readok = "yes"; else readok = "no"; printf("type: %s, read: %s\n", type, readok); exit(0); }
I/O重定向操做符命令:ls > foo.txt
使外殼加載和執行ls程序,而且將標準輸出重定向到磁盤文件foo.txt。
I/O重定向函數: dup2
函數定義爲:
#include <unistd.h> int dup2(int oldfd, int newfd); 返回值:成功返回描述符,錯誤返回-1
這個函數執行的操做是,拷貝描述符表表項oldfd,覆蓋描述表表項newfd,若是後者被打開,則在拷貝前關閉它。
ANSI C定義了一組高級輸入輸出函數,稱爲標準I/O庫,包含:
fopen、fclose,打開和關閉文件
fread、fwrite,讀和寫字節
fgets、fputs,讀和寫字符串
scanf、printf,複雜的格式化的I/O函數
標準I/O庫將一個打開的文件模型化爲一個流。
每一個ANSI C程序開始的時候都有三個打開的流:stdin、stdout、stderr,對應於標準輸入、標準輸出和標準錯誤,定義以下:
#include <stdio.h> extern FILE *stdin; extern FILE *stdout; extern FILE *stderr;
網絡套接字上最好不要使用標準I/O函數,而是使用RIO函數,緣由:
若是沒有清楚緩存區,輸入函數後面不能接輸出函數,輸出函數後面也不能接輸入函數,而對套接字使用lseek是非法的,打開兩個流有很麻煩,因此!在網絡套接字上不要使用標準I/O函數來進行輸入和輸出!
(1)Unix風格
遇到錯誤後返回-1,而且將全局變量errno設置爲指明錯誤緣由的錯誤代碼;
若是成功完成,就返回有用的結果。
(2)Posix風格
返回0表示成功,返回非0表示失敗;
有用的結果在傳進來的函數參數中。
(3)DNS風格
有兩個函數,gethostbyname和gethostbyaddr,失敗時返回NULL指針,並設置全局變量h_errno。
(4)錯誤報告函數
void unix_error(char *msg) /* unix-style error */ { fprintf(stderr, "%s: %s\n", msg, strerror(errno)); exit(0); } /* $end unixerror */ void posix_error(int code, char *msg) /* posix-style error */ { fprintf(stderr, "%s: %s\n", msg, strerror(code)); exit(0); } void dns_error(char *msg) /* dns-style error */ { fprintf(stderr, "%s: DNS error %d\n", msg, h_errno); exit(0); } void app_error(char *msg) /* application error */ { fprintf(stderr, "%s\n", msg); exit(0); }
2.錯誤處理包裝函數
成功時返回void,返回錯誤時包裝函數打印一條信息,而後退出。
void Kill(pid_t pid, int signum) { int rc; if ((rc = kill(pid, signum)) < 0) unix_error("Kill error"); }
成功時返回void,錯誤返回碼中不會包含有用的結果。
void Pthread_detach(pthread_t tid) { int rc; if ((rc = pthread_detach(tid)) != 0) posix_error(rc, "Pthread_detach error"); }
struct hostent *Gethostbyname(const char *name) { struct hostent *p; if ((p = gethostbyname(name)) == NULL) dns_error("Gethostbyname error"); return p; }
調用dup2(5,0)
/dup2(5,STDIN_FILENO)
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 10/10 | |
第二週 | 63/63 | 1/2 | 8/18 | |
第三週 | 31/94 | 1/3 | 18/36 | |
第四周 | 265/329 | 1/4 | 17/53 | |
第五週 | 106/435 | 2/6 | 18/71 | |
第六週 | 211/646 | 2/8 | 21/92 | |
第七週 | 1420/2066 | 2/10 | 17/109 | |
第八週 | 1061/3127 | 1/11 | 17/126 | |
第九周 | 1458/4585 | 3/14 | 20/146 | |
第十週 | 1410/5995 | 1/15 | 20/166 | |
第十一週 | 779/6774 | 2/17 | 18/184 | |
第十三週 | 326/7100 | 2/19 | 20/204 |
嘗試一下記錄「計劃學習時間」和「實際學習時間」,到期末看看能不能改進本身的計劃能力。這個工做學習中很重要,也頗有用。
耗時估計的公式
:Y=X+X/N ,Y=X-X/N,訓練次數多了,X、Y就接近了。
計劃學習時間:20小時
實際學習時間:20小時