深入分析Android 11 FUSE文件系统(二)

FuseDaemon分析

上文已经介绍了在Android 11的FUSE文件系统和之前的FUSE文件系统有很多不一样,在Android 11整个FUSE文件系统的用户态Daemon是在MediaProvider里面实现的,是用JNI来实现的。而FUSE文件系统的核心代码的实现都是在libfuse中,由FuseDaemon来调用的。

MediaProvider的代码路径是packages/providers/MediaProvider

libfuse的代码路径是external/libfuse

FuseDaemon主要数据结构

Figure 2-1 FuseDaemon主要数据结构关系图

Figure 2-1给出了在FuseDaemon里面有一些主要的数据结构和相互关系,为了简化图中并没有列出所有的成员变量,只是把主要的成员列了出来。

FuseDaemon

FuseDaemon是JNI层的一个类,开机MediaProvider就会为每一个用户(u0, u10等),创建一个类的实例,直到关机。Fuse文件系统的用户态Daemon入口就是FuseDaemon::Start。

主要成员变量

MediaProviderWrapper mp;         ---用于JNI和JAVA层通信










std::atomic_bool active;         ---FuseDaemon状态标志










struct ::fuse* fuse;             ---指向struct fuse实例的指针










struct fuse

struct fuse是JNI里面定义的和FUSE文件系统相关的一个结构体,只有一个实例,在FuseDaemon::Start函数里面初始化。

主要成员变量

MediaProviderWrapper* mp;    ---指向MediaProviderWrapper的指针










struct fuse_session* se;     ---指向fuse_session的指针










char* zero_addr;             ---指向一块全0的区域,用来加速fuse的读操作










std::atomic_bool* active;    ---指向FuseDaemon状态标志的指针

struct fuse_session

struct fuse_session是libfuse里面的结构体,保存fuse文件系统的整体信息,只有一个实例,在FuseDaemon::Start函数里面初始化。

主要成员变量

struct fuse_req list;          ---普通request链表










struct fuse_req interrupts;    ---interrupts request链表










struct fuse_notify_req notify_list;    ---notify request链表










void *userdata;                        ---指向struct fuse的指针








pthread_key_t pipe_key;    ---多线程私有数据,用来保存fuse_ll_pipe结构,加速功能使用







volatile int exited;           ---fuse session结束标志







struct fuse_lowlevel_ops op;   ---底层文件系统操作函数接口

struct fuse_mt

struct fuse_mt是FUSE多线程工作模式的总的结构体,只有一个实例,在fuse_session_loop_mt函数里面初始化。

主要成员变量

int numworker;     ---fuse_worker数目










int numavail;      ---可用线程数目










struct fuse_session *se;     ---指向fuse_session的指针 










struct fuse_worker main;     ---fuse_worker链表头 








sem_t finish;     ---结束信号量标志







int exit;         ---退出标志







int clone_fd;     ---/dev/fuse节点clone的fd





int max_idle;     ---最大idle线程数目

struct fuse_worker

struct fuse_worker是一个链表节点数据结构,每次执行fuse_loop_start_thread函数时会初始化一个实例,所有的实例都会添加到fuse_mt->main链表中。

主要成员变量

struct fuse_worker *prev;    ---双向链表指针










struct fuse_worker *next;    ---双向链表指针










pthread_t thread_id;         ---线程ID   










size_t bufsize;              ---Buffer大小








struct fuse_buf fbuf;        ---数据Buffer结构体







struct fuse_chan *ch;        ---clone fd的结构体







struct fuse_mt *mt;          ---指向fuse_mt实例的指针 

struct fuse_buf

struct fuse_buf是一个Fuse数据Buffer的结构体,是fuse_worker结构体里面的一个变量,所以也是一个线程就对应一个实例。

size_t size;                    ---data size










enum fuse_buf_flags flags;      ---buffer flag










void *mem;                      ---内存地址,当FUSE_BUF_IS_FD没有设置时有效










int fd;                         ---fd,当FUSE_BUF_IS_FD设置时有效








off_t pos;                      ---文件offset, 当FUSE_BUF_FD_SEEK设置时有效

FuseDaemon整体软件流程

Figure 2-2 FuseDaemon软件流程图

FuseDaemon::Start

JNI层FuseDaemon类是FUSE文件系统在用户空间的Daemon实现,开机后MediaProvider的Java层代码会调用JNI层代码,启动FuseDaemon,FuseDaemon的主要实现就是FuseDaemon::Start函数,这就是FuseDaemon的入口,这个函数做了一些初始化操作后,就调用了fuse_session_loop_mt函数,正常情况下这个函数一直不会退出。

void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) { 










    ... 










    // fuse_default初始化










    struct fuse fuse_default(path); 








    fuse_default.mp = ∓ 







    // fuse_default is stack allocated, but it's safe to save it as an instance variable because 







    // this method blocks and FuseDaemon#active tells if we are currently blocking 





    fuse = &fuse_default; 




 





    // Used by pf_read: redacted ranges are represented by zeroized ranges of bytes, 




    // so we mmap the maximum length of redacted ranges in the beginning and save memory allocations 




    // on each read. 




    fuse_default.zero_addr = static_cast<char*>(mmap( 




            NULL, MAX_READ_SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, /*fd*/ -1, /*off*/ 0)); 




    ... 




    // Custom logging for libfuse 




    if (android::base::GetBoolProperty("persist.sys.fuse.log", false)) { 




        fuse_set_log_func(fuse_logger); 




    } 




 




    // fuse_session实例初始化




    struct fuse_session 




            * se = fuse_session_new(&args, &ops, sizeof(ops), &fuse_default); 




    if (!se) { 




        PLOG(ERROR) << "Failed to create session "; 




        return; 




    } 




    fuse_default.se = se; 




    fuse_default.active = &active; 




    se->fd = fd.release();  // libfuse owns the FD now 




    se->mountpoint = strdup(path.c_str()); 




 




    // Single thread. Useful for debugging 




    // fuse_session_loop(se); 




    // Multi-threaded 




    LOG(INFO) << "Starting fuse..."; 



    // fuse文件系统处理,这个函数一直不会退出



    fuse_session_loop_mt(se, &config); 



 



    return; 



} 

fuse_session_loop_mt

在本例中FUSE_USE_VERSION是34,所以fuse_session_loop_mt对应的函数是fuse_session_loop_mt_32。fuse_session_loop_mt_32做的主要就是初始化fuse_mt的实例,然后启动一个FUSE文件系统处理线程,然后就是通过等待一个信号量的方式一直挂起,直到关机或者FUSE文件系统出现错误。

FUSE_SYMVER(".symver fuse_session_loop_mt_32,fuse_session_loop_mt@@FUSE_3.2"); 










int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config) 










{ 










        int err; 








        struct fuse_mt mt; 







        struct fuse_worker *w; 







     





        // fuse_mt 实例初始化




        memset(&mt, 0, sizeof(struct fuse_mt)); 




        mt.se = se; 




        mt.clone_fd = config->clone_fd; 




        mt.error = 0; 




        mt.numworker = 0; 




        mt.numavail = 0; 




        mt.max_idle = config->max_idle_threads; 




        mt.main.thread_id = pthread_self(); 




        mt.main.prev = mt.main.next = &mt.main; 




        sem_init(&mt.finish, 0, 0); 




        fuse_mutex_init(&mt.lock); 




     









        pthread_mutex_lock(&mt.lock); 




        // 启动一个FUSE处理线程




        err = fuse_loop_start_thread(&mt); 




        pthread_mutex_unlock(&mt.lock); 




        if (!err) { 




                /* sem_wait() is interruptible */ 




                 // 通过等一个信号量一直等待, 正常情况下不会退出 




                while (!fuse_session_exited(se)) 




                        sem_wait(&mt.finish); 




 




                pthread_mutex_lock(&mt.lock); 




                for (w = mt.main.next; w != &mt.main; w = w->next) 




                        pthread_cancel(w->thread_id); 




                mt.exit = 1; 




                pthread_mutex_unlock(&mt.lock); 



 



                while (mt.main.next != &mt.main) 



                        fuse_join_worker(&mt, mt.main.next); 



 



                err = mt.error; 


        } 


 


        pthread_mutex_destroy(&mt.lock); 


        sem_destroy(&mt.finish); 


        if(se->error != 0) 


                err = se->error; 


        fuse_session_reset(se); 


        return err; 


} 

fuse_loop_start_thread

fuse_loop_start_thread的功能就是分配一个fuse_worker的实例,然后启动一个线程,把fuse_worker的实例作为参数传给线程处理函数fuse_do_work

static int fuse_loop_start_thread(struct fuse_mt *mt) 










{ 











        int res; 










        // 分配一个新的struct fuse_worker并进行初始化








        struct fuse_worker *w = malloc(sizeof(struct fuse_worker)); 







        if (!w) { 







                fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n"); 





                return -1; 




        } 




        memset(w, 0, sizeof(struct fuse_worker)); 




        w->fbuf.mem = NULL; 




        w->mt = mt; 




 




        w->ch = NULL; 




        if (mt->clone_fd) { 




                w->ch = fuse_clone_chan(mt); 




                if(!w->ch) { 




                        /* Don't attempt this again */ 




                        fuse_log(FUSE_LOG_ERR, "fuse: trying to continue " 




                                "without -o clone_fd.\n"); 




                        mt->clone_fd = 0; 




                } 




        } 




        // 启动FUSE处理线程,fuse_worker结构体实例作为参数传入




        res = fuse_start_thread(&w->thread_id, fuse_do_work, w); 




        if (res == -1) { 




                fuse_chan_put(w->ch); 




                free(w); 




                return -1; 




        } 




        list_add_worker(w, &mt->main); 




        mt->numavail ++; 




        mt->numworker ++; 




 




        return 0; 




}

fuse_do_work

fuse_do_work是FUSE文件系统的核心工作线程,完成FUSE文件系统相关的所有命令的处理。简单说来就是从/dev/fuse节点读取数据,解析数据,再把处理结果回写到/dev/fuse节点。fuse_do_work里面有一个很巧妙的设计,就是在发现没有可用的工作线程后,会调用fuse_loop_start_thread来创建一个新的线程,而在空闲进程超过max_idle(本例中是10)的时候,则会结束当前的线程。因此在FUSE文件系统中如果操作很繁忙的时候FUSE工作线程的数目会快速增长,然后再空闲下来的时候仍然保持max_idle个线程,这样既充分的利用多线程来提高峰值处理速度,又保证了不会反复进行线程的创建和删除。

static void *fuse_do_work(void *data) 










{ 











        struct fuse_worker *w = (struct fuse_worker *) data; 










        struct fuse_mt *mt = w->mt; 








        // 判读FUSE结束标志,只有关机或者出现错误的时候才会设置,正常情况下一直循环







        while (!fuse_session_exited(mt->se)) { 







                int isforget = 0; 





                int res; 




 





                pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);




                // 分配receive_buf,并从/dev/fuse读取数据,如果没有数据到来会在阻塞在read函数 




                res = fuse_session_receive_buf_int(mt->se, &w->fbuf, w->ch); 




                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 




                if (res == -EINTR) 




                        continue; 




                if (res <= 0) { 




                        if (res < 0) { 




                                fuse_session_exit(mt->se); 




                                mt->error = res; 




                        } 




                        break; 




                } 




 




                pthread_mutex_lock(&mt->lock); 




                if (mt->exit) { 




                        pthread_mutex_unlock(&mt->lock); 




                        return NULL; 




                } 




 




                /* 




                 * This disgusting hack is needed so that zillions of threads 




                 * are not created on a burst of FORGET messages 




                 */ 




                if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) { 




                        struct fuse_in_header *in = w->fbuf.mem; 




 



                        if (in->opcode == FUSE_FORGET || 



                            in->opcode == FUSE_BATCH_FORGET) 



                                isforget = 1; 



                } 



 


                if (!isforget) 


                        mt->numavail--; 


                // 如果现在没有可用的线程,则新建一个FUSE的工作线程 


                if (mt->numavail == 0){ 


                        fuse_loop_start_thread(mt); 


                }             


                pthread_mutex_unlock(&mt->lock); 


                // 完成了Fuse基本命令的解析处理,并把结果写入/dev/fuse节点


                fuse_session_process_buf_int(mt->se, &w->fbuf, w->ch); 

 

                pthread_mutex_lock(&mt->lock); 

                if (!isforget) 

                        mt->numavail++; 

                // 如果当前可用线程数大于max_idle,则结束当前这个工作线程

                if (mt->numavail > mt->max_idle) { 

                        if (mt->exit) { 

                                pthread_mutex_unlock(&mt->lock); 

                                return NULL; 

                        } 

                        list_del_worker(w); 

                        mt->numavail--; 

                        mt->numworker--; 

                        pthread_mutex_unlock(&mt->lock); 

 

                        pthread_detach(w->thread_id); 

                        free(w->fbuf.mem); 

                        fuse_chan_put(w->ch); 

                        free(w); 

                        return NULL; 

                } 

                pthread_mutex_unlock(&mt->lock); 

        } 

 

        sem_post(&mt->finish); 

 

        return NULL; 

}

索引

回首页

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYjlqbuN' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片