web-dev-qa-db-ja.com

使用中のファイルを切り捨てるとどうなりますか?

Webの周りでは、ファイルの使用開始時に> 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を使用するとサイズが異なります。 tが変更されました

したがって、ファイルを切り捨てると、ファイルのオフセットがレポートされない場合、これをCentos 7Redhat 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);
}
7
c4f4t0r

システムコールは切り捨てと呼ばれますが、実際には「ファイルにこの数バイトのサイズを報告させる」と解釈する方が適切です。システムコールのマンページによると:

Truncate()およびftruncate()関数を使用すると、pathで指定された、またはfdで参照された通常のファイルが、正確な長さのバイトのサイズに切り捨てられます。

以前にファイルがこのサイズよりも大きかった場合、余分なデータは失われます。以前にファイルが短かった場合、ファイルは拡張され、拡張された部分はnullバイト( '\ 0')として読み取られます。

したがって、ファイルを切り捨てて、小さくするのではなく大きくすることができます。

だから私の質問は、私のプロセスがpos 1000で開いているファイルを持っていて、-s0ファイル名を切り捨てた場合、切り捨てが機能する場合、次のプロセスの書き込みで何が起こるかということです。

  • 切り捨てました。この段階でのファイルサイズは0バイトです。オフセットは1000です。
  • 位置1001で書き込みが発生します。
  • ファイルサイズは1002バイトです。バイト0〜1000には、「\ 0」(null)が含まれます。バイト1001+には、書き込まれたデータが含まれています。

ファイル自体よりも大きい位置からファイルに書き込む場合、ファイルの終わりと新しい書き込みの間のデータはnullバイトになり、これら2つのポイントの間のファイルデータはsparseと呼ばれます。 。

確かに、あなたは次のことをして同じ効果を生み出すことができます。

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': ''
11
Matthew Ife