fuse_operations与fuse_lowlevel_ops

Fuse provides two sets of interfaces for developers, namely fuse_lowlevel_ops and fuse_operations. Developers only need to implement one of these two sets of interfaces to implement a user space file system.

 

The members of struct fuse_lowlevel_ops are as follows, where the init method is called before all other methods to initialize the file system, fuse has been implemented, and destroy is to do some cleanup work when the file system is unmounted. The parameters used for most requests are ino of type fuse_ino_t, and the view provided by the file system to the user is presented by the file name, so lookup is the key to implementing the file system. It looks for the file corresponding to the name in the parent, and Return the corresponding information, you can use fuse_reply_entry or fuse_reply_err as the request return.

 

The methods in the interface should not be difficult for those who know VFS to understand. As long as you implement these interfaces as needed, you can customize your own file system. For the detailed description of this group of interfaces, see fuse_lowlevel.h.

 

void(*

init )(void *userdata, struct fuse_conn_info *conn)

void(*

destroy )(void *userdata)

void(*

lookup )(fuse_req_t req, fuse_ino_t parent, const char *name)

void(*

forget )(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)

void(*

getattr )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

setattr )(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)

void(*

readlink )(fuse_req_t req, fuse_ino_t ino)

void(*

mknod )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)

void(*

mkdir )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)

void(*

unlink )(fuse_req_t req, fuse_ino_t parent, const char *name)

void(*

rmdir )(fuse_req_t req, fuse_ino_t parent, const char *name)

void(*

symlink )(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)

void(*

rename )(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname)

void(*

link )(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)

void(*

open )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

read )(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)

void(*

write )(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi)

void(*

flush )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

release )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

fsync )(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)

void(*

opendir )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

readdir )(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)

void(*

releasedir )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)

void(*

fsyncdir )(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)

void(*

statfs )(fuse_req_t req, fuse_ino_t ino)

void(*

setxattr )(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags)

void(*

getxattr )(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)

void(*

listxattr )(fuse_req_t req, fuse_ino_t ino, size_t size)

void(*

removexattr )(fuse_req_t req, fuse_ino_t ino, const char *name)

void(*

access )(fuse_req_t req, fuse_ino_t ino, int mask)

void(*

create )(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)

void(*

getlk )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)

void(*

setlk )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock, int sleep)

void(*

bmap )(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)

void(*

ioctl )(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, struct fuse_file_info *fi, unsigned *flagsp, const void *in_buf, size_t in_bufsz, size_t out_bufszp)

void(*

poll )(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)

 

How does the user-implemented interface relate to this structure?

In fact, a set of interfaces has been implemented in fuse. In fuse_lowlevel.c, a static structure array is defined. The elements of the array are a set of (function, name) structures, but no actual work is done. When the fuse user After the space daemon reads the request from /fuse/dev, it identifies each request by the request number, and calls the corresponding processing function here. For example, when the read call is read, it will call do_read for processing.

 

static struct {

    void (*func)(fuse_req_t, fuse_ino_t, const void *);

    const char *name;

} fuse_ll_ops[] = {

    // only list part

    [FUSE_LOOKUP]      = { do_lookup,      "LOOKUP"      },

    [FUSE_OPEN]        = { do_open,        "OPEN"        },

    [FUSE_READ]        = { do_read,        "READ"        },

    [FUSE_WRITE]       = { do_write,       "WRITE"       },

    [FUSE_STATFS]      = { do_statfs,      "STATFS"      },

    [FUSE_FLUSH]       = { do_flush,       "FLUSH"       },

    [FUSE_INIT]        = { do_init,        "INIT"        },

    [FUSE_OPENDIR]     = { do_opendir,     "OPENDIR"     },

    [FUSE_READDIR]     = { do_readdir,     "READDIR"     },

    [FUSE_RELEASEDIR]  = { do_releasedir,  "RELEASEDIR"  },

    [FUSE_DESTROY]     = { do_destroy,     "DESTROY"     }

};

 

Next, take a look at the implementation of do_read

static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)

{

    struct fuse_read_in *arg = (struct fuse_read_in *) inarg;

    // If the user implements the read operation, call the read in the user space, otherwise it will respond with an error if the call is not implemented. The op here is implemented when the user implements the file system and passed to fuse.

    if (req->f->op.read) {

        struct fuse_file_info fi;

        memset(&fi, 0, sizeof(fi));

        fi.fh = arg-> fh;

        fi.fh_old = fi.fh;

        req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);

    } else

        fuse_reply_err(req, ENOSYS);

}

 

It can be seen from the implementation here that these operations do not add any locks. If developers need file system locks, they need to consider themselves when implementing the file system.

 

What about fuse_operations?

It is impossible for developers without knowledge of kernel VFS to implement the fuse_lowlevel_ops group of interfaces. In order to enhance the versatility of fuse and enable more users to develop file systems using fuse, fuse provides a set of simpler interfaces fuse_operations , please refer to fuse.h for details. The parameters of this group of interfaces are very similar to the parameters of the system call provided by Unix, which is easier for developers to understand. Fuse wants developers to shield the underlying related objects and directly use the file name as a parameter. Only developers can use this group in their own way. The interface can be implemented, obviously this is much simpler than the implementation of the above group of interfaces.

int(*

getattr )(const char *, struct stat *)

int(*

readlink )(const char *, char *, size_t)

int(*

mknod )(const char *, mode_t, dev_t)

int(*

mkdir )(const char *, mode_t)

int(*

unlink )(const char *)

int(*

rmdir )(const char *)

int(*

symlink )(const char *, const char *)

int(*

rename )(const char *, const char *)

int(*

link )(const char *, const char *)

int(*

chmod )(const char *, mode_t)

int(*

chown )(const char *, uid_t, gid_t)

int(*

truncate )(const char *, off_t)

int(*

utime )(const char *, struct utimbuf *)

int(*

open )(const char *, struct fuse_file_info *)

int(*

read )(const char *, char *, size_t, off_t, struct fuse_file_info *)

int(*

write )(const char *, const char *, size_t, off_t, struct fuse_file_info *)

int(*

statfs )(const char *, struct statvfs *)

int(*

flush )(const char *, struct fuse_file_info *)

int(*

release )(const char *, struct fuse_file_info *)

int(*

fsync )(const char *, int, struct fuse_file_info *)

int(*

setxattr )(const char *, const char *, const char *, size_t, int)

int(*

getxattr )(const char *, const char *, char *, size_t)

int(*

listxattr )(const char *, char *, size_t)

int(*

removexattr )(const char *, const char *)

int(*

opendir )(const char *, struct fuse_file_info *)

int(*

readdir )(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)

int(*

releasedir )(const char *, struct fuse_file_info *)

int(*

fsyncdir )(const char *, int, struct fuse_file_info *)

void *(*

init )(struct fuse_conn_info *conn)

void(*

destroy )(void *)

int(*

access )(const char *, int)

int(*

create )(const char *, mode_t, struct fuse_file_info *)

int(*

ftruncate )(const char *, off_t, struct fuse_file_info *)

int(*

fgetattr )(const char *, struct stat *, struct fuse_file_info *)

int(*

lock )(const char *, struct fuse_file_info *, int cmd, struct flock *)

int(*

utimens )(const char *, const struct timespec tv[2])

int(*

bmap )(const char *, size_t blocksize, uint64_t *idx)

unsigned int

flag_nullpath_ok: 1

unsigned int

flag_reserved: 31

int(*

ioctl )(const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)

int(*

poll )(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)

 

Provide this set of interfaces, what does fuse do?

Fuse still implements a set of fuse_lowlevel_ops interfaces, in fuse.c

static struct fuse_lowlevel_ops fuse_path_ops = {

    //Only some methods are listed

    .init = fuse_lib_init,

    .destroy = fuse_lib_destroy,

    .lookup = fuse_lib_lookup,

    .forget = fuse_lib_forget,

    .getattr = fuse_lib_getattr,

    .setattr = fuse_lib_setattr,

.access = fuse_lib_access,

.read = fuse_lib_read,

    .readlink = fuse_lib_readlink

};

 

This set of interfaces implemented by fuse is different from the previous method. It does not do nothing. It completes part of the work, mainly the conversion relationship between file nodes and file names, and then uses the file name as a parameter to call the user-implemented fuse_operations interface .

 

Such as the implementation of fuse_lib_read

int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,

                 off_t off, struct fuse_file_info *fi)

{

fuse_get_context()->private_data = fs->user_data;

//method implemented by the user

    if (fs->op.read)

        return fs->op.read(path, buf, size, off, fi);

    else

        return -ENOSYS;

}

 

static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,

                          off_t off, struct fuse_file_info *fi)

{

    struct fuse *f = req_fuse_prepare(req);

    char *path;

    char *buf;

    int res;

 

    buf = (char *) malloc(size);

    if (buf == NULL) {

        reply_err(req, -ENOMEM);

        return;

    }

 

    res = -ENOENT;

pthread_rwlock_rdlock(&f->tree_lock); //fuse_operations uses a read-write lock

//Get path from ino

    path = get_path(f, ino);

    if (path != NULL) {

        struct fuse_intr_data d;

        if (f->conf.debug)

            fprintf(stderr, "READ[%llu] %lu bytes from %llu\n",

                    (unsigned long long) fi->fh, (unsigned long) size,

                    (unsigned long long) off);

 

        fuse_prepare_interrupt(f, req, &d);

        res = fuse_fs_read(f->fs, path, buf, size, off, fi); //Call the user-implemented method through this method

        fuse_finish_interrupt(f, req, &d);

        free(path);

    }

    pthread_rwlock_unlock(&f->tree_lock);

 

    if (res >= 0) {

        if (f->conf.debug)

            fprintf(stderr, "   READ[%llu] %u bytes\n",

                    (unsigned long long)fi->fh, res);

        if ((size_t) res > size)

            fprintf(stderr, "fuse: read too many bytes");

        fuse_reply_buf(req, buf, res); //return result

    } else

        reply_err(req, res);

 

    free(buf);

 

As can be seen from the above code, fuse uses read-write locks instead of mutexes for the fuse_operations group of operations, which is conducive to improving the execution efficiency of the file system. When the read-write lock is write-locked, all threads trying to lock the lock will be blocked until it is unlocked. When a read-write lock is in read-locked state, all threads trying to lock it in read mode can gain access, but if a thread wishes to lock this lock in write mode, it must block until all threads release the read Lock. The implementation of read-write locks may vary on different systems, but when the read-write lock is locked in read mode, if another thread tries to lock in write mode, the read-write lock usually blocks the next read. Mode lock request to avoid read mode lock being occupied for a long time, resulting in write mode lock request that cannot be satisfied for a long time.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326940417&siteId=291194637