原文地址:http://blog.csdn.net/HellDevil/archive/2010/05/18/5604421.aspx
http://blog.csdn.net/HellDevil/archive/2010/05/06/5561918.aspx
http://blog.csdn.net/HellDevil/archive/2010/01/19/5213888.aspx
http://blog.csdn.net/HellDevil/archive/2010/05/15/5596196.aspxAndroid usb驱动模块 g_android.ko rmmod issues
我最近在调试Android系统的gadget功能,通过make menuconfig发现gadget是编译成module模式的。里面有很多可选项,其中有个Android的选项,其实就是提供ADB和Storage的功能的。
具体查看代码在/drivers/usb/gadget下,三个文件:android.c f_adb.c f_mass_storage.c
g_android.ko 是由这三个文件而来,其中android.c 依赖于f_adb.c 和f_mass_storage.c(这两个文件之间无依赖关系)
可以在android.c中的
view plaincopy to clipboardprint?
- static int __init android_bind_config(struct usb_configuration *c)
- {
- struct android_dev *dev = _android_dev;
- int ret;
- printk(KERN_DEBUG "android_bind_config\n");
- ret = mass_storage_function_add(dev->cdev, c, dev->nluns);
- if (ret)
- return ret;
- return adb_function_add(dev->cdev, c);
- }
看到关系
============================================================
下面说说我的问题,
1.当我将Android编译成module模式,既g_android.ko时候,然后放入rootfs中手动加载,Okay, no problem!非常happy。但是,当我关闭adb进程,rmmod g_android的时候,出现死锁状态,打开代码进行查询,发现问题出现在
view plaincopy to clipboardprint?
- struct usb_request *req_get(struct adb_dev *dev, struct list_head *head, int DD)
- {
- unsigned long flags;
- struct usb_request *req;
- spin_lock_irqsave(&dev->lock, flags);//被锁了,郁闷
- if (list_empty(head)) {
- req = 0;
- } else {
- req = list_first_entry(head, struct usb_request, list);
- list_del(&req->list);
- }
- spin_unlock_irqrestore(&dev->lock, flags);
- return req;
- }
一直死锁,通过打开kernel hacking中的spinlock debug发现 是自己把自己锁了,找阿找阿。。。。
看了下rmmod时候 函数调用过程当我rmmod的时候,首先会去执行__exit cleanup(void)中的usb_composite_unregister(&android_usb_driver)这句话中还调用 composite.c中的函数 (不细说),反正最后会调到f_adb.c中的
adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
看看这个函数
view plaincopy to clipboardprint?
- static void
- adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
- {
- struct adb_dev *dev = func_to_dev(f);
- struct usb_request *req;
- spin_lock_irq(&dev->lock);//锁死你个req_get,谁叫我们共用一个dev->lock
- while ((req=req_get(dev, &dev->rx_idle,0)))//被所到死
- {
- adb_request_free(req, dev->ep_out);
- }
- while ((req=req_get(dev, &dev->tx_idle,0)))
- {
- adb_request_free(req, dev->ep_in);
- }
- dev->online = 0;
- dev->error = 1;
- spin_unlock_irq(&dev->lock);
- misc_deregister(&adb_device);
- kfree(_adb_dev);
- _adb_dev = NULL;
- }
所以感觉 req_get代码中的spinlock有问题,没办法,只能写了个req_get_free_lock(不加锁)替代while循环中的req_get函数
rebuild 内核,Okay 卸载g_android.ko 没问题 哈哈。正当自己高兴的时候,发现乐极生悲了。。。
===========================================================
卸载完了g_android.ko 我再次insmod g_android.ko 挂了,不过不是kernel panic而是提示
"sysfs: duplicate filename 'usb_mass_storage' can not be created"
恩 这次出在f_mass_storage.c这个文件中,根据信息提示应该是没因为上次没卸载完全。没办法,在几个与insmod和rmmod的函数中加入 打印信息。主要是mass_storage_function_add, fsg_function_bind,fsg_function_unbind 三个函数,前两个是跟insmod有关,后者跟rmmod有关。
通过打印信息发现一个Android开发者的 小纰漏 导致的问题,就是在
view plaincopy to clipboardprint?
- int __init mass_storage_function_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c, int nluns)
- {
- int rc;
- struct fsg_dev *fsg;
- printk(KERN_INFO "mass_storage_function_add\n");
- rc = fsg_alloc();
- if (rc)
- return rc;
- fsg = the_fsg;
- fsg->nluns = nluns;
- spin_lock_init(&fsg->lock);
- init_rwsem(&fsg->filesem);
- kref_init(&fsg->ref);
- init_completion(&fsg->thread_notifier);
- the_fsg->buf_size = BULK_BUFFER_SIZE;
- the_fsg->sdev.name = DRIVER_NAME;
- the_fsg->sdev.print_name = print_switch_name;
- the_fsg->sdev.print_state = print_switch_state;
- rc = switch_dev_register(&the_fsg->sdev);//卸载的时候没有swith_dev_unregister
- if (rc < 0)
- goto err_switch_dev_register;
- wake_lock_init(&the_fsg->wake_lock, WAKE_LOCK_SUSPEND,
- "usb_mass_storage");
- fsg->cdev = cdev;
- fsg->function.name = shortname;
- fsg->function.descriptors = fs_function;
- fsg->function.bind = fsg_function_bind;
- fsg->function.unbind = fsg_function_unbind;
- fsg->function.setup = fsg_function_setup;
- fsg->function.set_alt = fsg_function_set_alt;
- fsg->function.disable = fsg_function_disable;
- rc = usb_add_function(c, &fsg->function);
- if (rc != 0)
- goto err_usb_add_function;
- return 0;
- err_usb_add_function:
- switch_dev_unregister(&the_fsg->sdev);
- err_switch_dev_register:
- kref_put(&the_fsg->ref, fsg_release);
- return rc;
- }
在掉用过/drivers/switch/class_switch.c 中的 switch_dev_register后,除了出错处理再也没作擦屁股的动作(unregister)汗,这不是占着XX不拉什么吗?没办法,去 fsg_function_unbind函数中的
view plaincopy to clipboardprint?
- for (i = 0; i < fsg->nluns; ++i) {
- curlun = &fsg->luns[i];
- if (curlun->registered) {
- switch_dev_unregister(&the_fsg->sdev);//加入擦屁股的一句
- device_remove_file(&curlun->dev, &dev_attr_file);
- device_unregister(&curlun->dev);
- curlun->registered = 0;
- }
- }
Okay 万事Okay 主阿!其实都不是什么大的问题拉。但是导致的现象确很恐怖。android developer太忙了,毕竟也是人阿。呵呵,以前都不敢改他们代码,总是推敲自己是不是哪边错了。
Android usb client mass-storage不能自动挂载到主机端的问题(1)
测试平台:Nvidia ARM架构
操作系统:Andorid(Nvidia修改)
最近在搞Android usb client,发现当将client线分别接上Android系统机器的client端以及主机Host端的时候,Android的USB client两大功能中的ADB可以使用,而mass-storage无法使用,即装有Android系统的机器不能将自己的sdhc卡,以及自身的内存 卡挂到host主机端,并且显示。
输入 ls -l /dev/sd*
发现终端显示了个叫/dev/sdb 的节点,尝试mount ,提示 无法mount 未知的文件系统。
Okay,查看Android系统端的init.rc发现 挂载方式是vold.
查看vold原代码,发现需要读取vold.conf,并且检测UMS
=======================================
那么大致猜测下和问题有关的可能是Android系统的这么几个:
1.rootfs /etc/vold.conf配置可能有问题
2.vold 的程序可能有问题
3.linux driver /driver/usb/gadget/f_mass_storage.c有问题
4.UMS功能可能有问题
=======================================
大致猜测了问题的所在,那么下一 步就是一步一步确认了。
1.先看看/etc/vold.conf文件,代码在/out/target/product/XX/system/etc/vold.conf
view plaincopy to clipboardprint?
- volume_sdcard {
- media_path /devices/platform/tegra-sdhci.3/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
- media_type mmc
- mount_point /sdcard
- }
- UMS的路径没有,个人经验:一般可以在/sys/devices/platform/
- 下面找到一个叫包含"udc"字符的节点,进入它的子目录,会看到lun0的节点。
进入/sys/devices/platform
这时候会有两种现象出现:1,就是有相应的节点;2,没有
1.如果有,则应该确定该节点是否和你的f_mass_storage驱动相对应,也就是说,这个节点是你的mass_storage驱动创建的,如果不是需要你去创建。
2.如果没有,那么需要在f_mass_storage.c里面去创建它。
我的情况是第一个,目录下有个叫shdc-udc.0/lun0的节点,但是并没有和我的g_android中的mass-storage驱动对应,所以我必须重新创建个。
在nvidia提供给我门的/arch/arm/mach-XXX/nvodm_board.c中会有一系列的device_register的动作
创建/sys/devices/platform下的节点 在这需要加入两句
view plaincopy to clipboardprint?
- static struct platform_device fsg_platform_device =
- {
- .name = "usb_mass_storage",
- .id = -1,
- };
- static void __init tegra_machine_init(void)
- {
- ....
- (void) platform_device_register(&fsg_platform_device);
- ....
- }
- 加入这两句okay
我们会发现/sys/devices/platform/usb_mass_storage/lun0这个目录产生了
这些动作是和你的linux driver中mass-storage的驱动内容相对应的。
最后一步,就是需要将新创建的UMS(负责启动usb mass-storage功能) Path加入到你的vold.conf中
view plaincopy to clipboardprint?
- volume_sdcard {
- media_path /devices/platform/tegra-sdhci.3/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
- media_type mmc
- mount_point /sdcard
- ums_path /devices/platform/usb_mass_storage/lun0
- //加的代码就是这个
- }
===========================
打开机器,灌入系统,启动,发现还是不行,在ADB终端中输入sdutil ums enabel 启动ums 实际上我机器已经在开机就启动了UMS功能
logcat显示UMS 挂掉了 一大堆的 堆栈问题 stack 什么的。
估计和内存分配或者指针有关。
估计是vold的代码有问题。
进入android源代码目录/system/core/vold
打开该目录下所有代码,加入打印语句,进行跟踪。
发现当你ums enable时候
1.会进入cmd_dispatch.c执行do_set_ums_enable函数
view plaincopy to clipboardprint?
- static int do_set_ums_enable(char *cmd)
- {
- ...
- if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS))
- return volmgr_enable_ums(true);
- return volmgr_enable_ums(false);
- }
它又调用了volmgr.c文件中的volmgr_enable_ums(bool enable)
view plaincopy to clipboardprint?
- int volmgr_enable_ums(bool enable)
- {
- ...
- volume_t *v = vol_root //链表,链表中存储了每个存储设备的相关信息,如state. ums_path, mount_point这些来自于/etc/vold.conf
- while(v)
- {
- if(v->ums_path)
- if(enable){
- ...
- volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable_false);//程序执行到这句话 挂掉了
- ...
- }
- }
- }
首先我们看看,程序是如何读取/etc/vold.conf配置信息并存储的
主要是这么几个函数volmgr_readconfig, volmgr_config_volume,等等,这里不详细讲,只是大概讲下,
首先 会建立一个大的链表,通过比配vold.conf中volume_XX来确定有多少成员 如volum_sdcard1 volum_sdcard2
表示有两个成员
然后,会在每个成员里在建一个链表,用于存储ums_path mount_point这些
最后再将这些东西重新在组织成个链表v。
=========================================
继续上面的代码说
跟踪volmgr_shutdown_volume()--->volmgr_stop_volume()--->_cb_volstopped_for_ums_enable()
view plaincopy to clipboardprint?
- staitc void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
- {
- char* devdir_path
- ...
- devdir_path = blkdev_get_devpath(v->dev->disk);//挂了
- ...
- }
继续跟下去到blkdev.c
view plaincopy to clipboardprint?
- char *blkdev_get_devpath(blkdev_t *blk)
- {
- ....
- char *dp = malloc(256);
- sprintf(dp, "%s/vold/%d:%d", blk->major, blk->minor);//挂掉
- }
- 经过调试 发现blk是空指针。
- 在看调用它的函数 _cb_volstopped_for_ums_enable
- 发现v->dev->disk空的指针
char *blkdev_get_devpath(blkdev_t *blk) { .... char *dp = malloc(256); sprintf(dp, "%s/vold/%d:%d", blk->major, blk->minor);//挂掉 } 经过调试 发现blk是空指针。 在看调用它的函数 _cb_volstopped_for_ums_enable 发现v->dev->disk空的指针
继续返回跟踪 ,发现v->dev->disk始终是空
怪不得,马的又没作异常处理,草了
在其他地方 发现v->dev和v->dev->disk是相同类型,有的地方用v->dev->major,有的地方用v->dev->disk->major
看了 下没见到v->dev->disk的初始化,悲剧阿。。。
于是尝试将v->dev作形参传入,而不是v->dev->disk
发现okay.汗查看/dev/vold/下面的设备号 和v->dev->major:v->dev->minor打出来的一致。
当机器启动时,插入usb client cable.主机端可以自动挂载mass-storage。
Oh, yeah success!
==========================================
不知道为什么v->dev->disk是空的。正在继续看。。估计这几天应该能搞清除。
写的不好,也不怎么详细,只是自己一点调试的看法和经验希望能帮助自己和大家,谢谢
Android usb client mass-storage挂载单个存储设备研究
上次,初步接触Android usb client 的挂载问题,所以写了第一篇研究的感受,许多地方还有点问题,有些现象还不够清晰,所以想继续写下去。
首先,通过上次的验证,看到的现象,是可以挂载了,但是每一次只能挂载一个存储设备。
其次,我上次所说的关于驱动的设备节点,也有问题,其实本身就有一个tegra-udc.c创建的/sys/devices/platform/tegra-udc.0/lun0这么个节点在,所以不需要在创建什么mass-storage节点。
===========================================================================
(一)首先,我们看看第一个问题,怎么每次只挂了一个设备在上面呢?
1.VOLD首先会去读取vold.conf的文件中配置内容(尾插入的链表方式)存储存储设备的必要信息(上次讲过,就是EMMC的partition 4)
接着,会去通过uevent读取动态插入的存储设备信息,以头插入链表的方式,并将第一个链表和这次的链表结合起来(auto_add_volume函 数),使得动态存储设备在链表头几个,而静态存储设备在链表后面。这种顺序,在我的环境中是正确的,因为我的虽有的动态存储设备是挂载在 partition 4上面的。用过手机或者有一点挂载卸载经验的人都知道,如果想要顺利卸载partition 4 需要卸载它的里面目录,否则partition 4一直显示busy而不能卸载。为什么要卸载了存储设备再挂呢,这是因为防止两边同时操作。
这里要说明下,就是如果想要动态存储设备通过ums_path路径的检测,需要在auto_add_volume函数里的ums_path下,明确指定目录,使得动态存储设备,能够被正确挂载。
2.当在client卸载成功时,就需要挂载到host端了,我一路跟踪,发现这段vold的volmgr.c代码起了作用
view plaincopy to clipboardprint?
- static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
- {
- int rc;
- char *devdir_path;
- #if DEBUG_VOLMGR
- LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point);
- #endif
- //check dev->disk whether is NULL
- if (v->dev->disk)
- devdir_path = blkdev_get_devpath(v->dev->disk);
- else
- devdir_path = blkdev_get_devpath(v->dev);
- if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) {//重点
- free(devdir_path);
- LOGE("Error enabling ums (%d)", rc);
- return;
- }
- free(devdir_path);
- volume_setstate(v, volstate_ums);
- pthread_mutex_unlock(mutex);
- }
最主要的是ums_enable(),是这句话让设备挂载到主机端,继续跟下去
在同目录下的ums.c代码中
view plaincopy to clipboardprint?
- int ums_enable(char *dev_fspath, char *lun_syspath)
- {
- LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath);
- int fd;
- char filename[255];
- sprintf(filename, "/sys/%s/file", lun_syspath);
- if ((fd = open(filename, O_WRONLY)) < 0) {
- LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
- return -errno;
- }
- if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) {
- LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
- close(fd);
- return -errno;
- }
- close(fd);
- return 0;
- }
open 一个"/sys/devices/platform/tegra-udc.0/lun0/file"文件
然后向file文件 写入一个 "/dev/block/vold/major:minor"的path路径,而这个路径对应的是各个存储设备
我尝试了写入不同的路径到file里(通过adb 中写入 如:echo "/dev/block/vold/179:0">/sys/devices/platform/tegra-udc.0/lun0/file)。
每写一个路径,就会挂载一个相应的存储设备到host端,但是当你挂载下个设备时候必须echo "">file中,先从host端unmount前一个设备才能挂载当前设备。
继续跟踪代码到kernenl,发现/driver/usb/gadget/f_mass_storage.c作了某些动作,挂载了设备
view plaincopy to clipboardprint?
- static ssize_t store_file(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = dev_get_drvdata(dev);
- int rc = 0;
- DBG(fsg, "store_file: \"%s\"\n", buf);
- #if 0
- /* disabled because we need to allow closing the backing file if the media was removed */
- if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) {
- LDBG(curlun, "eject attempt prevented\n");
- return -EBUSY; /* "Door is locked" */
- }
- #endif
- /* Remove a trailing newline */
- if (count > 0 && buf[count-1] == '\n')
- ((char *) buf)[count-1] = 0;
- /* Eject current medium */
- down_write(&fsg->filesem);
- if (backing_file_is_open(curlun)) {
- close_backing_file(fsg, curlun);
- curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
- }
- /* Load new medium */
- if (count > 0 && buf[0]) {
- rc = open_backing_file(fsg, curlun, buf);
- if (rc == 0)
- curlun->unit_attention_data =
- SS_NOT_READY_TO_READY_TRANSITION;
- }
- up_write(&fsg->filesem);
- return (rc < 0 ? rc : count);
- }
知道sysfs的原理的人 都知道static DEVICE_ATTR(file, 0444, show_file, store_file);是负责读(cat/read)或直写(echo/write)设备属性文件的宏,具体实现就是上面这句(写)。
看来看去 主要就是open_backing_file()函数的作用。还没怎么研究透,只知道肯定是这句起了作用,它打开了vold目录下的设备节点,并且初始化 了一些东西。看到的现象就是,host端只识别单设备不同分区的挂载,但是不识别多存储设备的同时挂载。
我不知道,到底是什么问题,让所有设备只挂载到一个设备节点上去了(冲突),而不是以往的/dev/sdb sdc sde等。
希望能够学习解决
======================================================================
(二)第二个问题 其实也没什么,只是 先从UDC(usb device controller)的驱动走了下,当时没注意。
不知道Android手机是不是驱动有这种只挂载单一存储设备的限制,至少目前看是这样的。想想也正常,手机一般只有一个SD插槽,自己本身内存有限,挂一个SD的存储设备无可厚非。。。
希望是自己代码的问题,还在解决中,希望有研究的达人,一起研究分享下这方面的经验。谢谢,估计很快会写(三),还在尝试,希望写下一篇的时候,能解决这个问题。
Android usb client mass-storage 多存储设备挂载问题解决
这是写的第三篇文章,废话不多说,接着上次的问题--不能同一时间挂载多个存储设备,继续探讨。
昨天,和一位linux 达人,在交流,无意中提到了这个问题,然后让他看了看现象,他说我的/devices/platform/tegra-udc.0/gadget这个目录,可能有问题。
于是查了下LUN是什么(Logical units)逻辑单元,跟SCSI有关,仔细查了下f_mass_storage.c的代码,里面的代码是fsg什么的,继续查了下
========================================================================
The File-backed Storage Gadget (FSG) provides support for the USB Mass Storage class. It can appear to a host as a set of up to 8 SCSI disk drives (called Logical UNits orLUNs), although most of the time a single LUN is all you will need. The information stored for each LUN must be maintained by the gadget somewhere, either in a normal file or in a block device such as a disk partition or even a ramdisk. This file or block device is called the backing storage for the gadget, and you tell FSG where the backing storage is when you load the gadget driver:
来自:http://www.linux-usb.org/gadget/file_storage.html
翻译一下:文件备份存储装置提供usb mass storage类的支持。它在主机端显示一连串最多高达8个的SCSI驱动盘符(被称作逻辑单元或者LUN),不过大多数时间里你只需要用到一个LUN。 每个LUN的信息必须被保存在某个gadget中,信息不是存在一个普通文件,就是存在一个块设备中,例如一个盘分区,甚至是一个ramdisk。这个文 件或者块设备对于gadget被称作“backing storage”, 而当你试图加载gadget驱动的时候,你可以告诉FSG"backing storage"在哪里。(英文不行,翻译可能有问题,达人请指正)。
==========================================================================
okay 大概知道,怎么回事了,应该是存储设备对应lun的多少的问题。按照linux的那套,其实一个client口 对应一个lun是正确的,但是现在情况是需要让client去把自己作为host,处理挂载自己身上的N个存储设备,所以按照道理来说,一个设备应该有一 个lun,多个设备就多个lun
进行验证,尝试去利用现在已经有的唯一的一个lun/file,向里面写挂载存储设备的路径,并且追加,证明确实只支持一个设备的写入,不支持什么追加。
现在有两种解决方式:
1.在主机端,模拟个文件 系统,将client的所有设备都挂载到这个文件系统上(那位达人给我的建议),客观的说,这应该是最科学的,在不该原有设计思路的最好的方式。不过实在是能力有限,无从下手。。于是想了下面一个方法。
2.就是假设,模拟多个lun节点,根据代码/drivers/usb/gadget/f_mass_storage.c中的MAX_LUNS 8的定义,先模拟8个lun设备看看情况
看代码:
view plaincopy to clipboardprint?
- tatic int __init
- fsg_function_bind(struct usb_configuration *c, struct usb_function *f)
- {
- ...
- /* Find out how many LUNs there should be */
- i = MAX_LUNS;//fsg->nluns; hook modify the i = MAX_LUNS.
- if (i == 0)
- i = 1;
- if (i > MAX_LUNS) {
- ERROR(fsg, "invalid number of LUNs: %d\n", i);
- rc = -EINVAL;
- goto out;
- }
- /* Create the LUNs, open their backing files, and register the
- * LUN devices in sysfs. */
- fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL);
- if (!fsg->luns) {
- rc = -ENOMEM;
- goto out;
- }
- fsg->nluns = i;
- ...
- }
原来的i = fsg->nluns给我改成了MAX_LUNS,这只是临时改的,应该按照道理来说 需要判断有多少个存储设备被挂载,然后给i的值赋多少。不过后来想想这样该貌似有问题,于是继续跟踪,发现在/arch/arm/mach-xxx /xx-board.c文件中有
view plaincopy to clipboardprint?
- static struct android_usb_platform_data android_usb_plat =
- {
- .vendor_id = 0x0955,
- .product_id = 0x7000,
- .adb_product_id = 0x7100,
- .product_name = "ADB Composite Device",
- .manufacturer_name = "NVIDIA Corporation",
- .serial_number = "0000000000000000",
- .nluns = 1,
- .bulk_size = 16384,
- };
nluns = 1;看来是这个问题阿。所以改动成3,
然后编译驱动,加载之,发现/sys/devices/platform/tegra-udc.0/gadget下会出现lun0-lun7的3个节点,
尝试把client端的每个存储设备的挂载信息写到对应的lun中的file下,如echo "/dev/block/vold/179:0">file
结果证明全能挂载了okay.
如果需要自动挂载这些存储设备,而不是手动,需要改vold代码中的volmgr.c在auto_add_volume()函数里,当每往链表中添加设备信息时候,给ums_path的值添上相应的lun路径 就行了。
==========================================================================
不过这种方法虽然可以作出来,但是并不是好的方法,只是投机取巧,会被人鄙视的,在这因为研究学习用,所以写了出来。现在还在继续研究模拟文件系统如何挂载的方式,希望能有所突破