怎么调用mknodexcel函数高手啊,高手指教

一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!
二、互相尊重,对自己的言论和行为负责。
本文标题:
本页链接:2237人阅读
linux内核(20)
我们注册完字符、块驱动设备后,一般会用mknod去建立,应用层与驱动的管道。例如mtd的字符驱动,我们会用mknod &/dev/mtdchar1 &c 30 0,创建/dev/mtdchar1来映射mtd第一个字符设备。
由于代码没法红字,代码中有color:#ff6666
&开头的行数,为重点分析的
接下来我们用代码来分析,这个过程。
第一个函数asmlinkage long
sys_mknod(const char __user *filename, int mode, unsigned dev) & &//在fs/namei.c里,filename就是所创建的路径:/dev/mtdchar1,mode:&S_IFCHR &| S_IRUSR | S_IWUSR,这个意思是可读可写的字符特殊文件;dev为设备号,主设备号跟次设备号的结合体。
上面那个函数没什么可分析的,再下来进入&long sys_mknodat(int dfd, const char __user *filename, int mode,unsigned dev)
asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
unsigned dev)
int error = 0;
struct dentry *
if (S_ISDIR(mode))
return -EPERM;
tmp = getname(filename); //比较安全的一种方式把filename放到tmp所指的空间里
if (IS_ERR(tmp))
return PTR_ERR(tmp);
error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd); //nd-&dentry为当前文件系统(rootfs)的主dentry
//当前fs下,也就是rootfs('/')
if (error)
dentry = lookup_create(&nd, 0); //生成一个新的dentry
//dentry-&d_parnet=nd.dentry
error = PTR_ERR(dentry);
if (!IS_POSIXACL(nd.dentry-&d_inode))
mode &= ~current-&fs-&
if (!IS_ERR(dentry)) {
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(nd.dentry-&d_inode,dentry,mode,&nd);
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(nd.dentry-&d_inode,dentry,mode,
new_decode_dev(dev)); //生成一个inode挂载到dentry
//inode-&i_sb=nd.dentry-&d_inode-&i_sb
inode-&i_rdev=dev
case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.dentry-&d_inode,dentry,mode,0);
case S_IFDIR:
error = -EPERM;
error = -EINVAL;
dput(dentry);
mutex_unlock(&nd.dentry-&d_inode-&i_mutex);
path_release(&nd);
putname(tmp);
上面那个函数里字体加红的部分为重点部分。再进行分析之前,我们先要搞清楚,dentry,inode之间的关系,其实嘛很简单,就是dentry-&d_inode=inode,就是通过dentry可以找到相对应的inode,至于inode里面的其他操作就各有风情。我个人是这么理解的,单细胞生物考虑问题就这么简单。
下面进行那两个函数的分析。第一个error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);第一眼看到这个函数,就应该知道这个函数主要作用是给nd做配置。
static int fastcall do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
int retval = 0;
struct file *
struct fs_struct *fs = current-&
nd-&last_type = LAST_ROOT; /* if there are only slashes... */
nd-&flags =
nd-&depth = 0;
if (*name=='/') {
//表明在根目录下,那么对应的mnt和dentry很简单就是根目录的mnt和dentry
read_lock(&fs-&lock);
if (fs-&altroot && !(nd-&flags & LOOKUP_NOALT)) {
nd-&mnt = mntget(fs-&altrootmnt);
nd-&dentry = dget(fs-&altroot);
read_unlock(&fs-&lock);
if (__emul_lookup_dentry(name,nd))
/* found in altroot */
read_lock(&fs-&lock);
nd-&mnt = mntget(fs-&rootmnt);
nd-&dentry = dget(fs-&root);
read_unlock(&fs-&lock);
} else if (dfd == AT_FDCWD) {
read_lock(&fs-&lock);
nd-&mnt = mntget(fs-&pwdmnt);
nd-&dentry = dget(fs-&pwd);
read_unlock(&fs-&lock);
…………//此处省略1024,为了排版好看点
current-&total_link_count = 0;
retval = link_path_walk(name, nd); //nd的dentry,inode,last
if (likely(retval == 0)) {
if (unlikely(!audit_dummy_context() && nd && nd-&dentry &&
nd-&dentry-&d_inode))
audit_inode(name, nd-&dentry-&d_inode);
fput_fail:
fput_light(file, fput_needed);
题外话:客官或许对current-&fs有疑问,这个我一开始也蛋疼了很久,其实嘛很简单,在start_kernel函数,也就是linux起来初始化的时候对其进行了初始化。start_kernel有个vfs_caches_init,vfs_caches_init里有个mnt_init,mnt里有个init_mount_tree,在init_mount_tree里有两行代码,对current-&fs进行了赋值。
mnt = do_kern_mount(&rootfs&, 0, &rootfs&, NULL); //ns-&mnt为rootfsset_fs_pwd(current-&fs, ns-&root, ns-&root-&mnt_root); //设置当前pwd为ns-&root
//fs-&pwdmnt=mnt
fs-&pwd=dentry
set_fs_root(current-&fs, ns-&root, ns-&root-&mnt_root);//设置当前root为ns-&root
//fs-&rootmnt=mnt
fs-&root=dentry
题外话完毕,上面那个函数的重点,是红字部分,也就是link_path_walk(name, nd);这个函数干嘛呢,看名字好像跟路径有关系,管它呢,接着一条道走到黑,进去再说。
int fastcall link_path_walk(const char *name, struct nameidata *nd)
struct nameidata save = *
/* make sure the stuff we saved doesn't go away */
dget(save.dentry); //保存信息,出错的时候可以纠正
mntget(save.mnt);
result = __link_path_walk(name, nd); //nd的dentry和mnt,last进行查找
//比如name为dev/console
//dentry,mnt均为dev的,last里保存console信息
if (result == -ESTALE) {
dget(nd-&dentry);
mntget(nd-&mnt);
nd-&flags |= LOOKUP_REVAL;
result = __link_path_walk(name, nd);
dput(save.dentry);
mntput(save.mnt);
} 这个函数看上去简单很多了,老规矩,接着红字部分
static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
.......//为了看上去短点,这里省略一些代码,具体可以对照namei.c
while (*name=='/') //如果是根目录的话,去掉根目录
inode = nd-&dentry-&d_
/* At this point we know we have a real path component. */
nd-&flags |= LOOKUP_CONTINUE;
err = exec_permission_lite(inode, nd); //可执行
this.name =
c = *(const unsigned char *)
hash = init_name_hash();
//一个路径一个路径的hash。比如dev/mtdchar1,先dev三个字符hash一下,去查找
hash = partial_name_hash(c, hash);
c = *(const unsigned char *)
} while (c && (c != '/'));
this.len = name - (const char *) this.
this.hash = end_name_hash(hash); //这个hash值不是this.name的,而是其中一个路径的,例如dev的
/* remove trailing slashes? */
goto last_ //如果是最后一项了,比如已经到mtdchar1了
while (*++name == '/');
if (!*name)
goto last_with_
..........//省略一些代码
err = do_lookup(nd, &this, &next); //根据name.hash查找dentry和mnt,next指向这些
//比如/dev/mtdchar1 ,第一次这里先找到了dev的dentry和mnt
……//省略
if (inode-&i_op-&follow_link) {
err = do_follow_link(&next, nd);
inode = nd-&dentry-&d_
path_to_nameidata(&next, nd);//nd的mnt和dentry指向next的mnt和dentry
//然后nd的mnt和dentry也有了变化
//翻转上去
/* here ends the main loop */
last_with_slashes:
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
nd-&flags &= lookup_flags | ~LOOKUP_CONTINUE;
if (lookup_flags & LOOKUP_PARENT)
goto lookup_
……//省略一部分代码
err = do_lookup(nd, &this, &next);
inode = next.dentry-&d_
if ((lookup_flags & LOOKUP_FOLLOW)
&& inode && inode-&i_op && inode-&i_op-&follow_link) {
err = do_follow_link(&next, nd);
goto return_
inode = nd-&dentry-&d_
path_to_nameidata(&next, nd);
err = -ENOENT;
if (!inode)
if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR;
if (!inode-&i_op || !inode-&i_op-&lookup)
goto return_
lookup_parent:
nd-&last = //nd的last指向最后那个this?
//比如/dev/console的话,这个nd-&last就指向了this,也就是name是console,相应的hash也是console
nd-&last_type = LAST_NORM;
if (this.name[0] != '.')
goto return_
if (this.len == 1)
nd-&last_type = LAST_DOT;
else if (this.len == 2 && this.name[1] == '.')
nd-&last_type = LAST_DOTDOT;
goto return_
return_reval:
……//省略
return_base:
dput_path(&next, nd);
path_release(nd);
return_err:
上面这个函数太长了,裁掉一些懒得分析的,或者说太烦的,或者说不懂的,主要还是不懂的^_^。接下来函数功能分析,一开始假设我们进来的/dev/mtdchar1,是根目录,进来的第一件事是去掉根目录,剩下dev/mtdchar1,然后进入一个大循环,这个为什么要大循环呢,作用就是把路径一层一层给剥掉。dev/mtdchar1剥成dev和mtdchar1,这个过程就是while
(c && (c != '/'))这个小循环里干的,在这个小循环里将路径转换成hash,通过err = do_lookup(nd, &this, &next);去查找dentry和mnt,也就是根据this里的hash值去nd里面查相对应的dentry,没有的话,创建一个,创建的dentry-&d_parent=nd-&dentry。关于dentry,后面有空再分析吧,比较烦的。
接下来path_to_nameidata(&next, nd);这个函数的功能一目了然,就是将next中的值赋予nd,这时候nd指向下一个路径。比如/dev/mtdchar1 ,第一进入大循环的时候,nd是根目录&/&,那么经过这一步后,nd就是“dev”了,即nd-&dentry-&d_name.name=“dev”。continue,进入第二次大循环。
如果if (!c) & &goto last_//最后一项了。this.hash的值为mtdchar1。如果nd-&flag==LOOKUP_PARENT的话,就进入lookup_parent,对了在很久很久之前,前到我边写边忘记,还得查查。sys_mknodat里的do_path_lookup,也就是第一段代码里,标志了LOOKUP_PARENT。直接就返回了,不干什么事。
这个函数这么长的一堆,最后return 0,它闲的蛋疼啊。我们再看看入口参数,对了它也在对nd这个参数进行修改。那好我们总结一下nd哪些东西变了,最最重要的变化,nd-&dentry这个不再是根目录了,它变成了路径最后一个目录了,这里具体一点,nd-&dentry-&d_name.name=“dev”,这说明dentry再也不是刚进来的那个清纯少年了。还有nd-&last,这个参数里保存了路径最后一个节点的信息。nd-&last.name=“mtdchar1”,hash=hash(mtdchar1)。nd的flag跟last_type也有了变化。总之nd已有点面目全非,快要最终目的了。
走完代码四,我们又得反过头回到代码一种去。经过代码二、三、四,已经将代码一中的nd进行了赋值。
题外话:例如这里的nd-&dentry-&d_name.name=“dev”,nd-&last.name=“mtdchar1”,hash=hash(mtdchar1)。
接下来进入dentry = lookup_create(&nd, 0);这个的是生成一个新的dentry父节点是nd-&dentry,dentry-&d_name=nd-&last。
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
struct dentry *dentry = ERR_PTR(-EEXIST);
mutex_lock_nested(&nd-&dentry-&d_inode-&i_mutex, I_MUTEX_PARENT);
if (nd-&last_type != LAST_NORM)
nd-&flags &= ~LOOKUP_PARENT;
nd-&flags |= LOOKUP_CREATE;
nd-&intent.open.flags = O_EXCL;
* Do the final lookup.
dentry = lookup_hash(nd); //这里根据nd-&last生成一个dentry挂到nd-&dentry上
if (IS_ERR(dentry))
* Special case - lookup gave negative, but... we had foo/bar/
* From the vfs_mknod() POV we just have a negative dentry -
* all is fine. Let's be bastards - you had / on the end, you've
* been asking for (non-existent) directory. -ENOENT for you.
if (!is_dir && nd-&last.name[nd-&last.len] && !dentry-&d_inode)
dput(dentry);
dentry = ERR_PTR(-ENOENT);
} 老规矩,红字搞起。
static struct dentry *lookup_hash(struct nameidata *nd)//根据last的hash去查找nd-&dentry上的节点,没有的话生成一个新的
return __lookup_hash(&nd-&last, nd-&dentry, nd);
static struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd)
//根据name查找dentry,如果没有则生成一个,挂载base下面
struct dentry *
struct inode *
inode = base-&d_
err = permission(inode, MAY_EXEC, nd);
dentry = ERR_PTR(err);
* See if the low-level filesystem might want
* to use its own hash..
if (base-&d_op && base-&d_op-&d_hash) {
err = base-&d_op-&d_hash(base, name);
dentry = ERR_PTR(err);
if (err & 0)
dentry = cached_lookup(base, name, nd); //根据name.hash在base上找dentry
if (!dentry) { //如果在base上没有的话
struct dentry *new = d_alloc(base, name); //生成一个dentry
//新dentry的d_parent为base,base-&d_subdirs中存放dentry
dentry = ERR_PTR(-ENOMEM);
dentry = inode-&i_op-&lookup(inode, new, nd);
if (!dentry)
dput(new);
上面代码里dentry是怎么产生呢,其实很一目了然了,先去根据name.hash去查找base下有没有对应的子dentry,如果没有,就生成一个新的dentry,并且它的父dentry为base。父子dentry关系,子可以通过dentry-&d_parent找到父;父可以通过dentry-&d_subdirs链表找到子。父找到子的代码,可以参考下面的代码八。遍历父节点下所有的子节点。
struct list_head *
struct dentry *
list_for_each(child,&this_dentry-&d_subdirs){
de = list_entry(child,struct dentry,d_u.d_child);
//printk(&de_name:%s\n&,de-&d_name.name);
} 好了,dentry = lookup_create(&nd, 0)也完成了,得到一个dentry,它的父节点是nd-&dentry,实例化的话,就是生成一个dentry-&d_name.name=&mtdchar1&的dentry,dentry-&d_parent-&d_name.name=“dev”。
接下来进入最后一步,因为我们的mode是S_IFCHR,所以调用error = vfs_mknod(nd.dentry-&d_inode,dentry,mode,new_decode_dev(dev));
int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
int error = may_create(dir, dentry, NULL);
if (error)
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
return -EPERM;
if (!dir-&i_op || !dir-&i_op-&mknod)
return -EPERM;
error = security_inode_mknod(dir, dentry, mode, dev);
if (error)
DQUOT_INIT(dir);
error = dir-&i_op-&mknod(dir, dentry, mode, dev); //调用相应文件系统的api
//例如调用ramfs/inode.c 中的mknod
//生成inode挂载到dentry上。
//inode-&i_sb=dir-&i_sb,inode-&i_rdev=dev
if (!error)
fsnotify_create(dir, dentry);
红色部分分析,因为当前使用的文件系统是rootfs,所以在ramfs/inode.c中找到相应的mknod:ramfs_mknod
题外话:关于如何定位dir-&i_op-&mknod,发现在这个函数与dir有关系,也就是路径,不妨假设没有dev这个路径,没有这个路径mknod的时候会报错,所以要先mkdir这个路径,因为是在根目录下mkdir的,这个i_op-&mknod肯定在根目录下。我们的根目录是roorfs,所以在ramfs/inode.c中很容易得到根目录的mknod为ramfs_mknod,再通过ramfs_get_inode得到新路径的inode-&i_op。其中i_op-&mknod为ramfs_mknod
static int
ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
struct inode * inode = ramfs_get_inode(dir-&i_sb, mode, dev); //得到一个新的inode
//sb跟inode的关系,inode-&i_sb=sb
sb-&s_inddes链表中有inode-&i_sb_list
//inode-&i_rdev=dev
int error = -ENOSPC;
if (inode) {
if (dir-&i_mode & S_ISGID) {
inode-&i_gid = dir-&i_
if (S_ISDIR(mode))
inode-&i_mode |= S_ISGID;
d_instantiate(dentry, inode); //将生成的inode挂至dentry上
dentry-&d_inode=inode
dget(dentry); /* Extra count - pin the dentry in core */
error = 0;
dir-&i_mtime = dir-&i_ctime = CURRENT_TIME;
红色部分进去ramfs_get_inode为得到一个新的inode
代码十一:
struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
struct inode * inode = new_inode(sb);
if (inode) {
inode-&i_mode =
inode-&i_uid = current-&
inode-&i_gid = current-&
inode-&i_blocks = 0;
inode-&i_mapping-&a_ops = &ramfs_
inode-&i_mapping-&backing_dev_info = &ramfs_backing_dev_
inode-&i_atime = inode-&i_mtime = inode-&i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
init_special_inode(inode, mode, dev); //挂设备号
case S_IFREG:
inode-&i_op = &ramfs_file_inode_
inode-&i_fop = &ramfs_file_
case S_IFDIR:
inode-&i_op = &ramfs_dir_inode_
inode-&i_fop = &simple_dir_
/* directory inodes start off with i_nlink == 2 (for &.& entry) */
inc_nlink(inode);
case S_IFLNK:
inode-&i_op = &page_symlink_inode_
} 因为我们的mode不是路径也不是文件,所以进入default
代码十二:
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
inode-&i_mode =
if (S_ISCHR(mode)) {
inode-&i_fop = &def_chr_
inode-&i_rdev =
} else if (S_ISBLK(mode)) {
inode-&i_fop = &def_blk_
inode-&i_rdev =
} else if (S_ISFIFO(mode))
inode-&i_fop = &def_fifo_
else if (S_ISSOCK(mode))
inode-&i_fop = &bad_sock_
printk(KERN_DEBUG &init_special_inode: bogus i_mode (%o)\n&,
} 根据上面加红部分,可以看到驱动设备号已经跟inode关联起来的。mknod的使命也完成了。
一些废话:在sys_open打开这个文件时,open = f-&f_op-&open,会根据inode-&i_rdev,去查找字符驱动的表(其实是一个数组),然后就能定位到相应的驱动设备的操作。
本人水平有限,有些不足或者错误之处还望多多指出
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:127947次
积分:2029
积分:2029
排名:第16351名
原创:81篇
转载:13篇
(1)(1)(1)(1)(2)(2)(2)(5)(4)(2)(5)(4)(2)(1)(1)(2)(4)(2)(1)(7)(14)(15)(3)(4)(2)(5)(1)2007年12月 Linux/Unix社区大版内专家分月排行榜第二
2008年11月 Linux/Unix社区大版内专家分月排行榜第二
2008年12月 Linux/Unix社区大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。}

我要回帖

更多关于 linux mknod函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信