贯穿文件IO的是文件描述符,而贯穿标准IO的是FILE指针(也可以说是流)
本章内容:
文件描述符的概念
文件IO的操作:open close read write lseek
文件IO与标准IO的区别
IO的效率问题
文件共享问题
原子操作
程序中得到重定向:dup和dup2
同步:sync、fsync
fcntl();
ioctl();
/dev/fd/目录
见P60
理清PCB进程控制块,进程表项,文件描述符,文件表项,V节点,i节点之间的关系
open需要注意的是:
1、要包含
#include
#include
#include
2、open的时候,与标准IO的打开方式的联系为:
其他的以此类推!
3、open函数的函数原型在man有两种形式,一种是有两个参数,一个是有三个参数。
当用open创建一个文件时,就要使用三个参数的原型形式,第三个参数来确定创建的文件的权限。
(这里需要注意的是,C中没有函数重载,这里的open是变参函数!变参函数可以传多个参数,没有固定。常见的printf等都是变参函数)
第三个参数依然是与umask相结合从而确立所创建文件的权限!
read/write/lseek
//mycpy.c
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 1024
//代码说明
// copy 一个文件到另外一个文件
int main(int argc,char **argv)
{
int sfd,dfd;
int len;
int ret;
int pos;
char * buf[BUFFSIZE];
if(argc<3)
{
fprintf(stderr,"Uage:too less arg");
exit(1);
}
sfd=open(argv[1],O_RDONLY);
if(sfd<0)
{
perror("open error1");
exit(1);
}
dfd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600);
if(dfd<0)
{
close(sfd); //注意这个地方!!!
perror("open error");
exit(1);
}
while(1)
{
len =read(sfd,buf,BUFFSIZE);
if(len<0)
{
perror("read error");
break;
}
if(len==0)
{
break;
}
pos=0;
while(len>0) //防止写的字节小于读到的字节数,解决方法,继续读!
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write error");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(dfd);
close(sfd);
exit(0);
}
区别:响应速度上(文件IO快),吞吐量上(标准IO大)
一个问题:如何使一个程序变快?
两者区别主要是缓冲区的存在!!
尽量使用标准IO!!
标准IO与文件IO两者不可混用!
补充两个函数:fileno和fdopen,可以将流和文件描述符相互转换。即使可以相互转换,但是两者还是不能混用!
一个小实验:
在执行的程序前加一个time,可以得到程序运行的时间。第一行是真实时间,第二行是在用户区执行的时间,第三行是系统调用花的时间。
实验:
在mycpy.c程序中更换缓冲区buf的大小,BUFSIZE从128到16M,依次增大2倍,做出一张表,来查看系统所消耗的时间的不同!找到性能最大的拐点以及BUFSIZE多大程序会出问题(段错误)。
文件共享指的是:多个任务共同操作一个文件或者协同完成任务。
面试:写程序删除一个文件的第十行。
思路1:在同一个程序中找那个打开一个文件两次,得到两个不一样的文件描述符,分别定位到11行和10行。然后进行循环读写(读写操作会自动更新文件的偏移量)。
这里需要注意的是,一个文件打开一个文件两次,得到的两个文件描述符指向的文件表项是不一样的,也就是说,他们有各自的文件偏移量,不会共享(这是与dup的区别);但是因为打开的是同一个文件,他们的指向的V节点一定是一样的。(详见 P61)
思路二:多进程和多线程
补充函数:truncate和ftruncate,进行文件的截断。
dup和dup2
第一个需要注意的是,这是赋值文件描述符的两个函数,复制后,两个文件描述符的指向的文件表项是相同的,也就是说共享文件偏移量。
第二个需要注意的是,dup2的一些小细节,见man dup2
写程序,注意三个地方
1 特别注意内存泄漏
2 特别注意数据溢出、越界
3 要有宏观编程思想,把main函数当做是编写模块。因此编写完要使使用到的资源回归到原来的位置(最简单的例子是,程序中我们自行关闭的标准输入输出,要重新打开)。
同步内核层面的缓存和磁盘。注意这里是指的是内核层面的。与fflush是不一样的。
管家函数,参数不一样,返回值也不一样
文件描述符所变得魔术几乎都来源于该函数
设备相关的内容
(一切皆文件设计思想的牺牲者)
是一个虚目录,显示的是当前进程的文件描述符信息,谁打开这个目录,看到的就是这个进程的文件描述符信息。
比如:
ls -l /dev/fd ,得到的就是ls的这个进程的文件描述符信息;
在程序中打开这个文件,就会得到程序运行起来的这个进程的。