深入分析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。

主要成员变量

c 复制代码
MediaProviderWrapper mp;         ---用于JNI和JAVA层通信

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

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

struct fuse

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

主要成员变量

lua 复制代码
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函数里面初始化。

主要成员变量

arduino 复制代码
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函数里面初始化。

主要成员变量

sql 复制代码
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链表中。

主要成员变量

lua 复制代码
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结构体里面的一个变量,所以也是一个线程就对应一个实例。

arduino 复制代码
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函数,正常情况下这个函数一直不会退出。

rust 复制代码
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文件系统出现错误。

ini 复制代码
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

ini 复制代码
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个线程,这样既充分的利用多线程来提高峰值处理速度,又保证了不会反复进行线程的创建和删除。

markdown 复制代码
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; 

}

索引

回首页

相关推荐
数据猎手小k7 分钟前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小1043 分钟前
JavaWeb项目-----博客系统
android
风和先行1 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.2 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰3 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶3 小时前
Android——网络请求
android
干一行,爱一行3 小时前
android camera data -> surface 显示
android
断墨先生3 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员5 小时前
PHP常量
android·ide·android studio
萌面小侠Plus6 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机