Android内卡挂载之FUSE文件系统

一、简介

FUSE(Filesystem in Userspace),是一种用户空间文件系统。用户可以通过FUSE文件系统操作内卡。FUSE主要实现代码位于用户空间中,而不需要重新编译到内核,用户空间开发者可以通过FUSE的接口直接访问内核空间,不需要了解文件系统的内幕和内核模块编程的知识,这给用户空间开发者带来了众多便利。

二、FUSE文件系统架构

 

 

1.FUSE内核模块(内核态)实现VFS 接口(FUSE文件驱动注册、supper block、dentry、inode的维护),接收请求传递给LibFUSE,LibFUSE 再传递给用户程序的接口进行操作。

2.LibFUSE模块(用户态)实现文件系统主要框架,比如对实现的文件系统操作进行封装、mount管理、通过设备/dev/fuse与内核模块通信。

3.用户程序模块(用户态)当内卡挂载成功后,对内卡进行读写操作。

这种架构的设计可以让用户通过FUSE在用户空间来定制自己的文件系统,将文件系统从内核剥离出来,大大缩减了开发的难度。本文将着重介绍libfuse如何挂载内卡。

三、内卡的挂载

3.1 内卡挂载与分区挂载的不同

分区挂载是挂载到内核实地文件系统,例如userdata分区挂载f2fs到 /data目录下。内卡挂载是挂载用户空间文件系统,如dev/fuse 挂载fuse到mnt/user/0/emulated目录下。

 

上图mnt/user/0/emulated和/data/media/0下的内容是一样的。原因是这两个目录是绑定的关系,说明内卡是userdata的一部分。这部分空间是用户可以直接操作的。

在手机的文件管理器中也可以看到同样的目录:

 3.2 内卡挂载和绑定

 

VoldNativeService::mount接收到framwork层发送的mount请求后调用vol->mount,从而执行VolumeBase::mount这个父类。真正的实现是在子类内卡会调用EmulatedVolume::doMount执行挂载。

1.VoldNativeService::mount

mountFlags决定挂载的是内卡还是SD卡,为3时挂载内卡,为2时挂载SD卡。内卡的mountUserId为0,SD卡的mountUserId是卡本身的guid。最终会执行vol->mount()。

 

binder::Status VoldNativeService::mount(
         const std::string& volId, int32_t mountFlags, int32_t mountUserId,
         const android::sp<android::os::IVoldMountCallback>& callback) {
     ENFORCE_SYSTEM_OR_ROOT;
     CHECK_ARGUMENT_ID(volId);
     ACQUIRE_LOCK;
 
     auto vol = VolumeManager::Instance()->findVolume(volId);
     if (vol == nullptr) {
         return error("Failed to find volume " + volId);
     }
 
     vol->setMountFlags(mountFlags);
     vol->setMountUserId(mountUserId);
 
     vol->setMountCallback(callback);
     int res = vol->mount();
     vol->setMountCallback(nullptr);
 
     if (res != OK) {
         return translate(res);
     }
 
     return translate(OK);
 }

2.vol->mount

vol是VolumeBase的实例,VolumeBase的mount方法由具体的子类EmulatedVolume、PublicVolume、PrivateVolume等实现。执行操作之后会发送应答消息给MountService。将挂载的结果上报给framwork层。

status_t VolumeBase::mount() {
     if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
         LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
         return -EBUSY;
     }
     setState(State::kChecking);
     status_t res = doMount();
     setState(res == OK ? State::kMounted : State::kUnmountable);
 
     if (res == OK) {
         doPostMount();
     }
     return res;
 }

3.EmulatedVolume::doMount ()

内卡会走到EmulatedVolume这个子类进行挂载,SD卡则会走PublicVolume挂载。在EmulatedVolume函数里建立了四个/mnt/runtime路径并设置了不同的权限,原因是控制不同权限APP访问。然后利用挂载命名空间实现了挂载点的隔离,用户在不同挂载命名空间的进程,看到的目录层次不同。MountUserFuse是挂载FUSE的实现,内卡和SD卡都会走这个流程。着重看一下MountUserFuse函数的实参,如果挂载的是内卡,user_id则为0,getInternalPath()为/data/media,label为emulated。

status_t EmulatedVolume::doMount() {
     std::string label = getLabel();
     bool isVisible = getMountFlags() & MountFlags::kVisible;
     mSdcardFsDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str());
     mSdcardFsRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());
     mSdcardFsWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str());
     mSdcardFsFull = StringPrintf("/mnt/runtime/full/%s", label.c_str());
 
     setInternalPath(mRawPath);
     setPath(StringPrintf("/storage/%s", label.c_str()));

 ………………………………
res = MountUserFuse(user_id, getInternalPath(), label, &fd);

…………………………..
}

4.MountUserFuse();

如下函数中只粘贴了重要的部分。fuse_path是挂载路径mnt/user/0/emulated。随后调用mount函数调用内核接口进行挂载,将/dev/fuse 挂载到/mnt/user/0/emulated。

status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
                        const std::string& relative_upper_path, android::base::unique_fd* fuse_fd) {

std::string fuse_path(
             StringPrintf("%s/%s", pre_fuse_path.c_str(), relative_upper_path.c_str()));

result = TEMP_FAILURE_RETRY(mount("/dev/fuse", fuse_path.c_str(), "fuse",
                                       MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_LAZYTIME,
                                       opts.c_str()));
}

挂载成功后可以用mount命令去查看,截图如下:

 

四、总结

本文介绍了内卡对FUSE的挂载,将创建好的FUSE设备挂载到内置存储空间关联目录。对于内置存储空间的访问变成了先访问FUSE文件系统,再访问f2fs文件系统。对于FUSE而言,在内核空间和用户空间来回切换会增加性能开销,所以对FUSE的性能优化至关重要。

 

参考链接

​https://blog.csdn.net/kongxinsun/article/details/79587305​

​https://blog.csdn.net/bob_fly1984/article/details/80720807​

centos 挂载ntfs所需要的步骤 我就是在挂载2TB的时候发现的 linux挂载NTFS分区移动硬盘2010-09-23 16:35CentOS 挂载NTFS分区移动硬盘 1.uname -r 查看当前的linux内核版本. [root@localhost Desktop]# uname -r 2.6.18-128.el5 2.去http://www.linux-ntfs.org/下载与内核版本相同的ntfs补丁.具体的地址 3.安装补丁:kernel-module-ntfs-2.6.18-128.el5-2.1.27-0.rr.10.11.i686.rpm [root@localhost Desktop]# rpm -ivh kernel-module-ntfs-2.6.18-128.el5-2.1.27-0.rr.10.11.i686.rpm Preparing... ########################################### [100%] 1:kernel-module-ntfs-2.6.########################################### [100%] 4.使用fdisk -l查看硬盘的分区信息. 5.mkdir /mnt/xxx 在mnt文件夹里新建文件夹,分别对应于移动硬盘下的分区(xxx为文文件夹名) 如:mkdir /mnt/name1 对应于叫做name1的盘 6.mount -t ntfs /dev/sdyz /mnt/xxx 将移动硬盘下的各分区挂载在新建的文件夹里(sdyz为硬盘的分区号码) 如: [root@localhost Desktop]# mount -t ntfs /dev/sdc1 /mnt/TheLORD,OurGod 今天将USB移动硬盘挂在CentOS上准备将压缩包拷贝下来的。 结果挂载移动硬盘的时候却提示: mount: unknown filesystem type ‘ntfs’ 原因:Linux无法识别NTFS格式的分区。 解决: 因为刚刚将CentOS升级到了2.6.18-164.el5内核,无法使用Kernel NTFS Module挂载Windows下的NTFS分区(没有在开源站点上找到相应内核包),所以只有使用ntfs-3g来解决了。 打开ntfs-3g的下载站点,将最新稳定版(当前最新版本为ntfs-3g-2010.3.6)下载到CentOS,执行以下命令安装: 1、编译安装ntfs-3g: #./configure #make #make install [/code] 2、查看USB设备点: #fdisk -l Disk /dev/sdb: 60.0 GB, 60011642880 bytes 255 heads, 63 sectors/track, 7296 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Device Boot Start End Blocks Id System /dev/sdb1 * 1 653 5245191 b W95 FAT32 /dev/sdb2 654 7295 53351865 f W95 Ext'd (LBA) /dev/sdb5 654 1958 10482381 b W95 FAT32 /dev/sdb6 1959 7295 42869421 7 HPFS/NTFS 3、挂载NTFS分区: #mount -t ntfs-3g /dev/sdb6 /mnt/win
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值