6

В Интернете многие люди говорят, что вы можете обрезать файл, используя > 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 ответ1

9

Обратите внимание, что хотя системный вызов называется truncate, на самом деле его лучше интерпретировать как «Сделать так, чтобы мой файл сообщал о таком количестве байтов». Согласно man-странице системного вызова:

Функции truncate() и ftruncate() приводят к усечению обычного файла с именем path или ссылкой на fd до размера байтов с точной длиной.

Если файл ранее был больше этого размера, лишние данные будут потеряны. Если файл ранее был короче, он расширяется, и расширенная часть читается как нулевые байты ('\0').

Таким образом, можно обрезать файл и сделать его больше, а не меньше.

Итак, мои вопросы: если у моего процесса есть открытый файл в позиции 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': ''

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .