一、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 = ∓
// 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_dev_do_read()处,FuseDaemon睡眠等待, 线程在此处阻塞
-
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;
}