2017-09-06 4 views
3

私は2つのプロセスによってアクセスされるファイルfoo.hexを持っています。 1つのプロセスはO_RDONLYのアクセス権を持ち、もう1つのプロセスはO_RDWRアクセス権を持ちます。原子的に開いてロックするファイル

初めてシステムを起動するときは、書き込みプロセスが初期化する前に、読み取りプロセスでファイルにアクセスするべきではありません。

このように、私はファイルを初期化するためにこのようなものを書きました。まだそれが初期化される前に、ファイルにアクセスするには、読者のプロセスに可能性を残し

fd = open("foo.hex", O_RDWR|O_CREAT, 0666); 
flock(fd, LOCK_EX); 

init_structures(fd); 

flock(fd, LOCK_UN); 

open()flock()へのアトミックな方法が見つかりませんでした。ミューテックスの他に、できるだけオーバーヘッドの少ないエレガントな方法で私の目標を達成するための他の可能性があります(これは初めてシステムを起動するときに初めて使用されるためです)。

+0

これは実際の問題ではありませんが、ライターで 'open(" foo.hex "、O_RDWR | O_CREAT | O_EXCL、0666)'を使用したい場合があります。 – zwol

+0

@zwol右、実際にはO_CREAT、0666で開きますが、競合状態の問題は変わりません。 –

+2

覚えておいてください: 'flock()'ロックは勧告であり、必須ではありませんので、あるプロセスにロックを適用した後でも、ロックを無視すれば、 –

答えて

6

ライターが代わりに "foo.hex.init"というファイルを作成し、初期化してから "foo.hex"に名前を変更します。このようにして、読者は初期化されていないファイルの内容を見ることはできません。

+0

私はそれを検討していましたが、これがどのように行われたのか、それとももっとハックがあるのか​​どうかは分かりませんでした。 –

+1

これはこれが完了した方法です。書き込み中にファイルを保護するためにファイルの名前を変更するのは非常に一般的です。 Webブラウザは、ファイルのダウンロード中にファイルの半分を開こうとしないようにします。ファイルのロックには危険が伴うため、可能な限り避けてください。 –

+1

名前の変更について。プログラムが始まる前に "foo.hex"が存在し、ライターが "foo.hex.init"を作ると仮定してください。名前を変更するには、「foo.hex.init」の名前を「foo.hex」に変更する前に、「foo.hex」を削除(または「some_tmp_name」に名前を変更)する必要はありませんか?これがそうなら、それは "foo.hex"が存在しない - 読者に問題を引き起こすかもしれないウィンドウを残すか? – chux

0

inter-process communicationsの方法はたくさんあります。

ファイルを開いて初期化する前に、書き込みプロセスがロックする名前付きセマフォを使用している可能性がありますか?次に、読み取りプロセスはセマフォをロックしようとする可能性があり、成功し、ファイルが存在しない場合、セマフォのロックを解除して少し待ってから再試行します。

最も簡単な方法ですが、特にファイルが毎回書き込みプロセスによって再作成される場合は、すでにthe answer by John Zwinckにあります。

1

別のアプローチは、少し寝て、ファイルがまだ存在し、または空でないことを発見すると再試行するリーダー・プロセスのためです。

int open_for_read(const char *fname) 
{ 
    int retries = 0; 

    for (;;) { 
     int fd = open(fname, O_RDONLY); 
     if (fd == -1) { 
      if (errno != ENOENT) return -1; 
      goto retry; 
     } 
     if (flock(fd, LOCK_SH)) { 
      close(fd); 
      return -1; 
     } 

     struct stat st; 
     if (fstat(fd, &st)) { 
      close(fd); 
      return -1; 
     } 
     if (st.st_size == 0) { 
      close(fd); 
      goto retry; 
     } 
     return fd; 

    retry: 
     if (++retries > MAX_RETRIES) return -1; 
     sleep(1); 
    } 
    /* not reached */ 
} 

ライターがレースを失った場合、再起動する必要がないように、書き込み側にも同様のコードが必要です。

1

それが書かれている後に別のアプローチは、既存のファイルを削除して再作成の権限なしにそれにアクセスするための任意のプロセスのために、そのファイルのパーミッションを変更することができます

unlink("foo.hex"); 
fd = open("foo.hex", O_RDWR|O_CREAT|O_EXCL, 0); 

init_structures(fd); 

fchmod(fd, 0666); 

おそらく動作しません、もしあなた」 rootとして実行しています。 unlink()呼び出しが行われた後、古いデータを使用してから任意のプロセスを妨げる

これを(あなたは...とにかくやってはなりません)。あなたの要件に応じて、それがまたはファイルが、既存または新規のファイルが初期化されている間、アクセス可能ではないに対処するために必要な追加のリーダー・コードの価値があってもなくてもよいです。 init_structures()大きな時間がかかり、新しいデータが利用可能になると、古いデータを使用しないように本当の、ハード要件がありますしない限り、

個人的に、私はrename("foo.hex.init", "foo.hex")ソリューションを使用すると思います。しかし、時には重要な人々は、新しいデータのいずれかの部分が利用可能である一方で、古いデータを使用してに慣れていない、と「読者プロセスは2ミリ秒早く、それはとにかく古いデータを使用し始めた場合は、」彼らは本当に、理解していません。

関連する問題