精华内容
下载资源
问答
  • linux内核mount源代码剖析

    千次阅读 2016-03-02 10:18:36
    mount命令是大家在平时使用linux的时候经常使用的一个命令,相信很多人都很熟悉这个命令,它的作用是把一个设备挂载到根文件系统的某一个目录上边去,但是有没有人对他的内部实现有过一些了解的呢,我今天就像从...
    mount命令是大家在平时使用linux的时候经常使用的一个命令,相信很多人都很熟悉这个命令,它的作用是把一个设备挂载到根文件系统的某一个目录上边去,但是有没有人对他的内部实现有过一些了解的呢,我今天就像从linux的源代码剖析,一层一层的剥开它实现的奥秘。
    
    首先mount是一个系统调用,在用户空间使用mount函数以后,会调用软中断,进入内核空间。然后根据传入的参数,调用对应的中端门,随后进入sys_mount函数,这个函数定义在fs/namespace.c里,定义如下
    /*传入的dev_name是mount的设备名,dir_name是挂载点,type是文件系统类型,flags是标记,data是私有数据*/
    asmlinkage long sys_mount(char __user * dev_name, char __user * dir_name,
    			  char __user * type, unsigned long flags,
    			  void __user * data)
    {
    	int retval;
    	unsigned long data_page;
    	unsigned long type_page;
    	unsigned long dev_page;
    	char *dir_page;
    	/*type是字符串的文件系统名字,比如"ext4"这样的字符串,这个函数就是把挂载的选项字符串复制到内核里的一块地方*/
    	retval = copy_mount_options(type, &type_page);
    	if (retval < 0)
    		return retval;
    	/*把用户空间传来的dir_name参数复制到内核的一块内存里边*/
    	dir_page = getname(dir_name);
    	retval = PTR_ERR(dir_page);
    	if (IS_ERR(dir_page))
    		goto out1;
    	/*把dev_name复制到内核的一个空闲页上*/
    	retval = copy_mount_options(dev_name, &dev_page);
    	if (retval < 0)
    		goto out2;
    	/*把data数据复制到内核的一个空闲页上*/
    	retval = copy_mount_options(data, &data_page);
    	if (retval < 0)
    		goto out3;
    	/*锁住内核,防止其他进程抢占*/
    	lock_kernel();
    	/*进入mount的主要工作*/
    	retval = do_mount((char *)dev_page, dir_page, (char *)type_page,
    			  flags, (void *)data_page);
    	/*释放锁,并释放内存*/
    	unlock_kernel();
    	free_page(data_page);
    
    
    out3:
    	free_page(dev_page);
    out2:
    	putname(dir_page);
    out1:
    	free_page(type_page);
    	return retval;
    }

    然后进入do_mount函数,这个函数也定义在fs/namespace.c里,定义如下
    long do_mount(char *dev_name, char *dir_name, char *type_page,
    		  unsigned long flags, void *data_page)
    {
    	struct nameidata nd;
    	int retval = 0;
    	int mnt_flags = 0;
    
    
    	/* 不用的magic就抛弃 */
    	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
    		flags &= ~MS_MGC_MSK;
    	/*参数检查*/
    	if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
    		return -EINVAL;
    	if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
    		return -EINVAL;
    	/* 把页的最后一个字节置为零 */
    	if (data_page)
    		((char *)data_page)[PAGE_SIZE - 1] = 0;
    
    
    	/* 把传入的flags分解到mnt_flags上 */
    	if (flags & MS_NOSUID)
    		mnt_flags |= MNT_NOSUID;
    	if (flags & MS_NODEV)
    		mnt_flags |= MNT_NODEV;
    	if (flags & MS_NOEXEC)
    		mnt_flags |= MNT_NOEXEC;
    	if (flags & MS_NOATIME)
    		mnt_flags |= MNT_NOATIME;
    	if (flags & MS_NODIRATIME)
    		mnt_flags |= MNT_NODIRATIME;
    	if (flags & MS_RELATIME)
    		mnt_flags |= MNT_RELATIME;
    
    
    	flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
    		   MS_NOATIME | MS_NODIRATIME | MS_RELATIME);
    
    
    	/* 从参数中寻找到挂载点,并且检查这个目录是否存在合法 */
    	retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
    	if (retval)
    		return retval;
    	/* 安全操作 */
    	retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page);
    	if (retval)
    		goto dput_out;
    	/* 如果不是第一次挂载的话,就执行do_remount */
    	if (flags & MS_REMOUNT)
    		retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
    				    data_page);
    	else if (flags & MS_BIND)/*  */
    		retval = do_loopback(&nd, dev_name, flags & MS_REC);
    	else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
    		retval = do_change_type(&nd, flags);
    	else if (flags & MS_MOVE)/* 移动目录 */
    		retval = do_move_mount(&nd, dev_name);
    	else/* 第一次挂载 */
    		retval = do_new_mount(&nd, type_page, flags, mnt_flags,
    				      dev_name, data_page);
    dput_out:
    	path_release(&nd);
    	return retval;
    }

    我们主要分析下第一次挂载的过程,其他的都类似,第一次挂载做了很多的事情,do_new_mount函数也定义在fs/namespace.c里,定义如下
    static int do_new_mount(struct nameidata *nd, char *type, int flags,
    			int mnt_flags, char *name, void *data)
    {
    	/* vfsmount结构体是挂载最重要的结构体 */
    	struct vfsmount *mnt;
    	/* 参数检查 */
    	if (!type || !memchr(type, 0, PAGE_SIZE))
    		return -EINVAL;
    
    
    	/* 如果没有权限,就结束函数 */
    	if (!capable(CAP_SYS_ADMIN))
    		return -EPERM;
    	/* 为第一次挂载创建dentry,root的inode,如果已经存在的话,就返回已经存在的 */
    	mnt = do_kern_mount(type, flags, name, data);
    	if (IS_ERR(mnt))
    		return PTR_ERR(mnt);
    	/* 执行挂载到具体的挂载点 */
    	return do_add_mount(mnt, nd, mnt_flags, NULL);
    }

    然后分析do_add_mount函数,这个函数也定义在fs/namespace.c里,定义如下
    int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
    		 int mnt_flags, struct list_head *fslist)
    {/* 所有的参数都被放在了nameidata里 */
    	int err;
    	/* 锁住内核的信号量,保持唯一性访问 */
    	down_write(&namespace_sem);
    	/* 看看目录挂载点是不是已经被挂载了,如果被挂载了,就再次寻找dentry和mount结构体 */
    	while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
    		;
    	err = -EINVAL;
    	/* 如果真的已经被挂载,就返回错误 */
    	if (!check_mnt(nd->mnt))
    		goto unlock;
    
    
    	/* 如果在一个挂载点是同一个文件系统的话就返回错误 */
    	err = -EBUSY;
    	if (nd->mnt->mnt_sb == newmnt->mnt_sb &&
    	    nd->mnt->mnt_root == nd->dentry)
    		goto unlock;
    	/* 如果根的inode是软连接,就返回错误 */
    	err = -EINVAL;
    	if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
    		goto unlock;
    	/*  把要挂载的dentry和挂载点的dentry结合,主要的工作函数*/
    	newmnt->mnt_flags = mnt_flags;
    	if ((err = graft_tree(newmnt, nd)))
    		goto unlock;
    	/* 记录在文件系统链表上 */
    	if (fslist) {
    		/* add to the specified expiration list */
    		spin_lock(&vfsmount_lock);
    		list_add_tail(&newmnt->mnt_expire, fslist);
    		spin_unlock(&vfsmount_lock);
    	}
    	up_write(&namespace_sem);
    	return 0;
    
    
    unlock:
    	up_write(&namespace_sem);
    	mntput(newmnt);
    	return err;
    }

    主要的工作是在graft_tree函数内部实现的,我们就进入graft_tree看一下,graft_tree函数定义在fs/namespace.c里,定义如下
    static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
    {
    	/*检查超级块可不可以挂载*/
    	int err;
    	if (mnt->mnt_sb->s_flags & MS_NOUSER)
    		return -EINVAL;
    	/*检查两个文件系统是不是一致的*/
    	if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
    	      S_ISDIR(mnt->mnt_root->d_inode->i_mode))
    		return -ENOTDIR;
    
    
    	err = -ENOENT;
    	mutex_lock(&nd->dentry->d_inode->i_mutex);
    	/*dead,死的也不行*/
    	if (IS_DEADDIR(nd->dentry->d_inode))
    		goto out_unlock;
    	/*安全操作*/
    	err = security_sb_check_sb(mnt, nd);
    	if (err)
    		goto out_unlock;
    	/*如果挂载点是根目录或者在缓存里边*/
    	err = -ENOENT;
    	if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry))
    		/*挂载操作*/
    		err = attach_recursive_mnt(mnt, nd, NULL);
    out_unlock:
    	mutex_unlock(&nd->dentry->d_inode->i_mutex);
    	if (!err)
    		security_sb_post_addmount(mnt, nd);
    	return err;
    }

    主要的工作是在attach_recursive_mnt函数内部实现的,attach_recursive_mnt函数定义在fs/namespace.c里,定义如下
    static int attach_recursive_mnt(struct vfsmount *source_mnt,
    			struct nameidata *nd, struct nameidata *parent_nd)
    {
    	LIST_HEAD(tree_list);
    	struct vfsmount *dest_mnt = nd->mnt;
    	struct dentry *dest_dentry = nd->dentry;
    	struct vfsmount *child, *p;
    	/*参数检查,是不是合理的,比如要挂载点在目的点的下边*/
    	if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
    		return -EINVAL;
    	/*是否可分享*/
    	if (IS_MNT_SHARED(dest_mnt)) {
    		for (p = source_mnt; p; p = next_mnt(p, source_mnt))
    			set_mnt_shared(p);
    	}
    	/*锁住vfsmount结构体,我们穿入的parent_nd是NULL所以执行else的*/
    	spin_lock(&vfsmount_lock);
    	if (parent_nd) {
    		detach_mnt(source_mnt, parent_nd);
    		attach_mnt(source_mnt, nd);
    		touch_mnt_namespace(current->nsproxy->mnt_ns);
    	} else {
    		/*dest_dentry的d_mounted++,标记已经挂载,source_mnt结构体填充*/
    		mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
    		/*把新的vfsmount提交到全局hash表*/
    		commit_tree(source_mnt);
    	}
    
    
    	list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
    		list_del_init(&child->mnt_hash);
    		commit_tree(child);
    	}
    	spin_unlock(&vfsmount_lock);
    	return 0;
    }

    展开全文
  • Linux如何mount Usb盘

    千次阅读 2013-11-15 10:57:19
    看看U盘是那个设备,然后mount -t vfat /dev/查到的磁盘 /你的某个目录 一 Linux不像Windows一样,接上新硬件后可以自动识别,在Linux下无法自动识别新硬件的,需要手动去识别。USB移动存储设备通常被识别为sda1,...

    看看U盘是那个设备,然后mount -t vfat /dev/查到的磁盘 /你的某个目录

    Linux不像Windows一样,接上新硬件后可以自动识别,在Linux下无法自动识别新硬件的,需要手动去识别。USB移动存储设备通常被识别为sda1,具体可以通过fdisk -l命令查询。

    在使用U盘前,我们先要为外挂点新建一个子目录,一般外挂点的子目录都是建立在/mnt里面的,我们也建在那里,当然也可以建在/目录下,名字可以自己定,我们就取名为usb,终端下的命令如下:

    mkdir /mnt/usb

    然后我们就可以接上我的U盘了,然后在终端下输入命令并击Enter键即可:

    mount /dev/sda1 /mnt/usb

    在Windows下当我们用完U盘后,在我们取下U盘前我们先要删除,同样在Linux下我们也要删除挂起点,方法是:

    umount /dev/sda1 /mnt/usb 或 umount /dev/sda1

    如果不把U盘给umount掉,那样很容易造成数据的丢失。

    把你的优盘插上去,打开系统终端,先看看有没有认出来
    #lsusb

    如果认出来了,你会看到usb什么的设备的

    接着进入mnt这个目录
    #cd /mnt

    新建一个目录,比如:usbdisk
    #mkdir usbdisk

    在运行命令
    #mount -t vfat -o iocharset=cp936 /dev/sda1 /mnt/usbdiak
    (这个是在一行里的哦,-t 和 -o是两个参数,sda1是你的优盘盘符)

    这样在进入/mnt/usbdisk这个目录里去看看有没有东西

    要卸载的话
    #umount /dev/sda1
    就可以了

    基本是不用配置.
    和windows系统下一样使用.
    所不同的是在linux下需要挂载一下.
    在这里我们先建立一个目录,用来将U盘挂载到该目录下:mkdir /mnt/usb

    red hat linux9 GNOME桌面环境下怎样用U盘

    方案一:
    Linux不像Windows一样,接上新硬件后可以自动识别,在Linux下无法自动识别新硬件的,需要手动去识别。USB移动存储设备通常被识别为sda1,具体可以通过fdisk -l命令查询。

    在使用U盘前,我们先要为外挂点新建一个子目录,一般外挂点的子目录都是建立在/mnt里面的,我们也建在那里,当然也可以建在/目录下,名字可以自己定,我们就取名为usb,终端下的命令如下:

    mkdir /mnt/usb

    然后我们就可以接上我的U盘了,然后在终端下输入命令并击Enter键即可:

    mount /dev/sda1 /mnt/usb

    在Windows下当我们用完U盘后,在我们取下U盘前我们先要删除,同样在Linux下我们也要删除挂起点,方法是:

    umount /dev/sda1 /mnt/usb 或 umount /dev/sda1

    如果不把U盘给umount掉,那样很容易造成数据的丢失

    方案二:
    USB的支持关键在于驱动,没有驱动设备时mount执行是肯定不成的。我在Red Hat里使用U盘的通用方法如下,屡试不爽

    1. 先别插U盘,/sbin/lsmod看是否有usb-storage。如果没有的话:

    cd /lib/modules/2.4.20-8/kernel/drivers/usb

    for v in *.o storage/*.o ; do /sbin/insmod $v ; done

    2. 这里再/sbin/lsmod,应该有usbcore、usb-ohci(或usb-uhci,根据主板芯片组而不同)、usb-storage、scsi_mod等。其中usb-storage的状态应该为

    (unused)。

    3. 插入U盘,不停的/sbin/lsmod,这期间usb-storage的状态应为Initializing,持续时间大约半分钟,其实就相当于Windows在右下角不停的弹汽球:)

    4. 初始化结束后, /sbin/fdisk -l,应该能看到/dev/sda1设备。这时,执行

    mount /dev/sda1 /mnt/udisk 才能成功。如果是MSDOS格式,又想看到中文,可以

    mount -t vfat /dev/sda1 /mnt/udisk -o iocharset=gb2312。

    (为方便可以/etc/fstab加一行,以后就可以 mount /mnt/udisk就行了)。

    5. 奇怪的是有时候fdisk是看不到sda1,但是也能mount上

    使用以上步骤挂U盘基本上可说是“万全”了。台式机、笔记本、VMware都一样。

    mkdir /mnt/usb

    mount /dev/sda1 /mnt/usb

    笔记本上安装linux用法和台式机一样

    ps:

    首先要检查lsmod|grep usb-storage 这是支持USB的模块。如果没有的话,是无法操作USB接口的设备的。

    如没有的话,可以手功modprobe usb-storage,一般查看U盘可以用fdisk -l可以很清楚的看到这个硬件的设备名,比猜测硬件名要准确多了.
    这样就在mnt下建立了一个叫做usb的文件夹。
    现在应该挂载了:mount -t -vfat /dev/sda1 /mnt/usb
    以上语句便将u盘挂载到了usb文件夹中。其中的-t -vfat是您u盘的分区格式。

    mount是挂载usb的命令,只是后面被挂载的对象没写对。而且挂载对象不存在
    正确做法如下:

    插好usb之后
    先用disk -l命令看一下你的usb设备名称是什么,列出的表格里面有fat格式的就是你的usb设备名字,可能是sda1,或者sda2什么的
    假设是sda1的话,就
    mount -t vfat /dev/sda1(注意这里是你usb在linux设备名) /mnt/

    就可以了。

    然后进入mnt目录就是你usb内容了。

    以下是脚本

    通常情况下,U盘设备应该是 /dev/sda1, 但是,有时候先后使用两个不同的U盘时,后面插入的一个是 /dev/sdb1,所以,使用了 fdisk -l | grep sd[b-z]1 | awk '{print $1}' 来取得它的设备名。代码只能mount 一个U盘,因为我基本上不会同时插入两个U盘,有这一需求的朋友可以自己修改。

    #! /bin/sh
    #
    # Mount/Unmount a USB disk.
    #
    # Mount:
    #    We try to mount /dev/sda1 first, if failed, search /dev/sdb1, /dev/sdc1 ...,
    #    and try to mount the 1st one we found.
    #

    MOUNT_DIR=/mnt/usb
    MOUNT_DEV=/dev/sda1

    mount | grep $MOUNT_DIR

    if [ "$?" = "0" ]
    then

        # Kill all process locked the USB disk
        fuser -m -v -k -i $MOUNT_DIR

        umount $MOUNT_DIR
        if [ "$?" = "0" ]
        then
            echo USB disk at $MOUNT_DIR has been unmounted
        else
            echo Can not unmount $MOUNT_DIR
        fi

    else
        mount -t vfat /dev/sda1 $MOUNT_DIR -o codepage=936,iocharset=cp936 2>/dev/null

        if [ "$?" = "0" ]
        then
            echo USB disk $MOUNT_DEV has been mounted at $MOUNT_DIR
        else

            #
            # Search all USB disk devices, and mount the first one
            #
            for MOUNT_DEV in $(fdisk -l | grep sd[b-z]1 | awk '{print $1}')
            do
                if [ "$?" = "0" ]
                then
                    mount -t vfat $MOUNT_DEV $MOUNT_DIR -o codepage=936,iocharset=cp936 2>/dev/null
                    if [ "$?" = "0" ]
                    then
                        echo USB disk $MOUNT_DEV has been mounted at $MOUNT_DIR
                        exit 0
                    fi
                fi
            done

            echo Can not mount $MOUNT_DEV to $MOUNT_DIR

        fi

    fi

    exit 0

    注意:如果U盘是在Windows下格式化的,则上面可能没有分区,所以,U盘设备是/dev/sda (而不是/dev/sda1),则上面的脚本不合适,需要修改。

    展开全文
  • Linux 磁盘挂载和mount共享

    千次阅读 2014-07-27 11:34:01
    其中根据文章http://blog.163.com/luoxiaoyu1010@126/blog/static/16581022720108162830579/也配置 ... 针对Linux服务器的磁盘挂载mount和共享做简单操作说明: 1、 查看已使用的磁盘情况 df –h

    其中根据文章http://blog.163.com/luoxiaoyu1010@126/blog/static/16581022720108162830579/也配置

    原文:http://blog.csdn.net/catoop/article/details/7334901

    针对Linux服务器的磁盘挂载mount和共享做简单操作说明:

    1、  查看已使用的磁盘情况

    df –h


     

     

    2、  查看所有磁盘

    fdisk –l


     

    3、  查看指定磁盘“/dev/xvde”的分区情况

    fdisk -l /dev/xvde


     

    如图可以看到,改磁盘并未分区

    4、  我们现在需要将/dev/xvde磁盘挂载mount到文件系统的/opt/huiyy目录上,

    首先对磁盘“/dev/xvde”分区(这里我们采用1个分区)

    命令fdisk /dev/xvde回车,按如下操作进行:


     

    输入w写入


     

    使用fdisk –l查看刚刚分配的磁盘号


     

    5、 格式化磁盘

    命令mkfs -t ext3 /dev/xvde1

    -t 指定格式化磁盘的文件系统类型为ext3,默认不指定为ext2(linux老文件系统类型)

    后面跟刚刚创建的分区号“/dev/xvde1”


     

     

    6、  挂载磁盘

    a)        创建磁盘挂载目标文件夹

    命令mkdir /opt/huiyy

    b)        挂载磁盘

    命令mount /dev/xvde1/opt/huiyy

    c)        记录到/ext/fstab文件中(防止重启后挂载丢失)

    命令vi /etc/fstab


     

    至此磁盘挂载完成,现在我们可以向/opt/huiyy中写文件了,文件将会被写入到我们挂载的磁盘中。

     

    7、  配置共享

    共享目录“/opt/huiyy/resource”和“/opt/huiyy/log”权限“rw,sync,no_root_squash”

    命令vi /etc/exports编辑内容后:wq保存退出


     

    共享查看命令为 more/etc/exports


     

    8、  客户端mount此目录

    我们把以上配置共享目录的服务器作为服务器A,把我们现在准备mount共享目录的服务器作为服务器B

    a)        登录服务器B

    b)        mount目录 “/opt/huiyy/log”(其中61.132.254.160为服务器A)

    mount  -t nfs -orw,bg,hard,nointr,rsize=32768,wsize=32768,tcp,vers=3,timeo=600,actimeo=061.132.254.160:/opt/huiyy/log /opt/huiyy/log

    /opt/huiyy/log 是挂载到本机的目录,可以自己定义。

    c)        保存mount挂载配置到/etc/fstab(防止服务器重启后挂载丢失)

    命令vi /etc/fstab编辑

    命令cat /etc/fstab查看配置


     

    现在服务器B和文件源服务器A上都可以对目录“/opt/huiyy/log”下创建文件、目录了。

     

    结束

    展开全文
  • linux内核sys_mount()分析

    千次阅读 2013-06-07 19:29:31
    1. 概述 今天本来打算写一篇关于linux内核sys_mount()代码分析的文章,习惯性地打开google,看看是否有别人写的比较好,可以参考参考,打开第一篇发现居然是自己以前写的博客,真是囧,而且也记录在CSDN上,本想...

    1. 概述

            今天本来打算写一篇关于linux内核sys_mount()代码分析的文章,习惯性地打开google,看看是否有别人写的比较好,可以参考参考,打开第一篇发现居然是自己以前写的博客,真是囧,而且也记录在CSDN上,本想直接行使拿来主义,但看了下内容,发觉略有不妥,于是在那篇博客的基础上进行些修改和润色。
            Linux下任何块设备在使用之前,首先要对其进行格式化成特定文件系统,如mkfs.ext3等,然后再将其挂载到系统中特定目录下,于是通过该挂载点即可访问该块设备文件系统下的所有文件和目录等。如 mount -t ext3 -o option /device/hda  /home/mountdir便将设备/dev/hda挂载到了/home/mountdir下,我们今天的重点就是钻研kernel在该命令下到底做了些什么。参考的内核版本是linux2.6.36,本人博客如无作特别说明,内核的版本都是2.6.36。

    2. 实现

    2.1 相关数据结构

           相关数据结构留到我们代码分析完成以后再写。

    2.2 sys_mount()流程分析

          内核mount函数的入口为sys_mount(),你需要在fs/namespace.c的SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data)才能找到其实现,其源代码如下:
    SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
    		char __user *, type, unsigned long, flags, void __user *, data)
    {
    	int ret;
    	char *kernel_type;
    	char *kernel_dir;
    	char *kernel_dev;
    	unsigned long data_page;
    
    	/* 以下几个函数将用户态参数拷贝至内核态,包括:
    	** 1. kernel_type:挂载文件系统类型,如ext3
    	** 2. kernel_dir:   挂载点路径
    	** 3. dev_name:  设备名称
    	** 4. data_pages: 选项信息
    	*/
    	ret = copy_mount_string(type, &kernel_type);
    	if (ret < 0)
    		goto out_type;
    
    	kernel_dir = getname(dir_name);
    	if (IS_ERR(kernel_dir)) {
    		ret = PTR_ERR(kernel_dir);
    		goto out_dir;
    	}
    
    	ret = copy_mount_string(dev_name, &kernel_dev);
    	if (ret < 0)
    		goto out_dev;
    
    	ret = copy_mount_options(data, &data_page);
    	if (ret < 0)
    		goto out_data;
    	
    	/*  调用do_mount 完成主要挂载工作 */
    	ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
    		(void *) data_page);
    
    	free_page(data_page);
    out_data:
    	kfree(kernel_dev);
    out_dev:
    	putname(kernel_dir);
    out_dir:
    	kfree(kernel_type);
    out_type:
    	return ret;
    }
    总体上来说,该函数还算比较简单,主要就作了两件事:
    1. 将用户态的参数拷贝至内核态,因为在后面需要使用这些参数(为什么需要,难道内核态不能直接使用用户态的数据吗?);
    2. 调用do_mount()接手接下来的挂载工作。
    /* 参数:  
    ** dev_name :   挂载设备名称
    ** dir_name :   挂载点名称
    ** type_page:   保存挂载的文件系统类型,如"ext3"
    ** flags    :   挂载标志
    ** data_page:   大部分情况下为NULL
    */
    long do_mount(char *dev_name, char *dir_name, char *type_page,
    		  unsigned long flags, void *data_page)
    {
    	struct path path;
    	int retval = 0;
    	int mnt_flags = 0;
    
    	/* Discard magic */
    	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
    		flags &= ~MS_MGC_MSK;
    
    	/* Basic sanity checks */
    
    	if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
    		return -EINVAL;
    
    	if (data_page)
    		((char *)data_page)[PAGE_SIZE - 1] = 0;
    
    	/* 调用kern_path(),根据挂载点名称查找其dentry等信息
    	** 参数:@dir_name     : 挂载点路径
    	**     :@LOOKUP_FOLLOW: 查找标志,遇到链接继续查找
    	**     :@path         : 查找结果保存与此结构中
    	*/
    	retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
    	if (retval)
    		return retval;
    	/* 安全相关,忽略*/
    	retval = security_sb_mount(dev_name, &path,
    				   type_page, flags, data_page);
    	if (retval)
    		goto dput_out;
    
    	/*  对于挂载标志的检查和初始化,忽略 */
    	/* Default to relatime unless overriden */
    	if (!(flags & MS_NOATIME))
    		mnt_flags |= MNT_RELATIME;
    
    	/* Separate the per-mountpoint flags */
    	if (flags & MS_NOSUID)
    		mnt_flags |= MNT_NOSUID;
    	if (flags & MS_NODEV)
    		mnt_flags |= MNT_NODEV;
    	if (flags & MS_NOEXEC)
    		mnt_flags |= MNT_NOEXEC;
    	if (flags & MS_NOATIME)
    		mnt_flags |= MNT_NOATIME;
    	if (flags & MS_NODIRATIME)
    		mnt_flags |= MNT_NODIRATIME;
    	if (flags & MS_STRICTATIME)
    		mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
    	if (flags & MS_RDONLY)
    		mnt_flags |= MNT_READONLY;
    
    	flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
    		   MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
    		   MS_STRICTATIME);
    
    	/* 我们不关心别的,只关注do_new_mount */
    	if (flags & MS_REMOUNT)
    		retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
    				    data_page);
    	else if (flags & MS_BIND)
    		retval = do_loopback(&path, dev_name, flags & MS_REC);
    	else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
    		retval = do_change_type(&path, flags);
    	else if (flags & MS_MOVE)
    		retval = do_move_mount(&path, dev_name);
    	else
    		retval = do_new_mount(&path, type_page, flags, mnt_flags,
    				      dev_name, data_page);
    dput_out:
    	path_put(&path);
    	return retval;
    }
            在do_mount()函数中,作了一些标志位的检查,安全性检查等附加工作,然后根据不同的flag来调用不同的挂载函数,我们这里主要关注普通块设备的挂载,其他的以后遇到再分析吧,这这里面调用了下面两个主要的函数:
    1. kern_path():该函数的主要作用是根据挂载点的路径名在内核中查找其内存目录项结构(struct dentry),保存在path中;
    2. do_new_mount(),该函数接手来完成接下来的挂载工作。
    /* 参数:
    ** @path: 保存挂载点信息,包括dentry结构等
    ** @type: 挂载文件系统类型,如ext3
    ** @flags & mnt_flags:挂载标志
    ** @name:设备名称
    ** @data: 保存额外的数据,一般是NULL
    */
    static int do_new_mount(struct path *path, char *type, int flags,
    			int mnt_flags, char *name, void *data)
    {
    	struct vfsmount *mnt;
    
    	if (!type)
    		return -EINVAL;
    
    	/* we need capabilities... */
    	/* 必须是root权限*/
    	if (!capable(CAP_SYS_ADMIN))
    		return -EPERM;
    
    	lock_kernel();
    	/* 调用do_kern_mount()来完成挂载第一步,主要是创建一个vfsmount结构并对其初始化
    	** 并读设备的superblock并初始化文件系统在内存中的结构等
    	*/
    	mnt = do_kern_mount(type, flags, name, data);
    	unlock_kernel();
    	if (IS_ERR(mnt))
    		return PTR_ERR(mnt);
    	/* 将上面创建的vfsmount结构添加到全局结构中
    	** 形成目录树结构
    	*/
    	return do_add_mount(mnt, path, mnt_flags, NULL);
    }
    do_new_mount()主要调用两个函数,进行具体挂载:
    1. do_kern_mount():该函数主要是为新的文件系统准备一个挂载结构vfsmount,初始化,并从设备上读出超级块等信息,在内存中构建文件系统的轮廓,会在后面具体描述这一过程;
    2. do_add_mount():将1中创建的vfsmount结构添加到全局结构中,以便在内存中形成一棵树结构。
    让我们对这两个函数一一击破吧,先从do_kern_mount,主要的挂载工作在该函数中完成。
    /* 参数: fstype: 文件系统名称,如"ext3"
    **       flags : 挂载标志
    **       name  : 设备名称,如"/dev/sda"
    **       data  : 其他挂载数据,暂时忽略
    */
    struct vfsmount *
    do_kern_mount(const char *fstype, int flags, const char *name, void *data)
    {
    	/* 首先根据文件系统名称获取文件系统结构file_system_type
    	** 内核中所有支持的文件系统的该结构通过链表保存
    	*/
    	struct file_system_type *type = get_fs_type(fstype);
    	struct vfsmount *mnt;
    	if (!type)
    		return ERR_PTR(-ENODEV);
    	
    	/* 调用vfs_kern_mount()完成主要挂载*/
    	mnt = vfs_kern_mount(type, flags, name, data);
    	if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
    	    !mnt->mnt_sb->s_subtype)
    		mnt = fs_set_subtype(mnt, fstype);
    	put_filesystem(type);
    	return mnt;
    }
    该函数主要流程是:1. 调用get_fs_type()通过文件系统名查找到文件系统的struct file_system_type结构;2.通过vfs_kern_mount()来进行主要的挂载。
    /* 主要挂载工作
    ** 参数: type : 文件系统内存结构
    **     : flags: 挂载标志
    **     : name : 设备名称
    **     : data : 额外挂载参数,忽略
    */
    struct vfsmount *
    vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
    {
    	struct vfsmount *mnt;
    	char *secdata = NULL;
    	int error;
    
    	if (!type)
    		return ERR_PTR(-ENODEV);
    
    	error = -ENOMEM;
    	//首先,分配一个代表挂载结构的struct vfs_mount结构
    	mnt = alloc_vfsmnt(name);
    	if (!mnt)
    		goto out;
    
    	if (flags & MS_KERNMOUNT)
    		mnt->mnt_flags = MNT_INTERNAL;
    
    	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
    		secdata = alloc_secdata();
    		if (!secdata)
    			goto out_mnt;
    
    		error = security_sb_copy_data(data, secdata);
    		if (error)
    			goto out_free_secdata;
    	}
    
    	//调用具体文件系统的get_sb方法
    	//从name代表的设备上读出超级块信息
    	error = type->get_sb(type, flags, name, data, mnt);
    	if (error < 0)
    		goto out_free_secdata;
    	BUG_ON(!mnt->mnt_sb);
    	WARN_ON(!mnt->mnt_sb->s_bdi);
    	mnt->mnt_sb->s_flags |= MS_BORN;
    
    	//与安全相关,暂时忽略
    	error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
    	if (error)
    		goto out_sb;
    
    	/*
    	 * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
    	 * but s_maxbytes was an unsigned long long for many releases. Throw
    	 * this warning for a little while to try and catch filesystems that
    	 * violate this rule. This warning should be either removed or
    	 * converted to a BUG() in 2.6.34.
    	 */
    	WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
    		"negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);
    
    	//设置挂载点的dentry结构为刚读出的设备的根目录
    	mnt->mnt_mountpoint = mnt->mnt_root;
    	mnt->mnt_parent = mnt;
    	up_write(&mnt->mnt_sb->s_umount);
    	free_secdata(secdata);
    	return mnt;
    out_sb:
    	dput(mnt->mnt_root);
    	deactivate_locked_super(mnt->mnt_sb);
    out_free_secdata:
    	free_secdata(secdata);
    out_mnt:
    	free_vfsmnt(mnt);
    out:
    	return ERR_PTR(error);
    }
            我们追踪到这里对于第一个部分的函数调用do_kern_mount()就已经基本结束,在这个相当底层的函数中,它主要调用了具体文件系统的get_sb()方法来从设备上读出超级块,这个我们会在具体文件系统剖析的时候去作深入地分析。其实就是读出超级块,作有效性检查,内存构造根目录项等工作。
            至此,我们第一部分的主要工作就完成了,在该部分的核心工作就是创建一个struct vfsmount,并读出文件系统超级块来初始化该结构。接下来就是将该结构添加到全局结构中,这就是do_add_mount()的主要工作。
    int do_add_mount(struct vfsmount *newmnt, struct path *path,
    		 int mnt_flags, struct list_head *fslist)
    {
    	int err;
    
    	mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
    
    	//这里为什么需要对namespace_sem加锁呢
    	down_write(&namespace_sem);
    	/* Something was mounted here while we slept */
    	//如果在获取信号量的过程中别人已经进行了
    	//挂载,那么我们进入已挂载文件系统的根目录
    	//如此继续
    	while (d_mountpoint(path->dentry) &&
    	       follow_down(path))
    		;
    	err = -EINVAL;
    	if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
    		goto unlock;
    
    	/* Refuse the same filesystem on the same mount point */
    	//重复挂载,毫无意义
    	err = -EBUSY;
    	if (path->mnt->mnt_sb == newmnt->mnt_sb &&
    	    path->mnt->mnt_root == path->dentry)
    		goto unlock;
    
    	err = -EINVAL;
    	if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
    		goto unlock;
    
    	newmnt->mnt_flags = mnt_flags;
    	//主要调用graft_tree()来实现文件系统目录树结构
    	//其中newmnt是本次创建的vfsmount结构
    	//path是挂载点信息
    	if ((err = graft_tree(newmnt, path)))
    		goto unlock;
    
    	if (fslist) /* add to the specified expiration list */
    		list_add_tail(&newmnt->mnt_expire, fslist);
    
    	up_write(&namespace_sem);
    	return 0;
    
    unlock:
    	up_write(&namespace_sem);
    	mntput(newmnt);
    	return err;
    }
    
    do_add_mount()主要调用了函数graft_tree()来实现linux文件系统的目录树结构。
    /*参数:  mnt: 本次挂载新建的vfsmount结构
    **           : path: 挂载点的结构
    */
    static int graft_tree(struct vfsmount *mnt, struct path *path)
    {
    	int err;
    	if (mnt->mnt_sb->s_flags & MS_NOUSER)
    		return -EINVAL;
    
    	if (S_ISDIR(path->dentry->d_inode->i_mode) !=
    	      S_ISDIR(mnt->mnt_root->d_inode->i_mode))
    		return -ENOTDIR;
    
    	err = -ENOENT;
    	mutex_lock(&path->dentry->d_inode->i_mutex);
    	if (cant_mount(path->dentry))
    		goto out_unlock;
    
    	if (!d_unlinked(path->dentry))
    		err = attach_recursive_mnt(mnt, path, NULL);
    out_unlock:
    	mutex_unlock(&path->dentry->d_inode->i_mutex);
    	return err;
    }
    
    该函数的处理也是首先检查挂载的有效性(如挂载点是否是目录)等,而真正的核心函数是attach_recursive_mnt()。
    static int attach_recursive_mnt(struct vfsmount *source_mnt,
    			struct path *path, struct path *parent_path)
    {
    	LIST_HEAD(tree_list);
    	struct vfsmount *dest_mnt = path->mnt;
    	struct dentry *dest_dentry = path->dentry;
    	struct vfsmount *child, *p;
    	int err;
    
    	//这个与sharemount相关,暂时忽略
    	if (IS_MNT_SHARED(dest_mnt)) {
    		err = invent_group_ids(source_mnt, true);
    		if (err)
    			goto out;
    	}
    	err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);
    	if (err)
    		goto out_cleanup_ids;
    
    	br_write_lock(vfsmount_lock);
    
    	if (IS_MNT_SHARED(dest_mnt)) {
    		for (p = source_mnt; p; p = next_mnt(p, source_mnt))
    			set_mnt_shared(p);
    	}
    	//因为传入的parent_path是NULL,所以我们
    	//对else分支比较感兴趣
    	if (parent_path) {
    		detach_mnt(source_mnt, parent_path);
    		attach_mnt(source_mnt, path);
    		touch_mnt_namespace(parent_path->mnt->mnt_ns);
    	} else {
    		//这里主要是将新建的source_mnt与挂载点(dest_dentry) 挂钩
    		//并和挂载点所在的dest_mnt建立起父子关系
    		mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
    		//该函数也比较简单,不深入了
    		commit_tree(source_mnt);
    	}
    
    	//这个是干嘛呢
    	list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
    		list_del_init(&child->mnt_hash);
    		commit_tree(child);
    	}
    	br_write_unlock(vfsmount_lock);
    
    	return 0;
    
     out_cleanup_ids:
    	if (IS_MNT_SHARED(dest_mnt))
    		cleanup_group_ids(source_mnt, NULL);
     out:
    	return err;
    }
            该函数中很多的代码可能与sharemount机制相关,关于该机制我会在另外一篇博客中专门阐述,这里就忽略了,因为参数parent_path主要为NULL,所以我们比较关注的代码分支是else,即mnt_set_mountpoint和commit_tree,其实两个函数干的事情都比较简单,主要是将当前新建的vfsmount结构与挂载点挂钩,并和挂载点所在的vfsmount形成一种父子关系结构。形成的结构图如下所示:



            至此,整个的mount过程的代码分析也就差不多了,虽然个中还有不少的细节问题需要再深入地研究。最后,让我们归纳下整个的调用流程:

    sys_mount()
           |
           -----------> do_mount()
                                    |
                                    -------------> do_new_mount()
                                                                  |
                                                                  -----------> do_kern_mount()
                                                                  |                            |
                                                                  |                             --------> vfs_kern_mount()
                                                                  |
                                                                  -------------> do_add_mount()
                                                                                                |
                                                                                                -----------> graft_tree()
                                                                                                                         |
                                                                                                                         --------> attach_recursive_mnt()



    展开全文
  • mount.cifs(8) System Administration mount.cifs(8) 名称 mount.cifs - 挂载通用网际文件系统(Common Internet File System) 语法 mount.cifs {service} {mount-point} [-o options] 描述 .
  • 细说linux挂载——mount,及其他……

    千次阅读 2012-08-07 23:57:15
    这个被挂载为/的分区,就叫做根分区(不管它是主分区还是逻辑分区),它从此开始在整儿linux系统里具有了特殊的地位,因为整儿电脑里的所有硬盘,包括其上的所有其他分区,不管是主分区、逻辑分区,都将以这个“根...
  • C语言中linux下查看sd卡mount的位置

    千次阅读 2016-03-02 16:49:44
    linux 查找sd卡mount的位置: #include //std::istringstream #include //FILE and popen #include ......其他头文件...... std::string getSDCardPath(); 主函数(getSDFCardPath); ......其他函数.........
  • Linux分区和挂载(mount命令的学习)

    千次阅读 2013-07-22 16:28:28
    同样,在Linux中也需要分区,但是Linux中对于分区的表示方式与Windows并不一样。下面将以我的电脑为例,讲述 Liunx的分区方法并且通过讲述Linux的启动过程来了解Linux系统和挂载。(本文章适合初学Linux的同
  • mount ntfs/fat32,linux挂载

    2012-12-09 22:49:46
    刚开始学习linux,对于挂载u盘、移动硬盘等不是很懂,在网上看了很多资料,自己也尝试了很多次,终于找到了下面的挂载方法: 对于fat32类型: 1. 查看是否已加载有需要的模块: lsmod  U盘挂载需要的加载模块...
  • linux的临时挂载(mount)与永久挂载

    万次阅读 2020-04-29 16:17:04
    mount命令的详细介绍 设备的临时挂载 设备的永久挂载 设备挂载 在系统中有设备id的设备是可以被系统使用的 验证:在系统中有设备id的设备可以被系统使用 在虚拟机中添加一块新的硬盘(sda),添加后使用mount /dev...
  • 昨天部署项目的时候,涉及到了不同服务器之间的文件共享,就用到了mount挂载命令: 具体过程见我的上篇博客:http://blog.csdn.net/zly412934578/article/details/72832797 注意:之前有人私信我,同一个服务上...
  • 一、简单用法 $ mount /dev/hda2 /home 第一个叁数是与包括文件系统的磁盘或分区相关的设备文件。 第二个叁数是要mount到的目录。...如果想在运行的Linux下访问其它文件系统中的资源的话,就要用mount命令来实
  • 文章目录fdisk文件分区fdisk分区步骤lvm文件分区pv vgs lvs命令结构挂载磁盘和设备mount查看磁盘占用df -h查看可用设备块blkid磁盘格式化mkfs fdisk文件分区 fdisk方式分区参考 -b<分区大小>:指定每个分区的...
  • 上班之余抽点时间出来写写...vmware-mount是vmware的一个工具,windows和linux都有。 windows可以直接在vmware网站下载。 linux可以通过两个方面获取: 1、安装vmware-workstation客户端的linux版,安装实现将会...
  • linux2.6.36文件系统分析---sys_mount()

    千次阅读 2011-12-30 08:56:35
     在linux上,任何磁盘或磁盘的分区在被使用之前必须首先被格式化成某个特定的文件系统(如ext2,ext3等),然后挂载到某个系统的目录树上,只有这样,该磁盘或分区才能被用户看到,使用。挂载一个文件系统的常用格式...
  • 这里对Linux创建扩展分区和逻辑分区,以及格式化分区和挂载分区进行一个详细的介绍,希望能帮助到你们! 1.Linux 创建扩展分区以及逻辑分区 [root@localhost ~]# fdisk /dev/sdb //进入要分配的磁盘 WARNING: ...
  • Linux从青铜到王者】第一篇:Linux常见指令

    万次阅读 多人点赞 2021-05-24 16:24:09
    文章目录系列文章目录前言一、Linux是什么二、Linux下基本指令1.ls指令2.pwd指令3.cd指令4.touch指令5.mkdir指令6.rmdir指令7.rm指令8.man指令9.echo指令10.cp指令2.读入数据总结 前言 一、Linux是什么 Linux是...
  • 上图蓝色部分是写硬盘卷标的,如不想要卷标可直接按回车,现在分区好了我们用mount 挂载一下该分区即可使用了,这里我把它挂载到mnt目录下,也可以自建一个目录挂载 来看一下分区大小是否和预定的一样,使用df...
  • mount 详解

    千次阅读 2016-01-05 13:05:54
    挂接命令(mount)  首先,介绍一下挂接(mount)命令的使用方法,mount命令参数非常多,这里主要讲一下今天我们要用到的。  命令格式:mount [-t vfstype] [-o options] device dir  其中:  1.-t vfstype 指定...
  • LinuxLinux常用命令

    万次阅读 2018-05-29 09:55:03
    Linux是一个可靠的、高性能的系统,而所有的这些优越性只有在直接使用Linux命令行时才能充分地体现出来。Linux系统安装成功后,就可以进入到与Windows类似的图形化界面了。这个界面就是Linux图形化界面X窗口系统的一...
  • Mount 使用方法 (中文版man mount

    万次阅读 2011-11-24 12:29:34
    Man page of MOUNT ...Section: Linux Programmer's Manual (8) Updated: 14 September 1997 Index 返回主目录   NAME mount - 挂载文件系统  SYNOPSIS 总览 mount [-lhV] mount
  • 文章目录linux 系统命令总结大全关于作者**作者介绍**0.IP 地址相关命令0.1 ifconfig 命令 ---显示网络设备信息02.ip 命令 ---显示与操作路由03.dhclient 命令 --- 动态获取或释放IP地址04.nmtui ---界面修改网卡...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,429
精华内容 14,971
关键字:

linux结束mount

linux 订阅