开宗明义
面对fy姥爷的考试 做一些准备 以防备措手不及
概念了解
文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文字描述符是一个小的非负整数,内核用以标识一个特定进程正在存访的文件。当内核打开一个现存文件或创建一个新文件时,他就返回一个文件描述符。当读,写一个文件时,用open或create返回的文件描述符标示该文件,将其作为参数传送给read或者write。
标准输入,标准输出,标准出错
按惯例,每当运行一个新程序时,所有的shell都为其打开三个文件描述符:标准输入,标准输出,标准出错。如果像简单命令ls那样没有做什么特殊处理,则这三个描述符都连向终端。大多数shell都提供一种方法,使任何一个或所有这三个描述符都能重新定向到某一个文件,例如:
ls > file.list
执行ls命令,其标准输出重新定向到名为file.list的文件上。
不用缓存的I/O
函数open,read,write,lseek以及close提供了不用缓存的I/O。这些函数都用文件描述符进行工作;
实例:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
int fd;
char buf[1000];
if((fd=open("my_data.dat",O_RDONLY))<0){
//O_RDONLY只读配置
printf("Open file fail\n");
return 0;
}
read(fd,buf,sizeof(buf));
printf("%s\n",buf);
close(fd);
return 0;
}
//输出:
Hello World!I am here!
(空行)
read函数返回读得的字节数,此值用作要写的字节数。当到达文件的尾端时,read返回0,程序停止,如果发生了一个读错误,read返回-1。出错时大多数系统函数返回-1。
标准I/O
标准I/O函数提供一种对不用缓存的I/O函数的带缓存的界面。使用标准I/O可无需担心如何选取最佳的缓存长度,例如上一程序的sizeof(buf)参数。另一个使用标准I/O函数的有点与处理输入行有关。
open函数
调用open函数可以打开或创建一个文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *pathname,int oflag,.../*,mode_t */);
//返回:若成功为文件描述符 若出错为-1
我们将第三个参数写入…,这是ANSI C说明余下参数的数目和类型可以变化的方法。对于open函数而言,仅当创建新文件时才使用第三个参数.
由open返回的文件描述符一定是最小的未用描述符数字。这一点被很多应用程序用来在标准输入,标准输出或标准出错输出上打开一个新的文件。例如,一个应用程序可以先关闭标准标准输出(通常是文件描述符1),然后打开另一个文件,事先就能了解到该文件一定会在文件描述符1上打开。
close函数
可用close函数关闭一个打开文件:
#include<unistd.h>
int close(int filedes);
关闭一个文件时也释放该进程加在该文件上的所有记录锁。
(记录锁的功能是:当一个进程正在读或者修改文件的某一个部分时,它可以阻止其他进程修改同一文件区)
当一个进程终止时,他所有的打开文件都由内核自动关闭。很多程序都使用这一功能而不显式地用close关闭打开的文件。
lseek函数
每个打开文件都有一个与其相关联的”当前文件位移量”。他是一个非负整数,用以度量从文件开始处计算的字节数。通常,读,写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0。
可以调用lseek显式地地位一个打开文件
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int filedes,off_t offset,int whence);
对参数offset参数的解释与惨数whence的值有关
1. 若whence是SEEK_SET 则将该文件的位移量设置为据文件开始处offset个字节。
2. 若whence是SEEK_CUR 则将该文件的位移量设置为其当前值加offset(offset可正可负)
3. 若whence是SEEK_END 则将该文件的位移量设置为文件长度加offset
若lseek成功执行 则返回新的文件偏移量 为此可以用下列方式确定一个打开文件的当前位移量:
off_t currpos;
currpos = lseek(fd,0,SEEK_CUR);
这种方法也可用来确定所设计的文件是否可以设置位移量。如果文件描述符引用的是一个管道或FIFO 则lseek返回-1 并将error设置为EPIPE。
lseek仅将当前的文件位移量记录在内核内,他并不引起任何I/O操作。然后,该位移量用于下一个读或写操作。
未变化吧位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次将延长该文件,并在文件中构成一个空调,这一点是允许的。位于文件中但没有写过的字节都被读为0。
read函数
用read函数从打开文件中读数据。
#includ<unistd.h>
ssize_t read(int filedes,void buff,size_t nbytes);
//返回:读到的字节数 若已到文件尾0 若出错为-1
如read成功 则返回读到的字节数 如已到达文件的尾端 则返回0。
有多种情况可使实际读到的字节数少于要求读字节数。
读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(){
int fd;
char buf[1000];
if((fd=open("my_data.dat",O_RDONLY))<0){
printf("Open file fail!\n");
return 0;
}
printf("1.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//0
off_t length = lseek(fd,0,SEEK_END);
printf("2.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//23
lseek(fd,0,SEEK_SET);
printf("3.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//0
read(fd,buf,length);
printf("4.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//23
buf[length]='\0';
printf("%s\n",buf);
close(fd);
return 0;
}
I/O效率
如果我们使用read和write函数来复制一个文件,我们应该注意以下几点:
1. 他从标准输入读,写至标准输出,这就假定在执行程序,这些标准输入,输出已由shell安排好。确实,所有常用的shell都提供一种方法,他在标准输入上打开一个文件用于读,在标准输出上创建或重写一个文件。
2. 很多应用程序假定标准输入是文件描述符0,标准输出是文件描述符1。
3. 考虑到进程终止时,unix会关闭所有打开的文件描述符,所以此程序并不关闭输入和输出文件。
4. 程序对文本文件和二进制代码文件都能工作,因为对unix而言,这两种文件并无区别。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(){
int fd;
char buf[1000];
if((fd=open("my_data.dat",O_RDONLY))<0){
printf("Open file fail!\n");
return 0;
}
printf("1.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//0
off_t length = lseek(fd,0,SEEK_END);
printf("2.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//23
lseek(fd,0,SEEK_SET);
printf("3.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//0
read(fd,buf,length);
printf("4.Current offset %ld\n",lseek(fd,0,SEEK_CUR));
//23
buf[length]='\0';
printf("%s\n",buf);
close(fd);
if((fd=open("new_file.dat",O_WRONLY|O_CREAT))<0){
printf("Open for write fail\n");
return 0;
}
write(fd,buf,length);
close(fd);
execl("/bin/cat","cat","new_file.dat",NULL);
return 0;
}