How not to open a file twice in linux?

后端 未结 3 1012
挽巷
挽巷 2021-01-21 18:24

I have a linked list with an fd and a string I used to open this file in each entry. I want to open and add files to this list only if this file is not already opened, because I

3条回答
  •  花落未央
    2021-01-21 19:26

    In situations like this, it's often useful to consider your data structures. Change to a data structure which does not allow duplicates, such as a hash table.

    Maintain a set of which data you've seen before. I've used a hash table for this set. As per @RossRidge's answer, use the inode and device as the key. This allows duplicates to be discovered in O(1) time.

    Here is an example implementation.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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);
            }
        }
    }
    

    And here's a sample result.

    $ 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
    

提交回复
热议问题