网上很多人说你可以使用> filename
或truncate -s0 filename
当文件开始使用时截断文件
我知道每次一个进程写入一个文件时,该进程都会使用一个偏移量来写入文件,并使用这样的脚本进行测试。
#!/usr/bin/env python
import os, time
with open("passwd","w") as f: #copy of passwd file in my current directory
f.seek(0)
for x in xrange(1,1000):
f.write("hello world \n" + time.ctime() + "\n")
f.flush()
time.sleep(2)
每次我的脚本进行写入系统调用时,/proc/pid_number/fdinfo/3 pos
字段中的偏移量都会发生变化,但是当我尝试使用上面列出的方法截断文件时,在我的文件中,当我使用或更少^@
打开文件时,我会看到许多这样的字符 ,文件类型从更改为,而当我使用时,大小没有改变vim
-u
ASCII text
data
ls -l filename
因此,当截断文件时,文件的偏移量不会报告,我在Centos 7
和中对此进行了测试Redhat 5
,因此我可以判断,当文件正在被进程使用时更改了文件大小,不会释放空间并弄脏我的文件。
所以我的问题是,如果我的进程中打开了一个文件pos 1000
并且我确实这样做了truncate -s0 filename
,如果截断有效,那么在下一个进程写入时会发生什么?
strace truncate -s0 passwd
open("passwd", O_WRONLY|O_CREAT|O_NONBLOCK, 0666) = 3
ftruncate(3, 0) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?
ls -l passwd
-rw-rw-r--. 1 user91 users 13832 Feb 23 17:16 passwd
正如你所见,我的文件没有被截断
如果我打开附加模式(例如使用此代码),则不会发生此问题。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
int main(){
int range = 1000;
int x; x = open("passwd", O_WRONLY|O_CREAT|O_APPEND);
int i = 0;
for( i = 0; i <= range; range++)
write(x,"hello world\n",12);
sleep(2);
}
答案1
请注意,尽管系统调用被称为截断,但实际上最好将其解释为“让我的文件报告这么多字节的大小”。根据系统调用手册页:
truncate() 和 ftruncate() 函数导致由路径命名或由 fd 引用的常规文件被截断为精确长度字节的大小。
如果文件之前大于此大小,则多余数据将丢失。如果文件之前较短,则文件将扩展,扩展部分将读取为空字节 ('\0')。
因此,可以截断文件并使其变大,而不是变小。
所以我的问题是,如果我的进程在 pos 1000 中打开了一个文件,并且我确实截断了 -s0 文件名,如果截断有效,那么下一个进程写入时会发生什么?
- 您已截断。此阶段的文件大小为 0 字节。偏移量为 1000。
- 在位置 1001 处发生写入。
- 文件大小为 1002 字节。字节 0-1000 包含“\0”(空)。字节 1001+ 包含写入的数据。
当你从大于文件本身的位置写入文件时,文件末尾和新写入之间的数据将成为空字节,并且这两点之间的文件数据被称为疏。
事实上,您可以执行以下操作并产生相同的效果。
import os, sys
f = open('data.txt','w')
f.seek(1048576)
f.write('a')
f.flush()
f.close()
您还提到,以追加模式打开可以避免这种行为。这是真的,因为在这种情况下,您正在指示内核“每次都写入文件的实际末尾”。如果您截断,则文件末尾会发生变化。在追加中,您无法重新定位文件指针。
这是一个示例程序,演示了被截断的文件、偏移量和文件中的数据会发生什么情况。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <err.h>
#define FPATH "/tmp/data.txt"
#define FSIZE 65536
int main() {
int a,b;
char buf[FSIZE];
char byte;
struct stat st;
memset(buf, 'A', FSIZE);
a = open(FPATH, O_WRONLY|O_CREAT);
b = open(FPATH, O_RDONLY);
if (a < 0 || b < 0)
errx(EXIT_FAILURE, "Could not open file");
printf("Writing %d * 'A' into file\n", FSIZE);
/* Write some bytes */
if(write(a, buf, FSIZE) != FSIZE)
errx(EXIT_FAILURE, "Couldn't write complete size out");
/* Seek to a new position in the file */
lseek(b, FSIZE/2, SEEK_SET);
printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
stat(FPATH, &st);
printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);
/* OK -- now, read the byte at the position */
if (read(b, &byte, 1) < 0)
err(EXIT_FAILURE, "Could not read file");
printf("Character at current position of handle 'b': '%c'\n", byte);
/* Truncate the file in the 'a' handle */
printf("Performing truncate...\n");
if (ftruncate(a, 0) < 0)
err(EXIT_FAILURE, "Cannot truncate file");
printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
stat(FPATH, &st);
printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);
printf("Writing one byte via handle 'a'\n");
if (write(a, buf, 1) < 0)
err(EXIT_FAILURE, "Cannot perform second write");
printf("Current position of handle 'a': %d\n", lseek(a, 0, SEEK_CUR));
printf("Current position of handle 'b': %d\n", lseek(b, 0, SEEK_CUR));
stat(FPATH, &st);
printf("Reported size on filesystem of %s: %d\n", FPATH, st.st_size);
if (read(b, &byte, 1) < 0)
err(EXIT_FAILURE, "Could not read file");
printf("Character at current position of handle 'b': '%c'\n", byte);
close(a);
close(b);
exit(0);
}
这导致以下输出;
Writing 65536 * 'A' into file
Current position of handle 'a': 65536
Current position of handle 'b': 32768
Reported size on filesystem of /tmp/data.txt: 65536
Character at current position of handle 'b': 'A'
Performing truncate...
Current position of handle 'a': 65536
Current position of handle 'b': 32769
Reported size on filesystem of /tmp/data.txt: 0
Writing one byte via handle 'a'
Current position of handle 'a': 65537
Current position of handle 'b': 32769
Reported size on filesystem of /tmp/data.txt: 65537
Character at current position of handle 'b': ''