如何不在 linux 中打开一个文件两次?
How not to open a file twice in linux?
我有一个链接列表,其中包含一个 fd 和一个字符串,用于在每个条目中打开该文件。仅当此文件尚未打开时,我才想打开文件并将其添加到此列表中,因为我打开并解析了此文件并且不想执行两次。我的想法是将文件名与此列表中的每个名称进行比较,但我的程序执行了多次并且 Linux 中的一个文件可以有多个名称(soft/hard 链接)。我认为它不应该这么复杂,因为 OS 很容易检查我是否已经使用了 inode,r?
我已经尝试 open
有和没有 flock
的同一个文件,但我总是得到一个新的 fd
.
当您成功打开文件时,由fstat
提交的fstat
on the file. Check to see if the st_ino
and st_dev
of the struct stat
已被记录在您的链表中。如果是,则关闭文件描述符并转到下一个文件。否则将文件描述符、文件名以及 st_ino
和 st_dev
值添加到列表中。
您可以在打开文件之前使用 stat
检查,但如果通常情况下文件尚未打开,则在打开文件之后使用 fstat
会稍微快一些。
在这种情况下,考虑数据结构通常很有用。更改为不允许重复的数据结构,例如散列 table.
维护一组您以前见过的数据。我使用了 hash table for this set. As per ,使用索引节点和设备作为密钥。这允许在 O(1) 时间内发现重复项。
这是一个示例实现。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
static int get_fd(GHashTable *fds, const char *filename, int mode) {
int fd;
struct stat stat;
int keysize = 33;
char key[keysize]; /* Two 64 bit numbers as hex and a separator */
/* Resolve any symlinks */
char *real_filename = realpath(filename, NULL);
if( real_filename == NULL ) {
printf("%s could not be resolved.\n", filename);
return -1;
}
/* Open and stat */
fd = open( real_filename, mode );
if( fd < 0 ) {
printf("Could not open %s: %s.\n", real_filename, strerror(errno));
return -1;
}
if( fstat(fd, &stat) != 0 ) {
printf("Could not stat %s: %s.\n", real_filename, strerror(errno));
return -1;
}
/* Make a key for tracking which data we've processed.
This uses both the inode and the device it's on.
It could be done more efficiently as a bit field.
*/
snprintf(key, keysize, "%lx|%lx", (long int)stat.st_ino, (long int)stat.st_dev);
/* See if we've already processed that */
if( g_hash_table_contains(fds, key) ) {
return 0;
}
else {
/* Note that we've processed it */
g_hash_table_add(fds, key);
return fd;
}
}
int main(int argc, char** argv) {
int mode = O_RDONLY;
int fd;
GHashTable *fds = g_hash_table_new(&g_str_hash, &g_str_equal);
for(int i = 1; i < argc; i++) {
char *filename = argv[i];
fd = get_fd(fds, filename, mode);
if( fd == 0 ) {
printf("%s has already been processed.\n", filename);
}
else if( fd < 0 ) {
printf("%s could not be processed.\n", filename);
}
else {
printf("%s: %d\n", filename, fd);
}
}
}
这是一个示例结果。
$ touch one two three
$ ln one one_link
$ ln -s two two_sym
$ ./test one* two* three*
one: 3
one_link has already been processed.
two: 5
two_sym has already been processed.
three: 7
只要不关闭成功打开的文件,就可以使用非阻塞flock
来防止对同一个文件再次加锁:
#include <unistd.h>
#include <sys/file.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>
int openAndLock(const char* fn){
int fd = -1;
if(((fd = open(fn, O_RDONLY)) >= 0) && (flock(fd, LOCK_EX|LOCK_NB) == 0)){
fprintf(stderr, "Successfully opened and locked %s\n", fn);
return fd;
}else{
fprintf(stderr, "Failed to open or lock %s\n", fn);
close(fd);
return -1;
}
}
int main(int argc, char** argv){
for(int i=1; i<argc; i++){
openAndLock(argv[i]);
}
return 0;
}
示例:
$ touch foo
$ ln foo bar
$ ./a.out foo foo
Successfully opened and locked foo
Failed to open or lock foo
$ ./a.out foo bar
Successfully opened and locked foo
Failed to open or lock bar
我有一个链接列表,其中包含一个 fd 和一个字符串,用于在每个条目中打开该文件。仅当此文件尚未打开时,我才想打开文件并将其添加到此列表中,因为我打开并解析了此文件并且不想执行两次。我的想法是将文件名与此列表中的每个名称进行比较,但我的程序执行了多次并且 Linux 中的一个文件可以有多个名称(soft/hard 链接)。我认为它不应该这么复杂,因为 OS 很容易检查我是否已经使用了 inode,r?
我已经尝试 open
有和没有 flock
的同一个文件,但我总是得到一个新的 fd
.
当您成功打开文件时,由fstat
提交的fstat
on the file. Check to see if the st_ino
and st_dev
of the struct stat
已被记录在您的链表中。如果是,则关闭文件描述符并转到下一个文件。否则将文件描述符、文件名以及 st_ino
和 st_dev
值添加到列表中。
您可以在打开文件之前使用 stat
检查,但如果通常情况下文件尚未打开,则在打开文件之后使用 fstat
会稍微快一些。
在这种情况下,考虑数据结构通常很有用。更改为不允许重复的数据结构,例如散列 table.
维护一组您以前见过的数据。我使用了 hash table for this set. As per
这是一个示例实现。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
static int get_fd(GHashTable *fds, const char *filename, int mode) {
int fd;
struct stat stat;
int keysize = 33;
char key[keysize]; /* Two 64 bit numbers as hex and a separator */
/* Resolve any symlinks */
char *real_filename = realpath(filename, NULL);
if( real_filename == NULL ) {
printf("%s could not be resolved.\n", filename);
return -1;
}
/* Open and stat */
fd = open( real_filename, mode );
if( fd < 0 ) {
printf("Could not open %s: %s.\n", real_filename, strerror(errno));
return -1;
}
if( fstat(fd, &stat) != 0 ) {
printf("Could not stat %s: %s.\n", real_filename, strerror(errno));
return -1;
}
/* Make a key for tracking which data we've processed.
This uses both the inode and the device it's on.
It could be done more efficiently as a bit field.
*/
snprintf(key, keysize, "%lx|%lx", (long int)stat.st_ino, (long int)stat.st_dev);
/* See if we've already processed that */
if( g_hash_table_contains(fds, key) ) {
return 0;
}
else {
/* Note that we've processed it */
g_hash_table_add(fds, key);
return fd;
}
}
int main(int argc, char** argv) {
int mode = O_RDONLY;
int fd;
GHashTable *fds = g_hash_table_new(&g_str_hash, &g_str_equal);
for(int i = 1; i < argc; i++) {
char *filename = argv[i];
fd = get_fd(fds, filename, mode);
if( fd == 0 ) {
printf("%s has already been processed.\n", filename);
}
else if( fd < 0 ) {
printf("%s could not be processed.\n", filename);
}
else {
printf("%s: %d\n", filename, fd);
}
}
}
这是一个示例结果。
$ touch one two three
$ ln one one_link
$ ln -s two two_sym
$ ./test one* two* three*
one: 3
one_link has already been processed.
two: 5
two_sym has already been processed.
three: 7
只要不关闭成功打开的文件,就可以使用非阻塞flock
来防止对同一个文件再次加锁:
#include <unistd.h>
#include <sys/file.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>
int openAndLock(const char* fn){
int fd = -1;
if(((fd = open(fn, O_RDONLY)) >= 0) && (flock(fd, LOCK_EX|LOCK_NB) == 0)){
fprintf(stderr, "Successfully opened and locked %s\n", fn);
return fd;
}else{
fprintf(stderr, "Failed to open or lock %s\n", fn);
close(fd);
return -1;
}
}
int main(int argc, char** argv){
for(int i=1; i<argc; i++){
openAndLock(argv[i]);
}
return 0;
}
示例:
$ touch foo
$ ln foo bar
$ ./a.out foo foo
Successfully opened and locked foo
Failed to open or lock foo
$ ./a.out foo bar
Successfully opened and locked foo
Failed to open or lock bar