linux平台用VFS驱动实现目录重定向(文件驱动实现目录重定向 四)

                                                                    By Fanxiushu 2016-09-27 转载或引用请注明原始作者


当你工作中或者生活中都面临着多台电脑,而且要经常访问每台电脑上的文件时候,

你会很需要一种机制,或者工具,能非常方便的访问每台电脑的文件。
各种操作系统平台都通用的工具,比如FTP工具,HTTP工具,
windows平台最常用就是网上邻居(SMB/CIFS协议),UNIX等平台的网络文件系统(SMB,NFS等)
当然NFS是跨平台的,windows也能使用,而且NFS是网络文件系统,可以跟本地操作系统无缝合并在一起。
然而windows平台大家用得最多的估计就是网上邻居(SMB)了,通过UNC方式( ”\\Server\DIR“ )的方式互相访问各台机器上的文件。

windows文件系统的机制跟 UNIX不一样的,windows是以不同的盘符区分不同的文件系统,比如D和E盘可以安装不同的文件系统,
但是同一个盘符下则不像UNIX,是不能挂载不同文件系统的。所以在windows平台下的不同文件系统,
类似NFS,SMB(网上邻居),某些开源dokan工程等等,都只能创建一个新盘符来挂载他们自己的文件系统。
如果在安装的NTFS本地文件系统的D盘符下,要把 某个目录,比如 D:\MonDir 转换到网络服务端,
如果不跟微软商量,叫他们帮忙解决,自己开发新文件系统是不可能办得到的。
自己解决这个问题的唯一途径就只有文件过滤驱动来拦截和转向 d:\MonDir 请求了。

这也就是前几篇博客中详细介绍的“文件过滤驱动实现目录重定向” 。
虽然要达到跟本地NTFS完美结合是艰难的
(而且要兼容各种文件操作情况和修补各种BUG与开发其他windows类型驱动做比较,
是复杂和难搞定的驱动之一,也是一不留神就会蓝屏的驱动之一)
但是大部分情况下,都能欺骗大部分软件。
对应的博客地址:
http://blog.csdn.net/fanxiushu/article/details/43636575     (文件过滤驱动实现目录重定向一)
http://blog.csdn.net/fanxiushu/article/details/43845699      ( 文件过滤驱动实现目录重定向二)
http://blog.csdn.net/fanxiushu/article/details/44737171     (文件过滤驱动实现目录重定向三)
windows对应的驱动代码下载地址:
http://download.csdn.net/detail/fanxiushu/8545567   
(源代码,BUG不少,仅供参考;鉴于当初刚写这份代码时没现在对文件系统理解的透入)
http://download.csdn.net/detail/fanxiushu/8448785   
(例子程序)

下图是在本人电脑上,利用博文中介绍的过滤驱动实现的目录重定向功能,有个FXS-VDISK的K盘符,
在此盘符下,挂载出多个目录,分别重定向到不同机器上,比如 iPhone6定向到手机的照片和视频共享目录,
linux7.2定向到linux系统, MacOS目录定向到MAC OSX 系统,反正是在本人电脑上汇集了多台电脑各种不同系统的文件,

因此这对我的工作和生活带来了许多方便。




在windows平台下,文件系统是以磁盘卷为根的方式组织的,共有26个看得见的磁盘卷,从 'A' 到 'Z' ,
每个磁盘卷使用树的方式来组织文件,比如磁盘卷D,“D:\”是跟目录,而“D:\Path” 是 ”D:\“ 根目录的子目录。
windows平台利用文件过滤驱动,挂载到每个磁盘卷,过滤特定的目录,
比如“D:\Path"目录,让所有发送到 D:\Path目录的请求全部转发,从而达到目录重定向的目的。
而linux要达到目录重定向的功能不同于windows的做法。
linux只有一个根目录 "/", 所有的子目录只有一个祖宗根 ”/",  不同的文件系统挂载到祖宗根的某个子目录下,
因此,linux下实现目录重定向,不会像windows那么麻烦需要开发过滤驱动来拦截分析被重定向的目录,
只需要开发一个新的文件系统,挂载到某个目录下,就可以实现这个目录的重定向功能。

所以linux平台实现目录重定向,则集中在开发一个新的文件系统上, linux平台实现新文件系统的办法也是挺多的,
比如要非常快速的一两天就实现一个文件系统,
则可以使用fuse,这个是用户空间文件系统,linux自带的一个使用起来挺方便的开源库。
还有些不用做开发,可以直接使用的文件系统,比如smb,nfs等,直接mount本地的一个目录到网络服务器端,
操作这个目录就等于在操作服务端的目录。
因此linux平台要实现目录重定向功能,不论是选择现成工具,或者是自己开发,都要比windows简单得很多。

本文不打算介绍fuse的开发和smb,nfs等的使用,
而是从linux驱动层出发,开发一个自己的文件系统来达到目的。

linux内核开发新文件系统,必须要掌握的就是 VFS,
大部分人都把VFS简单翻译为虚拟文件系统,其实不准确,应该是 Virtual FileSystem Switch, 虚拟文件系统交换层,
也就是说VFS是linux平台文件系统的中间层,它衔接上层通用的文件访问方式和底层文件的组织和存储。
他是承上启下的中间件,是linux内核的核心组件之一。

VFS必须掌握的四大结构体,以及每个结构体对应的回调函数集合结构体:
一, struct super_block 俗称超级块,每个文件系统对应一个超级块,
        超级块就是描述这个文件系统的一些总体信息,文件系统加载的时候被创建。
        struct super_operations是超级块对应的操作函数集合,
        super_block里边的s_op指针指向的就是super_operations。比如要创建自己扩展结构inode,可以
        实现 alloc_inode和destroy_inode函数,以及超级块卸载时候的put_super函数等,
        struct super_operations 操作函数集合比较多,这里不全部解释,具体使用可以查看linux内核源代码。

二,struct inode 俗称 i-节点, 说明白点,其实就是文件控制块FCB,每个文件(包括目录)都应该有个文件头信息,
       用于描述这个文件属性,时间,大小等信息,各种不同的文件系统在磁盘上都有各自的FCB结构,当把这些FCB加载到linux内核,
       则使用 struct inode结构来描述FCB信息。windows平台下的文件对象FILE_OBJECT,此结构体有个FsContext指针,当FILE_OBJECT
       代表文件对象的时候,FsContext指向的其实就是FCB文件控制块结构,
      只是它不像linux描述inode那样描述得那么清晰,而且像NTFS对应的FCB结构体细节一直处于未公开状态。
       struct inode_operations是i-节点对应的操作函数集合,由inode的i_op指针指向。
       比如在某个目录内创建某个空白文件,创建空子目录,删除目录,删除文件,重命名文件等等对某个目录的操作,
       就是调用 这个目录的inode的i_op指向的inode_operations结构体的回调函数。

struct file 代表的是文件对象,这个跟进程相关的,就是某个文件可以被打开多次,每打开一次,
       就使用struct file结构体来描述这个文件被打开的情况,针对同一个文件的不同的打开的file结构,
       可以互相不干扰的读写的不同位置的数据,可以独立浏览子目录等
       llinux的file跟windows的FILE_OBJECT几乎是一个意思,但是却有不同,
       windows一切的文件操作都是从打开这个文件开始,所有操作都是以FILE_OBJECT为基础;
       而linux对文件的某些操作可不必打开,比如rename,unlink,mkdir,等等操作。
       对于熟悉了windows行为模式的人,初次接触linux的这种看似零散的做法,可能有些不大习惯,但是各有各的好处。
       struct file_operations是对应的file操作函数集合,比如打开释放回调函数open和release,
       读写回调函数read,aio_read,write,aio_write等函数。

struct dentry 用来描述文件名字信息。linux平台下,文件名和 i-节点 是分开管理存储的,
       并不是把某个文件的完整路径名附加到inode结构体里边,而是单独采用struct dentry结构体来按照树状方式管理。
       比如路径 /dir1/dir2/name.mp4, 在linux 内核中拆分成 dir1, dir2, name.mp4, 三个文件名用三个dentry来描述,
       并且同时管理着dir1,dir2,name.mp4的树状的层次关系以及和i-节点的关联。

struct address_space_operations 这个结构体有关文件的系统缓存操作函数,文件数据的读写操作,经过linux的缓存机制,
       最终都进入到address_space_operations,调用相应的回调函数,实现文件数据的真正读写。
       自己实现文件系统的话,为了简单,其实都可以把file_operations 结构体的操作函数设置成VFS框架的默认的函数,
       而在 address_space_operations 结构体里设置 readpage,writepage等函数实现真正的数据读写。

以上就是对 VFS的核心结构体的简单描述,对初学者来说上边的描述只会感觉迷茫,因此初学者可以多查阅相关资料,
从阅读linux内核源代码慢慢理解掌握,比如从简单的ramfs源代码,到 smbfs, 再到fuse等。
正是因为linux的开放源代码,因此linux的驱动比windows简洁和好理解得多,因为一旦感觉那里不对劲,
可从阅读内核源代码中排除自己的疑惑,windows的驱动就比较糟糕,
出了问题,或者本身就比较疑惑的地方,找不到源代码来排除疑惑,只能依靠多调试测试来帮忙解决问题。
当然有时可以借助WRK研究性质的源代码和泄漏的年代久远的win2000部分源代码窥伺windows内核的大致运行机制。

再简单看看linux的系统调用过程:
比如 read系统调用,应用层调用read读取数据,进入到内核,调用sys_read,sys_read调用 vfs_read,
vfs_read调用file_operations结构的read回调函数进行数据读取,
实际上,linux拥有缓存读写机制,file_operations的read回调函数可以直接调用do_sync_read等VFS框架提供的函数,
do_sync_read调用file_operations结构的aio_read进行异步读取,我们接着把aio_read交给VFS框架提供的generic_file_aio_read函数,
generic_file_aio_read函数首先从linux的内核缓存页查找需要读取的数据,若找到,直接从缓存读取,
如没找到,则发生”缺页中断“,从而进一步调用 address_space_operations 结构的readpage回调函数进行真正的数据读取,
因此,我们可以简单的只在address_space_operations 结构中,实现readpage回调函数,就能完成复杂的 read读取操作过程。
readpage是按照页方式读取,也就是每次都是 按照4K大小读取,对于本地磁盘块设备来说问题不大,
但是对于网络文件系统,如果每读4K就发生一次网络数据交换,对于非常大的文件,比如几百M或者几G的文件来说,速度将会很慢。
因此常常需要在readpage等回调函数中,对网络数据再做些缓存机制,
或者在file_operations的read和aio_read回调函数中直接从网络读取数据。
write系统调用跟read也基本是一样的过程,只是generic_file_aio_write不是立即调用address_space_options的writepage写数据,
它只是修改内存页之后,把该内存页标记为脏页,当系统内存达到一定限制或脏页达到一定阎值时候,
系统调用pdflush进程,把脏页刷新到磁盘,这个时候address_space_options的writepage被调用,数据真正被写出。

再来简单对比下windows的ReadFile过程,ReadFie进入内核调用NtReadFile,
NtReadFile会首先调用 FastIoRead快速读取回调函数,检测内核缓存是否有数据,有则直接读取,
如果文件驱动不支持FastIoRead,或者FastIoRead返回FALSE,
NtReadFile则会创建IRP_MJ_READ的IRP请求来调用文件系统驱动的派遣函数,
然后文件系统驱动的IRP_MJ_READ派遣函数分两种情况处理此IRP, 
如果是IRP_NOCACHE类的Irp请求则发生真正数据读取操作;
如果不是,则调用CcCopyRead从缓存读取,如果数据不在缓存中,会发生缺页中断调用 MmAccessFault,从而进入到IoPageRead函数,
IoPageRead构造 IRP_PAGING_IO|IRP_NOCACHE 类型的IRP请求再次调用文件驱动的IRP_MJ_READ派遣函数,实现数据的真正读取。
数据读完之后,数据被缓存到系统缓存中,然后CcCopyRead从缓存copy数据,并且返回。
是不是跟linux的读取过程有异曲同工之妙!只是windows两次都进入IRP_MJ_READ派遣函数,
一不留神的话容易造成无限嵌套调用或者错误调用,事实上,读写派遣函数往往是多次被重入调用的。
这也是为什么windows派遣函数必须是可以重入的。

至于系统的其他调用, 比如mkdir,rmdir,unlink,mknod,rename,stat等,
这些系统调用进入内核都会调用相应的 sys_* 函数,最后进入到VFS框架,调用inode_operations结构体对应的回调函数,
如mkdir最终会进入到 inode_operations的mkdir回调函数。
像readdir系统调用,最后会进入到file_operations的readdir回调,只要我们在readdir回调函数中填写对应的查询结果,
则应用层软件就能浏览对应的目录内容了。
应用层对应的文件相关的系统调用,VFS框架都有对应的回调函数实现真正的文件操作。
对比windows的文件系统调用,VFS框架提供的函数库没有windows那种多如牛毛的文件操作的WIN32 API,
应用层开发使用的文件操作函数API,也没有windows的丰富,
但是VFS提供多种文件系统格式的支持,目前的linux起码能支持十几种文件系统,
这几乎是VFS框架的通用性带来的功劳。
而且在VFS框架下开发自己的新文件系统,也方便和简洁得多,windows平台要开发一个新的文件系统,就没这么容易了。

再来梳理一下,linux平台如何开发一个自己的文件系统的简单流程:

首先定义 file_system_type 结构体,大致如下:
static struct file_system_type xfs_fs_type = {
    ////
    .owner = THIS_MODULE,
    .name = “xfs_redir”,
    .mount = fs_get_sb,
    .kill_sb = fs_kill_sb,
    .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
};
在驱动初始化函数里调用 register_filesystem(&xfs_fs_type); 注册文件系统。
在驱动卸载函数注销unregister_filesystem(&xfs_fs_type);

当在应用层调用 mount 函数挂载文件系统时候, 进入到内核执行sys_mount,sys_mount调用do_mount,
do_mount执行一些列初始化操作, 然后调用我们在 file_system_type提供的mount回调函数 fs_get_sb,
在fs_get_sb回调函数,我们可以调用 mount_nodev等VFS框架提供的函数,mount_nodev帮我们创建super_block超级块,
mount_nodev同时提供一个fill_super函数作为参数,用于我们进一步填充super_block结构体。
大致如下:
int fs_get_sb(...)
{
     return mount_nodev(fs_type, flags, raw_data, fs_fill_super);
}
int fs_fill_super(struct super_block *sb, void *data, int silent)
{
       /////进一步填充 super_block 结构体,完成我们自定义文件系统的功能。
       初始化 root_fattr 属性值。root_fattr 是根 "/" 的属性值
       root_inode = xfs_iget(sb, &root_fattr); // xfs_iget是我们自己定义的,通过inode的相关属性,创建inode对象。
       sb->s_root= d_make_root(root_inode);//创建根 dentry
       sb->s_op = &xfs_sb_ops;// 初始化  super_operations
       ......
}

在 fs_fill_super 回调函数做一些我们自己的初始化工作,同时必须填写 sb->s_op 指向 super_operations 结构体,
创建根inode,创建根dentry,把根inode关联到根dentry, 再把根dentry赋值给 sb->s_root。
以及填写super_block其他一些必须的属性值。

这样文件系统就被加载了,当卸载的时候,实现 file_system_type结构体的kill_sb或者super_operations结构体的put_super
等回调函数,在这些卸载函数中删除在fs_fill_super加载函数中添加的一些自己的数据。

在我们的驱动中,加载文件系统采用的办法,
首先在应用层通过IOCTL,发送监控目录路径等参数到驱动,让驱动首先创建一个mon_dir_t数据结构,
并且挂载到全局队列。
结构描述大致如下:
struct mon_dir_t
{
    struct list_head      list;      /// --> xfs_t.mon_dirs;挂载到全局队列中
   
    unsigned char         is_valid;  //  是否有效,
    unsigned char         is_poll_nowait; ///
    long                  ref_count; //  引用计数

    struct string_t       path;      /// 监控的完整路径,格式 /home/mon_dir , /root/path2
    ///
    struct list_head      wait_head; /// 等待队列,指向 op_queue_t

    struct list_head      busy_head; /// 忙碌队列,指向 op_queue_t

    ////
    wait_queue_head_t     wait_q;//等待队列,用于通知应用层有新的请求

    struct super_block*   sb;
    /////////
    ........................
};

接着在应用层调用mount函数实现文件系统的挂载,这个时候上边的fs_fill_super将被调用,
在fs_fill_super函数中从保存mon_dir_t的全局队列中,用监控目录路径作为匹配项,查找到刚才创建的mon_dir_t,
并且设置到 super_block的s_fs_info指针中,这样方便以后操作。
在卸载的时候,首先移除mon_dir_t,然后在应用层调用umount函数,卸载文件系统驱动,这个时候,put_super被调用,
在put_super函数中清除数据。

具体代码,可查看稍后提供在CSDN上的代码。

虽然加载和卸载已经实现了,但是还没实现实际的功能,接下来需要处理的就是inode的创建和销毁问题,
在我们的VFS驱动中,往往需要扩展标准的inode结构体,用来添加一些跟inode相关的自己的数据,
因此往往需要自己实现 super_operations的的alloc_inode和destroy_inode函数,
较为通用的做法如下,定义一个包含inode的结构体,
struct xfs_inode_t
{
    ////
    struct inode         vfs_inode;  ///包含标准的inode
    u64                  i_no;       /// inode number

    u64                  i_attr_time; //// 属性的有效时间, jiffies方式表达

    atomic_t             open_count;  //作为文件打开的个数
    /////
    .................
};
然后再 super_operations的alloc_inode回调函数中,做类似如下处理
static struct inode* xfs_alloc_inode(struct super_block *sb)
{
    struct xfs_inode_t *xi;
    xi = (struct xfs_inode_t *)kmem_cache_alloc(xfs->inode_pool, GFP_KERNEL); //有点类似windows的LookasideList 函数库,
    if (!xi)
        return NULL;

    ///
    memset(xi, 0, sizeof(struct xfs_inode_t)); /// init xfs_inode
   ............................
    /// init inode
    inode_init_once(&xi->vfs_inode);
    ////
   ..........................
    return &xi->vfs_inode;
}

在使用inode时候,从标准的 inode 指针通过 container_of(inode, struct xfs_inode_t, vfs_inode);
就可以获得我们自己的 xfs_inode_t结构体指针。

准备好属于自己结构体的inode的分配和销毁之后,
我们就可以使用 new_inode或者iget_locked等VFS框架函数获取到一个未使用的inode,在VFS的其他回调函数中使用。
第一个要使用新inode的地方就是 inode_operations的lookup回调函数,这个是调用频率最多的函数之一,
VFS内部用来建立 dentry树状链和inode的关系链,比如我们要打开一个 文件路径是 /home/path1/song.mp3 的文件,
VFS首先拆成 home,path1,song.mp3三个部分,然后从它的dentry缓存中查找,如果都存在,则认为没问题。
如果某个不存在,比如 path1不在他的缓存中,VFS会调用我们驱动中的 inode_operations的lookup,
并且操作目录的inode是 home目录指向的inode, 我们的lookup中通过最终处理,认为path1在home目录中,
于是通过new_inode创建一个新inode,然后通过 d_add 附加到lookup提供的dentry结构中,
然后VFS接着调用我们的inode_operations的lookup,这次的操作目录inode是path1指向的inode,然后我们接着在lookup做重复的工作。
当这一切都顺利后,我们就能打开/home/path1/song.mp3路径的文件了。

接下来就是集中实现 inode_operations ,file_operations,address_space_operations 等接口函数,
具体实现细节,以及实现了哪些接口,可查看我稍后发布在CSDN上的源代码,
当然不比fuse。我是偷工减料,能少实现的接口,就尽量少实现。反正只要最终完成目标即可。

这里说说如何把这些接口请求转发到应用层。
跟fuse一样,我们的驱动依然是把对目录的各种请求,发送到应用层程序,在应用层程序再转发到网络上去的,
这也是我对这类驱动的一贯做法,比如上章介绍的虚拟USB设备是这么做的,windows平台下的目录重定向也是这么做的,
因为这么做,大部分复杂的业务数据请求交给应用层来完成,
除非是那种非常需要性能的功能,比如在驱动层实现一个大IO量的服务器需求等。

没研究过fuse如何把请求转发到应用层的,也懒得去研究,只是按照自己的习惯,借鉴自己在windows平台的一贯做法,
先来个BEGIN IOCTL ,从驱动获取请求,处理完成后,调用 END IOCTL完成这个请求。
当然linux稍微有些变化。
刚开始还想着创建一个Semaphore信号量等待读请求呢,可是linux内核没有信号量概念,
不过倒是有等待队列的概念来完成类似功能。
同时把 BEGIN IOCTL和END IOCTL命令分别交给 read和write去完成,linux的ioctl没windows那个强大。
首先当然是创建一个字符设备用于应用层程序通讯。
字符设备的read和write函数完成 BEGIN IOCTL和ENDIOCTL功能。
应用层打开这个字符设备,调用read之前,首先调用select等待是否有新的请求到来,如果select成功,调用read读取新的请求。
处理完这个新请求,调用write完成这个请求。

驱动层实现这个字符设备的 read,write,等回调函数,同时实现poll回调,用于检测是否有新请求在队列中等待应用层读取。
mon_dir_t监控目录结构体中,定义了一个wait_q变量,就是用来当这个监控目录中有新的请求时候的通知事件。

在处理每个请求时候,创建一个op_queue_t结构体来进行处理,大致描述如下:
struct op_queue_t
{
    struct list_head    list; //// -> mon_dir_t.wait_head or mon_dir_t.busy_head
   
    /////
    wait_queue_head_t   wq; //等待这个请求被完成或者超时

    int                 op_type; ///操作类型

    unsigned char       is_complete; //  是否完成
    unsigned char       is_noreply;  //  不需要从用户层返回数据
    unsigned char       is_nobuffer; //  IOCTL处理时候,用户层提供的空间不够
    unsigned char       is_clear_inode_usrctx; ///是否发送清除usrctx的命令
    int                 ret;         /// 返回码

    /////
    u64                 inter_seqno; ///

    struct inode*       inode;    /// 操作的inode
    struct dentry*      dentry;   /// 操作的dentry
    struct file_stat_t* stat;     /// 操作的属性

     。。。。。。。。。。
};

当我们在VFS的某个回调函数中,以lookup为例。
在 lookup回调函数中,首先创建 op_queue_t结构体,
op  = xfs_alloc_op_queue(OP_LOOKUP,  inode);//OP_LOOKUP表示只lookup操作,inode是当前执行这个操作的inode。
///然后填充一一些必要的数据信息。
op->dentry=....
//最后等待这个op完成
ret = xfs_wait_op_queue_complete(op); ////

xfs_wait_op_queue_complete函数则实现把op_queue_t加入到监控目录mon_dir_t的队列中,
并且等待应用层取走这个请求和完成这个请求,
应用层处理完这个请求之后,xfs_wait_op_queue_complete 函数返回,当然也包括超时返回。

代码大致如下:
int xfs_wait_op_queue_complete(struct mon_dir_t* md,
    struct op_queue_t* op , long tmo_jiffies ) // timeout is jiffies
{
    int ret; ////

    op->is_complete = 0;

    lock();
    ///
    if (!md->is_valid) { //监控目录已经无效了
        printk("monitor director not valid. \n");
        xfs_free_op_queue(op);
        ///
        unlock(); ///

        return -ENOENT;
    }
    ///
    list_add_tail(&op->list, &md->wait_head); /// 加入到等待处理队列

    wake_up_interruptible(&md->wait_q); //通知用户进程,有新的请求

    unlock();

    /////
    ret = wait_event_interruptible_timeout( op->wq, op->is_complete!=0 , tmo_jiffies); //等待用户层完成或者超时

    lock();
    list_del(&op->list); ////可能已经在 __op_queue_complete 中被移除出队列,但是已经INIT_LIST_HEAD,所以这里再次list_del不会出问题
    INIT_LIST_HEAD(&op->list);
    unlock();
    。。。。。。。。

要了解详细细节,请查看CSDN上发布的源代码工程。

总之linux平台下的这种处理方式比windows简洁和看着舒服,光是代码就已经简洁得多,还挺好理解。

另外值得一提的是,在刚开始做windows目录重定向驱动的代码时候,比较崇尚把内核内存直接映射到用户空间,
认为这样能减少一次数据copy,提高性能。因此CSDN上的那个代码无处不在的
MmMapLockedPagesSpecifyCache(mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); //UserMode模式的函数调用。
但实际上,在后来的开发中,发现问题非常多,而且还是极其难查找的BUG,动不动就蓝屏死机。
后来完全取消了 内存直接映射,采用老老实实的 RtlCopyMemory,多一次内存copy,至少稳定了许多。
以至后来的提供到CSDN的USB驱动代码,以及这个VFS代码,都是采用普通的 RtlCopyMemory或者copy_to_user/copy_from_user
来传递内核和用户空间数据。
内存映射不是随便什么地方都好用,限制挺多,得看具体场所。

CSDN上源代码下载地址:

http://download.csdn.net/detail/fanxiushu/9642449



最后来张linux下,目录被重重定向到windows平台的目录的图片,聊以自夸。
linux平台是 rhel 7.2 ,内核版本3.10

图片


2017-01更新,完整版本程序下载地址:

CSDN的下载地址:

http://download.csdn.net/detail/fanxiushu/9719017

GITHUB上的下载地址:

https://github.com/fanxiushu/xFsRedir/raw/master/xFsRedir-1.0.0.1.zip

这个是1.0.0.1版本,如果寻找新版本,请关注:

https://github.com/fanxiushu/xFsRedir

或发送邮件 [email protected] 如发现BUG,可在CSDN或者GitHUB上提出来,或者发送邮件 [email protected]



猜你喜欢

转载自blog.csdn.net/fanxiushu/article/details/52681705