前面我们以及其简单的例子分析了linux的open系统调用过程。下面换一个例子,假如我们有个字符驱动:my_kmem.ko。使用如下脚本将其加载入内核空间:
#!/bin/sh
module="my_kmem"
device="my_chr_dev"
mod="664"
insmod $module.ko || exit 1
rm -f /dev/${device}0
major=`awk -v dev=$device '$2 == dev {print $1}' /proc/devices`
echo "major number: $major"
mknod /dev/${device}0 c $major 0
chmod $mod /dev/${device}0
驱动的init函数为:
static int my_chrdev_init(void)
{
int ret;
ret = alloc_chrdev_region(&dev, 0, 1, "my_chr_dev");
if (ret != 0) {
printk(KERN_ALERT "error allocating device number\n");
return ret;
}
cdev_init(&my_chrdev, &my_chr_dev_fops);
my_chrdev.owner = THIS_MODULE;
ret = cdev_add(&my_chrdev, dev, 1);
if (ret < 0) {
printk(KERN_ALERT "adding charactor device failed\n");
unregister_chrdev_region(dev, 1);
return ret;
}
return 0;
}
其中 `my_chr_dev_fops`是自定义的文件操作集,包括 open(),read(),write()等。我们知道,当我们通过系统调用 open() 打开驱动文件的时候,最后肯定会调用到我们自定义的文件操作集中的 open()。这里我们来分析具体的调用过程。从前面的分析过程知道,内核最终都会调用到do_dentry_open()函数,来完成文件打开的操作。而do_dentry_open()函数里面会找到inode的i_fop成员变量,该成员变量也是一个指向文件操作集的指针,其中就包括 open() 函数,而后面的操作就和具体的文件系统相关了。这里简化do_dentry_open()函数如下:
static int do_dentry_open(struct file *f,
struct inode *inode,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
...
f->f_op = fops_get(inode->i_fop);
...
if (!open)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
...
}
看来要分析整个open()系统调用的调用过程,主要是分析 inode->i_fop->open所指向的函数。这里提前说一下,这个函数指向的是 chrdev_open()函数,后面分析 mknod 加载驱动的具体过程时会详细分析为什么是这个函数。现在来分析下这个函数吧:
/*
* Called every time a character special file is opened
*/
static int chrdev_open(struct inode *inode, struct file