Internet Application 4:O_APPEND到底移不移偏移量

Task 1

Learn about the O_APPEND flag of open(). Please write a program to append one file to another file with open() and lseek().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>

int main(void){
char buf[10];
int fd1,fd2,count=0;

if((fd1 = open("file1",O_RDWR|O_APPEND,0644)) == -1)
printf("Error in opening file1\n");

if((fd2 = open("file2",O_RDWR,0644)) == -1)
printf("Error in opening file2\n");

while(read(fd2,buf,1)!= 0){
write(fd1,buf,1);
count++;
}
printf("file2 has %d char.\n",count);

close(fd1);
close(fd2);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
[email protected]:~/append$ cat > file1
1234567890
abcdefghij
[email protected]:~/append$ cat > file2
##########
@@@@@@@@@@
[email protected]:~/append$ ./append1
file2 has 22 char.
[email protected]:~/append$ cat file1
1234567890
abcdefghij
##########
@@@@@@@@@@

从这个实例看来,如果我们在open()的flag里加上O_APPEND,在写入内容时不需要lseek,就会自动把内容写入到文件末尾,毕竟APPEND本意就是附加嘛。

Task 2

When using O_APPEND to open a file for reading and writing, can we read the file from any location using lseek()? Can we update the data in any part of the file using lseek()? Please write a program to verify your answer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(void){
char buf[10],temp[10];
int fd,offset,count=0,update,a=0;

if((fd = open("file2seek.txt",O_RDWR|O_APPEND)) == -1)
printf("Error in opening file\n");

printf("we will read the rest of one line from the position you point.\n");
printf("please input the position you want to read from the file:");
scanf("%d",&offset);
printf("the new offset is = %d\n",offset);

if(lseek(fd,offset,SEEK_SET)==-1){
printf("lseek error");
exit(1);
}
while(buf[0]!='\n'){
read(fd,buf,1);
temp[a] = buf[0];
a++;
}
temp[a] = '\0';//printf()以'\0'作为输出结束符,如果不强制给它加个结尾,后续打印会出乱码。
printf("%d bytes have been read, the read content is: %s\n",a-1,temp);

printf("we will insert ***** into the position you point\n");
printf("Please input the begining point you want to update the file:");
scanf("%d",&update);
lseek(fd,update,SEEK_SET);
write(fd,"*****",5);

close(fd);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[email protected]:~/append$ vi task2.c
[email protected]:~/append$ gcc task2.c -o task2
[email protected]:~/append$ cat > file2seek.txt
1234567890
abcdefghij
##########
[email protected]:~/append$ ./task2
we will read the rest of one line from the position you point.
please input the position you want to read from the file:18
the new offset is = 18
3 bytes have been read, the read content is: hij

we will insert ***** into the position you point
Please input the begining point you want to update the file:12
[email protected]:~/append$ cat file2seek.txt
1234567890
abcdefghij
##########
*****[email protected]:~/append$

O_APPEND的含义:

第二个task让我们自己验证,如果使用了O_APPEND:是否可以利用lseek从任意位置read?是否可以用lseek任意write?
从运行结果可以看出,此时可以利用lseek从任意位置read,但不能write。这看起来也很合理,附加只允许附加到结尾,中间的插入是无效的,但这不影响读。

O_APPEND的原理:

1)有人觉得:写了O_APPEND会在一开始把文件中的指针移到了文件尾,以方便从文件尾添加数据。我觉得显然行不通,第二个实例中read前面的lseek()显然把文件指针移到了想要的位置,后边遇到write又不能移了,说不通呀。
2)还有人觉得:可能O_APPEND会把文件的指针移到文件末尾,如果中途使用了lseek()来读,则允许移动指针,如果使用lseek()来写,则lseek失效,文件指针还在结尾。我觉得也很扯,总不能执行完lseek()在观察下一步是读还是写,是写就撤销吧?那要是我写一个lseek()在写点别的隔几行在write,系统岂不是懵逼了?
3)所以现在最大的问题就是,O_APPEND到底移动不移动偏移量?我们不妨写个程序试试。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>  
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
int fd;
off_t off;
printf("Not using O_APPEND:\n");
fd = open("./test.c", O_WRONLY);
off = lseek(fd, 0, SEEK_SET);
printf("The offset of SEEK_SET is %d.\n", off);
close(fd);

fd = open("./test.c", O_WRONLY);
off = lseek(fd, 0, SEEK_END);
printf("The offset of SEEK_END is %d.\n", off);
close(fd);

fd = open("./test.c", O_WRONLY);
off = lseek(fd, 0, SEEK_CUR);
printf("The offset of SEEK_CUR is %d.\n", off);
close(fd);

printf("Using O_APPEND:\n");
fd = open("./test.c", O_WRONLY | O_APPEND);
off = lseek(fd, 0, SEEK_SET);
printf("The offset of SEEK_SET is %d.\n", off);
close(fd);

fd = open("./test.c", O_WRONLY | O_APPEND);
off = lseek(fd, 0, SEEK_END);
printf("The offset of SEEK_END is %d.\n", off);
close(fd);

fd = open("./test.c", O_WRONLY | O_APPEND);
off = lseek(fd, 0, SEEK_CUR);
printf("The offset of SEEK_CUR is %d.\n", off);
close(fd);

exit(0);

测试结果

1
2
3
4
5
6
7
8
9
Not using O_APPEND:
The offset of SEEK_SET is 0.
The offset of SEEK_END is 49. //文件长度为49
The offset of SEEK_CUR is 0.
Using O_APPEND:
The offset of SEEK_SET is 0.
The offset of SEEK_END is 49.
The offset of SEEK_CUR is 0.//注意:在O_APPEND情况下调用lseek(fd, 0, SEEK_CUR)
//输出的结果是0

通过最后一行打印结果,可以看出来O_APPEND根本没有移动偏移量,当前指针位置还是在文件开头。

查询得知:

每个进程有一个打开文件描述符表,而该表的表项中有一个文件状态标志,当前文件偏移量和一个v节点指针,v节点指针指向一个v节点表,而该表中的i节点信息中有一个当前文件长度。

对于lseek来说,它直接修改文件描述符表项中的当前文件偏移量,并返回当前的文件偏移量,而对于O_APPEND标志,则只是将其设置到了文件表项的文件状态标志中,此时当前文件偏移量并未修改,仍然为0,只有当用户企图写入文件时,才将文件当前偏移量置为文件长度。这从另一个角度说明,如果文件以O_APPEND标志打开,则lseek对该文件的写将不起作用,因为无论lseek怎样调整当前文件偏移量,在写入时仍然会被设为文件长度而将内容添加在文件尾。

思考

对于我们的Task 2,如果在代码最后多加一行打印一下当前文件指针的偏移量,是12还是文件的末尾呢?
答案:12,惊不惊喜,意不意外?

Scan the QR code to add me on Wechat