open、creat、write、close、lseek等文件操作函数详解
程序员文章站
2024-02-26 14:44:04
...
首先我们回忆一下,stdin&stdout&stderr
C默认会打开三个输出输入流,分别是stdin,stdout,stderr。且这三个流的类型都是FILE*,fopen返回值类型,文件指针
文件操作
文件操作的一般过程:
打开文件,打开成功后,应用程序将获得文件描述符;
应用程序使用文件描述符对文件进行读写等操作;
全部操作完毕后,应用程序需要将文件关闭以释放用于管理打开文件的内存;
一、open和openat函数:系统调用可以打开或创建一个文件
1、看一下open函数:
(图中create函数在下面讲解)
参数说明:
pathname:指向欲打开的文件路径字符串
flags :打开文件时,可以传入多个参数选项,用下面的一个或者多个常量(只列出了一部分)进行“或”运算,构成flags.(如下)
注:open函数具体使用那个,和具体场景有关。比如,目标文件不存在,需要open创建,则第三个参数表示文件的默认权限(默认权限请看文章第三点对umask的介绍点击打开链接)。
否则就使用两个参数即可。
返回值:
成功:新打开的文件描述符
失败:-1
2、看一下openat函数
可以看出来,参数dirfd将open函数和openat函数区分开来,共有三种可能性:
(1)path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat函数相当于open函数
(2)path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。
fd参数是通过打开相对路径名所在的目录来获取。
(3)path参数指定了相对路径名,fd参数具有特殊值AF_FDCWD。在这种情况下,路径名在当前工作目录中获取,openat函数在操作上与open函数类似。
所以,openat函数是希望解决两个问题:
(1)让线程可以使用相对路径名打开目录中的文件
(2)可以避免time-of-check-of-use(TOCTTOU)错误。
二、函数creat-创建一个新文件
见上图open。
此函数等效于:
open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);因为creat函数是以只写方式打开所创建的文件。
三、read函数:系统调用从打开的文件中读取数据
如下:
参数:
fd:要读取的文件的描述符。
buf:读取到的数据要放入的缓冲区。
count:要读取的字节数。
返回值:
若成功返回读到的字节数,若已到文件结尾则返回0
若出错则返回-1并设置变量errno的值。
(注意:1. 这里的size_t是无符号整型,ssize_t是有符号整型。2. buf指向的内存空间必须事先分配好)
会由多种情况使实际读到的字节数少于要求读的字节数:
(1)读普通文件时,在要求读到的字节数之前已达到了文件尾端。
(2)当从终端设备读时,通常一次只能读一行
(3)当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数。
(4)当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read只能返回实际可用的字节数。
(5)当从某些面向记录的设备(如磁带),一次最多返回一个记录。
四、write:系统调用向打开的文件写数据
如下:
参数:
fd:要写入的文件的描述符。
buf:要写入的数据所存放的缓冲区。
count:要写入的字节数。
返回值:
若成功返回已写的字节数
出错则返回-1并设置变量errno的值
write出错的一个常见原因是磁盘已写满,或者超出了一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前偏移量处开始。如果在打开该文件时,指点了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处,在一次成功写之后,该文件偏移量增加实际写的字节数。
五、close:系统调用关闭一个打开的文件。
参数:
fd:要关闭的文件的描述符。
返回值:若成功返回0
出错则返回-1
注:当一个进程终止时,内核会自动关闭它所有打开的文件
六、lseek:系统调用可以改变文件偏移量(File Offset)。
文件偏移量是一个整数,表示距文件起始处的字节数
参数whence必须是以下三个常量之一:
SEEK_SET:将文件偏移量设置在距文件开始处offset个字节。
SEEK_CUR:将文件偏移量设置在其当前值加offset,offset可正可负。
SEEK_END:将文件偏移量设置为文件长度加offset,offset可正可负。
若成功,则返回新的文件偏移量。若出错,返回-1。
注意:通常,文件的当前偏移量应当是一个非负整数,但是,某些设备也可能允许负的偏移量。但对于普通文件,其偏移量必须是非负值。
因此在比较lseek的返回值时不要测试其是否小于0,而要测试它是否等于-1。
lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作。然后,该偏移量用于下一个读或写操作。
文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被读为0。
文件中的空洞并不要求在磁盘上占用存储区。
eg:
1、hellow.c 写文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7
8 int main(){
9 umask(0);
10 int fd=open("myfife",O_WRONLY|O_CREAT,0644);
11 if(fd<0){
12 perror("open");
13 return 1;
14 }
15
16 int count=5;
17 const char *msg="hello bit!\n";
18 int len=strlen(msg);
19
20 while(count--){
21 write(fd,msg,len);
22 }
24 close(fd);
25 return 0;
26 }
结果,新建了文件myfife且写入成功
2、hellor.c读文件
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/stat.h>
4 #include<fcntl.h>
5 #include<unistd.h>
6 #include<string.h>
7
8 int main(){
9 int fd=open("myfife",O_RDONLY);
10 if(fd<0){
11 perror("open");
12 return 1;
13 }
14
15 const char *msg="hello bit!\n";
16 char buf[1024];
17
18 while(1){
19 ssize_t s=read(fd,buf,strlen(msg));
20 if(s>0){
21 printf("%s",buf);
22 }else{
23 break;
24 }
25 }
26
27 close(fd);
28 return 0;
29 }
结果:将刚写入的文件读出: