複数のスレッドを使用して同じファイルの異なる部分に同時に書き込むときに、書き込み中の正しいファイル位置を取得するのに問題があります。
ファイルへの 1 つのグローバル ファイル記述子があります。私の書き込み関数では、最初にミューテックスをロックしてlseek(global_fd, 0, SEEK_CUR)
から、現在のファイル位置を取得します。次に、 を使用して 31 個のゼロ バイト (31 はエントリ サイズ) を書き込みwrite()
、後で使用するためのスペースを確保します。次に、ミューテックスのロックを解除します。
関数の後半で、ローカルfd
変数を同じファイルに宣言し、それを開きます。私は今、lseek
そのローカルでfd
を実行して、以前に学んだ私のスペースが予約されている位置に到達します。最後にwrite()
、エントリ用にそこに 31 データ バイトを入力し、ローカルを閉じfd
ます。
問題は、まれに、エントリが予期された場所に書き込まれないことです (データが壊れているわけではありません。別のエントリと交換されているか、2 つのエントリが同じ場所に書き込まれているようです)。私が説明した「関数の書き込み」が実行されている複数のスレッドがあります。
pwrite()
それ以来、それを使用して特定のオフセットに書き込むことができることを学びました。これはより効率的であり、 lseek()
. ただし、最初に調べたいのは、元のアルゴリズムの何が問題なのですか? 予想される書き込み場所と、データが実際にファイルに格納される場所との間の不一致を引き起こす可能性のあるバッファリングの種類はありますか?
関連するコード スニペットを以下に示します。これが問題となる理由は、2 番目のデータ ファイルに、書き込み中のエントリが保存される場所を記録するためです。書き込み前に基づくその場所lseek()
が正確でない場合、データが適切に一致しません。これは時々発生することです (再現するのは困難です。おそらく 100,000 回の書き込みに 1 回発生します)。ありがとう!
db_entry_add(...)
{
char dbrecord[DB_ENTRY_SIZE];
int retval;
pthread_mutex_lock(&db_mutex);
/* determine the EOF index, at which we will add the log entry */
off_t ndb_offset = lseek(cfg.curr_fd, 0, SEEK_CUR);
if (ndb_offset == -1)
{
fprintf(stderr, "Unable to determine ndb offset: %s\n", strerror_s(errno, ebuf, sizeof(ebuf)));
pthread_mutex_unlock(&db_mutex);
return 0;
}
/* reserve entry-size bytes at the location, at which we will
later add the log entry */
memset(dbrecord, 0, sizeof(dbrecord));
/* note: db_write() is a write() loop */
if (db_write(cfg.curr_fd, (char *) &dbrecord, DB_ENTRY_SIZE) < 0)
{
fprintf(stderr, "db_entry_add2db - db_write failed!");
close(curr_fd);
pthread_mutex_unlock(&db_mutex);
return 0;
}
pthread_mutex_unlock(&db_mutex);
/* in another data file, we now record that the entry we're going to write
will be at the specified location. if it's not (which is the problem,
on rare occasion), our data will be inconsistent */
advertise_entry_location(ndb_offset);
...
/* open the data file */
int write_fd = open(path, O_CREAT|O_LARGEFILE|O_WRONLY, 0644);
if (write_fd < 0)
{
fprintf(stderr, "%s: Unable to open file %s: %s\n", __func__, cfg.curr_silo_db_path, strerror_s(errno, ebuf, sizeof(ebuf)));
return 0;
}
pthread_mutex_lock(&db_mutex);
/* seek to our reserved write location */
if (lseek(write_fd, ndb_offset, SEEK_SET) == -1)
{
fprintf(stderr, "%s: lseek failed: %s\n", __func__, strerror_s(errno, ebuf, sizeof(ebuf)));
close(write_fd);
return 0;
}
pthread_mutex_unlock(&db_mutex);
/* write the entry */
/* note: db_write_with_mutex is a write() loop wrapped with db_mutex lock and unlock */
if (db_write_with_mutex(write_fd, (char *) &dbrecord, DB_ENTRY_SIZE) < 0)
{
fprintf(stderr, "db_entry_add2db - db_write failed!");
close(write_fd);
return 0;
}
/* close the data file */
close(write_fd);
return 1;
}
完全を期すために、もう1つ注意してください。問題を引き起こしている可能性のある同様の、しかしより単純なルーチンがあります。これは buffered outputを使用しますが、各書き込みの最後に を(FILE*, fopen, fwrite)
実行します。以前のルーチンとは異なるファイルfflush()
に書き込みますが、同じ症状を引き起こす可能性があります。
pthread_mutex_lock(&data_mutex);
/* determine the offset at which the data will be written. this has to be accurate,
otherwise it could be causing the problem */
offset = ftell(current_fp);
fwrite(data);
fflush(current_fp);
pthread_mutex_unlock(&data_mutex);