Android Fuse文件系统-5:FuseDaemon-start

1,365 阅读3分钟

一、framework

StorageManagerService 到 ExternalStorageServiceImpl 流程

fuse文件系统挂载完成后会回调 StorageManagerService .mount 代码中的 onVolumeChecking 方法

之后的流程如下:

// frameworks/base/services/core/java/com/android/server/StorageManagerService.java
// 此方法是由 native 层的 vold 在 EmulatedVolume::doMount() 中回调过来
boolean onVolumeChecking(FileDescriptor fd, String path, String internalPath);
|	mStorageSessionController.onVolumeMount(pfd, vol);
|-->void onVolumeMount(ParcelFileDescriptor deviceFd, VolumeInfo vol) throws ExternalStorageServiceException;
	|	StorageUserConnection connection = new StorageUserConnection(mContext, userId, this);
	|	connection.startSession(sessionId, deviceFd, vol.getPath().getPath(), vol.getInternalPath().getPath());
	|-->void startSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
            				  String lowerPath) throws ExternalStorageServiceException;
		|	mActiveConnection.startSessionLocked(session, pfd);
		|-->void startSessionLocked(Session session, ParcelFileDescriptor fd) throws ExternalStorageServiceException;
			// mRemote是ExternalStorageServiceImpl服务,ExternalStorageServiceImpl继承ExternalStorageService,
			// 其 onBind 返回的是 ExternalStorageServiceWrapper类的实例
			// 此处调用 ExternalStorageServiceWrapper 的 startSession
			|	mRemote.startSession(session.sessionId,
            |          FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
            |          fd, session.upperPath, session.lowerPath, new RemoteCallback(result -> setResultLocked(latch, result)));
			|-->ExternalStorageServiceWrapper.startSession(...)	;
				|	onStartSession(sessionId, flag, deviceFd, new File(upperPath), new File(lowerPath));
				// 再调用实现类的 onStartSession ,其实现在 ExternalStorageServiceImpl 中
				// ExternalStorageServiceImpl.onStartSession
				|-->void onStartSession(String sessionId, /* @SessionFlag */ int flag, @NonNull ParcelFileDescriptor deviceFd,
                                        @NonNull File upperFileSystemPath, @NonNull File lowerFileSystemPath);
					|	FuseDaemon daemon = new FuseDaemon(mediaProvider, this, deviceFd, sessionId,
                    |                                       upperFileSystemPath.getPath());
					|	daemon.start();// 启动 FuseDaemon 守护线程

二、MediaProvider App

ExternalStorageServiceImpl.java

onStartSession 内容很简单就是启动FuseDaemon的线程,即 Fuse 用户空间的守护线程

//packages/providers/MediaProvider/src/com/android/providers/media/fuse/ExternalStorageServiceImpl.java
//com.android.providers.media.fuse.ExternalStorageServiceImpl
public final class ExternalStorageServiceImpl extends ExternalStorageService {
    //...
	@Override
    public void onStartSession(String sessionId, /* @SessionFlag */ int flag,
            @NonNull ParcelFileDescriptor deviceFd, @NonNull File upperFileSystemPath,
            @NonNull File lowerFileSystemPath) {
        MediaProvider mediaProvider = getMediaProvider();

        synchronized (sLock) {
            if (sFuseDaemons.containsKey(sessionId)) {
                Log.w(TAG, "Session already started with id: " + sessionId);
            } else {
                Log.i(TAG, "Starting session for id: " + sessionId);
                // We only use the upperFileSystemPath because the media process is mounted as
                // REMOUNT_MODE_PASS_THROUGH which guarantees that all /storage paths are bind
                // mounts of the lower filesystem.
                FuseDaemon daemon = new FuseDaemon(mediaProvider, this, deviceFd, sessionId,
                        upperFileSystemPath.getPath());
                daemon.start();//FuseDaemon 继承 Thread 类,启动Fuse守护线程,调用FuseDaemon的run方法
                sFuseDaemons.put(sessionId, daemon);
            }
        }
    }
   //... 
}

FuseDaemon.java

run方法的内容也很简单,就是调用 native层的 FuseDaemon::Start 方法

// com.android.providers.media.fuse.FuseDaemon.java
public final class FuseDaemon extends Thread { // 继承线程类
    //...
	public FuseDaemon(@NonNull MediaProvider mediaProvider,
            @NonNull ExternalStorageServiceImpl service, @NonNull ParcelFileDescriptor fd,
            @NonNull String sessionId, @NonNull String path) {
        mMediaProvider = Objects.requireNonNull(mediaProvider);
        mService = Objects.requireNonNull(service);
        setName(Objects.requireNonNull(sessionId));
        mFuseDeviceFd = Objects.requireNonNull(fd).detachFd();
        mPath = Objects.requireNonNull(path);
    }

    /** Starts a FUSE session. Does not return until the lower filesystem is unmounted. */
    @Override
    public void run() {
        final long ptr;
        synchronized (mLock) {
            mPtr = native_new(mMediaProvider);
            if (mPtr == 0) {
                throw new IllegalStateException("Unable to create native FUSE daemon");
            }
            ptr = mPtr;
        }

        Log.i(TAG, "Starting thread for " + getName() + " ...");
        //调用native层的 FuseDaemon::Start 方法
        native_start(ptr, mFuseDeviceFd, mPath); // Blocks
        Log.i(TAG, "Exiting thread for " + getName() + " ...");

        synchronized (mLock) {
            native_delete(mPtr);
            mPtr = 0;
        }
        mService.onExitSession(getName());
        Log.i(TAG, "Exited thread for " + getName());
    }
    //...
}

FuseDaemon.cpp

// packages\providers\MediaProvider\jni\FuseDaemon.cpp
void FuseDaemon::Start(android::base::unique_fd fd, const std::string& path) {
    android::base::SetDefaultTag(LOG_TAG);

    struct fuse_args args;
    struct fuse_cmdline_opts opts;

    struct stat stat;

    if (lstat(path.c_str(), &stat)) {
        PLOG(ERROR) << "ERROR: failed to stat source " << path;
        return;
    }

    if (!S_ISDIR(stat.st_mode)) {
        PLOG(ERROR) << "ERROR: source is not a directory";
        return;
    }

    //配置参数
    args = FUSE_ARGS_INIT(0, nullptr);
    if (fuse_opt_add_arg(&args, path.c_str()) || fuse_opt_add_arg(&args, "-odebug") ||
        fuse_opt_add_arg(&args, ("-omax_read=" + std::to_string(MAX_READ_SIZE)).c_str())) {
        LOG(ERROR) << "ERROR: failed to set options";
        return;
    }

    struct fuse fuse_default(path);
    fuse_default.mp = &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));
    if (fuse_default.zero_addr == MAP_FAILED) {
        LOG(FATAL) << "mmap failed - could not start fuse! errno = " << errno;
    }

    // Custom logging for libfuse
    if (android::base::GetBoolProperty("persist.sys.fuse.log", false)) {
        fuse_set_log_func(fuse_logger);
    }
    // 是否启用 passthrough 
#if SUPPROT_PASSTHOUGH
    fuse->passthrough = android::base::GetBoolProperty("persist.sys.fuse.passthrough.enable", true);
    if (fuse->passthrough) {
        LOG(INFO) << "Using FUSE passthrough";
    }
#endif
    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...";
    // =======================开启无限循环读取数据,处理上层目录/storage/emulated文件请求=============!
    fuse_session_loop_mt(se, &config);
    fuse->active->store(false, std::memory_order_release);
    LOG(INFO) << "Ending fuse...";

    if (munmap(fuse_default.zero_addr, MAX_READ_SIZE)) {
        PLOG(ERROR) << "munmap failed!";
    }

    fuse_opt_free_args(&args);
    fuse_session_destroy(se);
    LOG(INFO) << "Ended fuse";
    return;
}

三、libfuse库

external\libfuse

  • fuse_session_receive_buf_int()函数会读/dev/fuse,

    • FuseDaemon读取 /dev/fuse 时会调用到内核 fuse_dev_read 方法
    • fuse_dev_read 调用 fuse_dev_do_read()
    • fuse_dev_do_read: kernel\msm-5.4\fs\fuse\dev.c
      • 在fuse_dev_do_read()处,FuseDaemon睡眠等待, 线程在此处阻塞
        • 有请求时会被唤醒,就从的for(;;)循环中跳出,然后从队列中取出请求,复制到用户空间
  • fuse_session_process_buf_int():处理fuse请求数据,并把结果通过/dev/fuse传回kernel

1、fuse_session_loop_mt_32 多线程循环读取 /dev/fuse 内容

//__asm__(".symver fuse_session_loop_mt_32,fuse_session_loop_mt@@FUSE_3.2");
//内联汇编: fuse_session_loop_mt_32 为3.2版本的 fuse_session_loop_mt 实现
// external\libfuse\Android.bp cflags "-DFUSE_USE_VERSION=34"
// external\libfuse\lib\fuse_versionscript 中定义了 FUSE_3.4(继承了FUSE_3.2)
// 综上,外界调用 fuse_session_loop_mt,实质是调用 fuse_session_loop_mt_32
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;

	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_init

	pthread_mutex_lock(&mt.lock);
    //核心代码
	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;
}

static int fuse_loop_start_thread(struct fuse_mt *mt)
{
	int res;

	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;
		}
	}
	//核心代码-------内部实质调用的 pthread_create 创建线程,并运行 fuse_do_work 函数
	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->main 元素的前边
	mt->numavail ++;
	mt->numworker ++;

	return 0;
}


static void *fuse_do_work(void *data)
{
	struct fuse_worker *w = (struct fuse_worker *) data;
	struct fuse_mt *mt = w->mt;

	while (!fuse_session_exited(mt->se)) {// FuseDaemon 主要在此处循环工作
		int isforget = 0;
		int res;

		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        //--------------------核心代码--------从/dev/fuse读取数据
		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--;
		if (mt->numavail == 0)
			fuse_loop_start_thread(mt);
		pthread_mutex_unlock(&mt->lock);
		//--------------------核心代码--------处理数据
		fuse_session_process_buf_int(mt->se, &w->fbuf, w->ch);

		pthread_mutex_lock(&mt->lock);
		if (!isforget)
			mt->numavail++;
		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;
}