2016-11-27 4 views
0

ファイルに原子的に書き込みたいと思っています。ほとんどのlinux/unixシステムでアトミック書き込みを許可しているようですのでwrite()関数を使用しようとしています。Snprintf()でWrite()を使ってファイルに原子的に書き込み

私は文字列変数の長さと、複数のprintfのを持っているので、私は私がやったこの関数のドキュメントを読めば、これを適切に行うことができるようにするためにsnprintf()を使用すると、書き込み関数の引数として渡すように言われましたテスト実装、次のように:働いているようだが、私はむしろ、大きなバッファ(何か私は100%を確認してください意志を作成していますので、これはそれを実装するための最も正しい方法であれば、私の質問は

int file = open("file.txt", O_CREAT | O_WRONLY); 
if(file < 0) 
    perror("Error:"); 
char buf[200] = ""; 
int numbytes = snprintf(buf, sizeof(buf), "Example string %s" stringvariable); 
write(file, buf, numbytes); 

私のテストから、すべての私のprintfsに合う)を書いて渡す前にそれを保存する。

+0

'INT NUMBYTES =のsnprintf(BUF、はsizeof(BUF)、 "例列%S" stringvariable);'のsnprintf() 'そのコードにはsizeof 'よりも大きい値を返すことができることに注意してください(BUF ) '結果の完全な出力文字列が' buf'に収まらない場合は ' –

答えて

1
  1. いいえ、write()は、それが単一の呼び出しで与えられたすべてのデータを書き込んでない場合でも、アトミックではありません。

  2. アトミックファイルの更新を実現するために、すべての読者および作成者にアドバイザリレコードロック(fcntl(fd, F_SETLKW, &lock))を使用します。

    fcntl()ベースのレコードロックは、LinuxとBSDの両方でNFS上で動作します。 flock()ベースのファイルロックは、システムとカーネルのバージョンによって異なります。 (一部のWebホスティングサービスのようにNFSロックが無効になっている場合は、ロックは信頼できません)。を.l_whence = SEEK_SET, .l_start = 0, .l_len = 0に初期化するだけで、ファイル全体を参照することができます。

  3. 使用asprintf()が動的に割り当てられたバッファに印刷する:ループの中で、あなたのwrite()をラップん、ロックを追加した後

    char *buffer = NULL; 
    int length; 
    
    length = asprintf(&buffer, ...); 
    if (length == -1) { 
        /* Out of memory */ 
    } 
    
    /* ... Have buffer and length ... */ 
    
    free(buffer); 
    
  4. { 
        const char  *p = (const char *)buffer; 
        const char *const q = (const char *)buffer + length; 
        ssize_t   n; 
    
        while (p < q) { 
    
         n = write(fd, p, (size_t)(q - p)); 
         if (n > 0) 
          p += n; 
         else 
         if (n != -1) { 
          /* Write error/kernel bug! */ 
         } else 
         if (errno != EINTR) { 
          /* Error! Details in errno */ 
         } 
        } 
    } 
    

    そのいくつかのローカルファイルシステムがありますが保証write()は、記憶領域を使い切っていない限り、短いカウントを返しません。特にネットワーク化されたものではありません。上のようなループを使用すると、あなたのプログラムはそのようなファイルシステムでも動作します。私の意見では、信頼性の高い堅牢な運用のために追加するコードはそれほど多くありません。

  5. Linuxでは、あるファイルにa write leaseを入れて、そのファイルをしばらく開いているプロセスを除外することができます。

    ファイルをブロックすることはできませんが、最大では/proc/sys/fs/lease-break-time秒(通常は45秒)まで遅延させることができます。リースは、他のプロセスがファイルを開いていない場合にのみ許可され、他のプロセスがファイルを開こうとすると、リース所有者は信号を取得します。 (リース所有者がファイルを閉じるなどしてリースを解放しない場合、リース休憩時間が経過した後、カーネルは自動的にリースを中断します)。ローカルファイルでは、制限された使用になります。

  6. 読者がファイルを開いたままにしておらず、読んだり読んだりするたびにファイルを読み込んだりすると、完全な置き換えファイルを書き込むことができます(同じファイルシステム上になければなりません;ロックサブディレクトリこのために)、古いファイルにハードリンクします。

    すべての読者には、古いファイルまたは新しいファイルのいずれかが表示されますが、ファイルを開いたままにしておくと、変更が表示されません。

+0

残念ながら、これは特定の定義された関数を使用する必要がある割り当てのいくつかの種類です。この場合、 'write()'は常にアトミックな書き込みを保証するものとみなされます。しかし、あなたは私が気づいていないいくつかの大きな点を持ち出します。 – spacing

+0

'asprintf()'に関して、簡単に言えば、私はマルチスレッドプログラムで作業していて、各スレッドはループ内にプリントを持ち、各スレッドの各ループはバッファをリセットする必要があります。あなたがそうしたように各スレッド関数のポインタを使ってバッファを宣言し、 'asprintf()'を使ってそのバッファに動的に割り当てると、これはバッファが各スレッドに対してプライベートであることを保証しますか?つまり、asprintfはそのダイナミックバッファ用に別のメモリを割り当てるでしょうか?私はそれを理解するのにいくつかの問題を抱えています。 – spacing

+0

@spacing:ポインタをNULLにリセットすると、 'asprintf()'は 'malloc()'と同じようにメモリを割り当てます。もちろん、1回の書き込みごとにメモリを割り当てたり解放したりするのはオーバーヘッドです。 'snprintf()'が 'buffer_size()'を返すたびに、より大きいバッファを再割り当てするだけで、スレッドごとの永続バッファポインタ( 'static __thread char * buffer_data = NULL;静的__thread size_t buffer_size = 0;') -1 "以上にする必要がありますが、各スレッドが' free(buffer_data);を呼び出すようにする必要があります。 buffer_data = NULL; buffer_size = 0; 'で終了(またはキャンセル)されます。 –

関連する問題