我正在开发一个多线程系统,其中一个文件可以基于文件访问权限在不同的线程之间共享。
如何检查文件是否已被另一个线程打开?
/proc/self/fd
目录,以查看该文件是否与文件描述符相关联。下面的程序概述了一种解决方案:DIR *d = opendir("/proc/self/fd");
if (d) {
struct dirent *entry;
struct dirent *result;
entry = malloc(sizeof(struct dirent) + NAME_MAX + 1);
result = 0;
while (readdir_r(d, entry, &result) == 0) {
if (result == 0) break;
if (isdigit(result->d_name[0])) {
char path[NAME_MAX+1];
char buf[NAME_MAX+1];
snprintf(path, sizeof(path), "/proc/self/fd/%s",
result->d_name);
ssize_t bytes = readlink(path, buf, sizeof(buf));
buf[bytes] = '\0';
if (strcmp(file_of_interest, buf) == 0) break;
}
}
free(entry);
closedir(d);
if (result) return FILE_IS_FOUND;
}
return FILE_IS_NOT_FOUND;
fopen()
创建了一个文件,则检索现有的FILE *
。标准C库没有提供遍历所有当前打开的FILE *
的机制。如果有这样的机制,您可以使用fileno()
获取其文件描述符,然后像上面显示的那样使用readlink()
查询/proc/self/fd/#
。FILE *
。可能使用文件名作为键的哈希表对您最有用。fileno(FILE)
的宏,它可以从文件指针返回文件描述符。然而,我不会扫描/proc/self/fd
,因为它不是原子操作——在扫描/proc/self/fd
时,其他线程仍然可以关闭或打开文件。 - mvp您可以使用int flock(int fd, int operation);将文件标记为已锁定,并检查它是否已被锁定。
Apply or remove an advisory lock on the open file specified by fd.
The argument operation is one of the following:
LOCK_SH Place a shared lock. More than one process may hold a
shared lock for a given file at a given time.
LOCK_EX Place an exclusive lock. Only one process may hold an
exclusive lock for a given file at a given time.
LOCK_UN Remove an existing lock held by this process.
如果您在每个线程中单独打开文件,则flock应该可以在多线程应用程序中正常工作: 多个线程能够同时获取flock
关于flock及其潜在弱点,这里有更多信息点击此处。
lsof $filename
命令。我对Windows上的多线程不太了解,但如果你在Linux上,你有很多选择。这里是一个绝妙的资源。你还可以利用操作系统固有或明确提供的文件锁定功能(例如:fcntl
)。更多关于Linux锁定的内容请参见此处。创建和手动管理自己的互斥锁比你通常拥有的灵活性更高。user814064
关于使用flock()
看起来是完美的解决方案,但保留其他选择从来不会有坏处!
添加了一个代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
FILE *fp;
int counter;
pthread_mutex_t fmutex = PTHREAD_MUTEX_INITIALIZER;
void *foo() {
// pthread_mutex_trylock() checks if the mutex is
// locked without blocking
//int busy = pthread_mutex_trylock(&fmutex);
// this blocks until the lock is released
pthread_mutex_lock(&fmutex);
fprintf(fp, "counter = %d\n", counter);
printf("counter = %d\n", counter);
counter++;
pthread_mutex_unlock(&fmutex);
}
int main() {
counter = 0;
fp = fopen("threads.txt", "w");
pthread_t thread1, thread2;
if (pthread_create(&thread1, NULL, &foo, NULL))
printf("Error creating thread 1");
if (pthread_create(&thread2, NULL, &foo, NULL))
printf("Error creating thread 2");
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
fclose(fp);
return 0;
}
如果您需要确定另一个线程是否打开了一个文件,而不是知道该文件已经被打开,那么您可能正在错误的方式中进行操作。
在多线程应用程序中,您希望管理所有线程都可以访问的公共资源列表。该列表需要以多线程安全的方式进行管理。这意味着您需要锁定一个mutex
,对列表执行操作,然后解锁mutex
。此外,由多个线程读写文件可能非常复杂。同样,您需要锁定才能安全地执行此操作。在大多数情况下,将文件标记为“繁忙”(即某个线程正在使用该文件),并等待文件变为“就绪”(即没有线程在使用它)要容易得多。
因此,假设您有一种链接列表实现形式,您可以通过类似以下方式的搜索来搜索列表:
my_file *my_file_find(const char *filename)
{
my_file *l, *result = NULL;
pthread_mutex_lock(&fmutex);
l = my_list_of_files;
while(l != NULL)
{
if(strcmp(l->filename, filename) == 0)
{
result = l;
break;
}
l = l->next;
}
pthread_mutex_unlock(&fmutex);
return result;
}
如果函数返回 NULL
,那么在搜索期间没有其他线程打开文件(因为互斥锁已解锁,在函数执行 return
之前,另一个线程可能已经打开了文件)。如果您需要以安全方式打开文件(即只能有一个线程打开文件 filename
),则需要具有一个名为 my_file_open()
的函数,该函数锁定、搜索、添加新的 my_file
(如果未找到),然后返回新添加的 my_file
指针。如果文件已存在,则 my_file_open()
可能会返回 NULL
,表示无法打开一个文件,其中一个线程可以使用(即另一个线程正在使用它)。
请记住,在搜索和添加之间不能解锁互斥锁。因此,在使用上面的 my_file_find()
函数之前,您不能先获取互斥锁(在这种情况下,您可能想要使用递归互斥锁)。
换句话说,只有在先锁定互斥锁、完成所有工作,然后解锁互斥锁的情况下,才能搜索现有列表、扩展现有列表并缩小(即关闭文件)。
这适用于任何类型的资源,不仅仅是文件。它可以是内存缓冲区、图形界面小部件、USB 端口等。
fcntl(fileno(stream),F_SETLEASE,F_WRLCK)
)。如果文件已经被其他人打开,它将失败。这仅适用于用户拥有的普通文件。如果其他人尝试打开该文件,则租赁所有者将收到一个信号,并且在/proc/sys/fs/lease_break_time
秒内可以操作文件并释放或降级租约,然后才能进行其他打开(但即使您重命名或取消链接该文件,它最终也会进行)。 - Nominal Animal