标签:
【学习时间:6小时】
【学习内容:CHAPTER 10——系统级I/O】
【把I/O抽象成文件,其实是把系统内的一切操作都变成对文件(字节序列)的操作;这样极大地简洁了各类动作。上述对文件的描述,其实是输入输出类型的“文件操作”,分别对应的是进行I/O、读写操作。】
打开文件:
fd = Open("文件名",flag位——表示访问方式及额外提示,mode参数);
//出错的时候返回-1
关闭文件:
int close(int fd);//若成功则返回0,不成功则为-1
【这里的打开文件时返回值fd的详细介绍(比如什么是“在进程中当前没有打开的最小描述符”可以参考练习题10.1)】
读函数
ssize_t read(int fd,void *buf,size_t n);//成功则返回n;EOF返回0;出错返回-1
read函数从描述符为fd的当前文件位置拷贝最多n个字节到存储器位置buf。
【什么是EOF?就是给定了m字节大小的文件;在从k字节位置开始读或者写的时候,发现k>=m】
写函数
ssize_t write(int fd,const void *buf,size_t n);
【ssziet,sizet有什么区别?前者被定义为int,有符号;后者被定义成unsigned int,无符号】
无缓冲的输入输出函数。直接在存储器和文件之间传送数据(允许被中断的字节调用,并在必要的时候重启它们)。
ssizet riowriten(int fd,const void *usrbuf,size_t n);
ssizet riowriten(int fd,const void *usrbuf,size_t n);
rio__writen函数遇到EOF的时候返回0;
rio__readn遇到EOF的时候返回不足值(即 不足n的那个部分的字节数)。
原理:函数从内部缓冲区中拷贝一个文本行,当缓冲区变空的时候,会自动地调用read重新填满缓冲区。
每打开一个描述符都会调用一次该函数,它将描述符fd和地址rp处的类型为rio_t的缓冲区联系起来。
从文件rp中最多读n个字节到存储器位置usrbuf。对同一描述符,rioreadnb和rioreadlineb的调用可以交叉进行。
从文件rp中读取一个文本行(包括结尾的换行符),将它拷贝到存储器位置usrbuf,并用空字符来结束这个文本行。
static ssize_t rio_read(rio_t *rp,char *usrbuf,size_t n)
{
int cnt;
while(rp->rio_cnt<=0)//如果缓冲区为空,先调用函数填满缓冲区再读数据
{
rp->rio_cnt=read(rp->rio_fd,rp->rio_buf,sizeof(rp->rio_buf));//调用read函数填满缓冲区
if(rp->rio_cnt<0)//排除文件读不出数据的情况
{
if(error != EINTR)
{
return -1;
}
}
else if(rp->rio_cnt=0)
return 0;
else
rp->rio_bufptr = rp->rio_buf;//更新现在读到的位置
}
cnt=n;
if(rp->rio_cnt<n)
cnt=rp->rio_cnt;//以上三步,将n与rp->rio_cnt中较小的值赋给cnt
memcpy(usrbuf,rp->rio_bufptr,cnt);把读缓冲区的内容拷贝到用户缓冲区
rp->rio_bufptr+=cnt;
rp->rio_cnt-=cnt;
return cnt;
}
函数格式:
#include <unistd.h>
#include <sys/stat.h>
int stat(cost char *filename,struc sta *buf);
int fstat(int fd,struct stat *buf);
#include "csapp.h"
int main()
{
int fd1,fd2;
fd1=Open("foo.txt",O_RDONLY,0);
Close(fd1);
fd2=Open("baz.txt",O_RDONLY,0);
printf("fd2=%d\n",fd2);
exit(0);
}
【unix进程生命周期开始的时候,就已经有了三个打开的描述符:标准输入、标准输出、标准错误,分别为0,1,2。所以fd2的值应该是3】
#include "csapp.h"
int main(void)
{
char c;
while(Read(STDIN_FILENO,&C,1) !=0)
Write(STDOUT_FILENO,&c,1);
exit(0);
}
【每次一个字节,从标准输入传递到标准输出】
#include "csapp.h"
int main()
{
int fd1,fd2;
char c;
fd1=Open("foobar.txt",O_RDONLY,0);
fd2=Open("foobar.txt",O_RDONLY,0);
Read(fd1,&c,1);
Read(fd2,&c,1);
printf("c=%c\n",c);
exit(0);
}
【描述符fd1和fd2都有各自的打开文件表表项,所以有它们各自的文件位置(也就是说互不影响,不会因为fd1先执行就使得fd2打开的文件位置推后)。则fd2打开文件读出的第一个字母还是f。】
#include "csapp.h"
int main()
{
int fd;
char c;
fd=Open("foobar.txt",O_RDONLY,0);
if(Fork()==0)
{
Read(fd,&c,1);
exit(0);
}
Wait(NULL);
Read(fd,&c,1);
printf("c=%c\n",c);
exit(0);
}
【父子进程共享相同的文件表表项,因此依次读取的是“f”和“o”。输出为o。】
本次学习的章节内容较少,然而值得深入挖掘的代码部分也是十分重要。我之间就觉得,读代码是一件很有意思的事情——在逐行推敲之后恍然大悟,好像柳暗花明。本章节的代码设计的都是对“基本操作”的分解(原来简单的输入和输出背后也是这么多有序的步骤);读起来更是饶有趣味。
另外,在使用虚拟机的时候,我发现虚拟机平台提示“硬盘位置改动”导致linux系统不可用;在百度了资料之后,利用虚拟介质管理修复了错误。
标签:
原文地址:http://www.cnblogs.com/lwr-/p/4940746.html