打开文件。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有什么区别? 
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小时