当使用多个线程同时写入同一文件的不同部分时,我在获取正确的文件位置时遇到问题。
我有一个文件的全局文件描述符。在我的写作功能中,我首先锁定一个互斥锁,然后lseek(global_fd, 0, SEEK_CUR)
获取当前文件位置。接下来我使用 写入 31 个零字节(31 是我的条目大小)write()
,实际上是为以后保留空间。然后我解锁互斥锁。
稍后在函数中,我fd
为同一个文件声明了一个局部变量,然后打开它。我现在lseek
对该本地进行一次操作,fd
以到达我之前学到的位置,我的空间是保留的。最后,我write()
在那里输入了 31 个数据字节,并关闭了本地fd
.
问题似乎很少,条目没有写入预期的位置(它不是损坏的数据 - 似乎它与不同的条目交换,或者两个条目被写入相同的位置)。有多个线程在运行我描述的“写入功能”。
从那以后我了解到pwrite()
可以用来写入特定的偏移量,这样效率更高,并且消除了lseek()
. 但是,我首先要弄清楚:我原来的算法有什么问题?是否有任何类型的缓冲可能导致预期写入位置与数据实际最终存储在文件中的位置之间的差异?
相关代码片段如下。这是一个问题的原因是,在第二个数据文件中,我记录了我正在编写的条目将被存储的位置。如果根据lseek()
写入之前的位置不准确,我的数据不正确匹配——这有时会发生(很难重现——它可能发生在 100k 写入中的 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;
}
为了完整起见,还要注意。我有一个类似但更简单的例程,也可能导致问题。这个使用缓冲输出(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);