本文以自问自答的形式解答了有关char device driver相关的四个问题。
1.如果只是使用设备属性文件, device_create的dev_t输入参数可以是0?
【1】从函数device_create的说明看应该是可以的;
* A "dev" file will be created, showing the dev_t for the device, if* the dev_t is not 0,0.
【2】从代码看:
device_create -> device_create_vargs -> device_register ->int device_add(struct device *dev) -->
if (MAJOR(dev->devt)) {
/*sysfs创建了相应的属性文件*/
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
/*该函数应该应该是创建了设备结点*/
devtmpfs_create_node(dev);
}
【3】从实验结果看:是可以的,可以不使用参数dev_t
root@android:/sys/class/albimg/abcd # ls
power
resetBP
subsystem
uevent
注意这里ls下没有dev,如果有dev_t参数则有dev
device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
* @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
*
* This function can be used by char device classes. A struct device
* will be created in sysfs, registered to the specified class.
*
* A "dev" file will be created, showing the dev_t for the device, if
* the dev_t is not 0,0.
* If a pointer to a parent struct device is passed in, the newly created
* struct device will be a child of that device in sysfs.
* The pointer to the struct device will be returned from the call.
* Any further sysfs files that might be required can be created using this
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
*
* Note: the struct class passed to this function must have previously
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
2.如何生成一个char device 设备结点?
alloc_chrdev_region(&dev_id, 0, 1, NAME);
cdev_init(&alb_context->cdev, &alb_fops);
cdev_add(&alb_context->cdev, dev_id, 1);
【1】device_create是必须的吗?
/*这里的device_create是必须的?因为它创建了设备结点?*/device_create(alb_context->alb_class, NULL,
MKDEV(MAJOR(dev_id),0),
NULL,
NAME);
device_create -> device_create_vargs -> device_register -> device_add ->
{ device_create_file(dev, &devt_attr);
devtmpfs_create_node,
}
【2】实验结果:
device_create 是必须的,且必须有参数 dev_t
.root@android:/sys/class/albimg/abcd # ls
dev
power
resetBP
subsystem
uevent
此时
/dev/下有对应的设备,且设备号和class下的一直。
【3】这是为什么哪?
udev动态生成设备结点,它建立时查询的数据就是/sys/class/xx如:root@android:/sys/class/albimg/abcd # cat dev
248:0
3.open系统调用
这里只跟踪了从系统调用到注册的open函数得到运行的过程,其中好多文件系统相关的细节都没有分析,可参考understanding linux kernel.
【1】 sys_open函数的入口
c库函数open 对应的系统调用函数应该是sys_open,但是在代码搜不到函数的定义。
从crash 文件看:
crash> dis -l sys_open
/home/wenshuai/code/3.4kernel/linux_kernel/fs/open.c: 998
0xc00ab284 <sys_open>: mov r12, sp
0xc00ab288 <sys_open+4>: push {r11, r12, lr, pc}
0xc00ab28c <sys_open+8>: sub r11, r12, #4
0xc00ab290 <sys_open+12>: mov r12, r1
0xc00ab294 <sys_open+16>: mov r3, r2
/home/wenshuai/code/3.4kernel/linux_kernel/fs/open.c: 1004
0xc00ab298 <sys_open+20>: mov r1, r0
0xc00ab29c <sys_open+24>: mov r2, r12
0xc00ab2a0 <sys_open+28>: mvn r0, #99 ; 0x63
0xc00ab2a4 <sys_open+32>: bl 0xc00ab0f8 <do_sys_open>
/home/wenshuai/code/3.4kernel/linux_kernel/fs/open.c: 1008
0xc00ab2a8 <sys_open+36>: ldm sp, {r11, sp, pc}
从这里可以看出函数的定义:
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
SYSCALL_DEFINE3可能是个宏,从而生成sys_open.
fs/open.c
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
int lookup = build_open_flags(flags, mode, &op);
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
/*fd 赋值为:allocate a file descriptor, mark it busy.*/
fd = get_unused_fd_flags(flags);
if (fd >= 0) {
/*这里又创建了一个file*/
struct file *f = do_filp_open(dfd, tmp, &op, lookup);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {/*这里open file, 且关联fd and f*/
fsnotify_open(f);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
【2】do_filp_open
fs/namei.cstruct file *do_filp_open(int dfd, const char *pathname,
const struct open_flags *op, int flags)
{
struct nameidata nd;
struct file *filp;
filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
if (unlikely(filp == ERR_PTR(-ECHILD)))
filp = path_openat(dfd, pathname, &nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
return filp;
}
函数do_filp_open调用了path_openat根据返回的值,重新调整flag,再次生成file.
static struct file *path_openat(int dfd, const char *pathname,
struct nameidata *nd, const struct open_flags *op, int flags)
{
struct file *base = NULL;
struct file *filp;
struct path path;
int error;
/* Find an unused file structure and return a pointer to it.*/
filp = get_empty_filp();
if (!filp)
return ERR_PTR(-ENFILE);
error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(error))
goto out_filp;
current->total_link_count = 0;
error = link_path_walk(pathname, nd);
if (unlikely(error))
goto out_filp;
filp = do_last(nd, &path, op, pathname);
return filp;
}
static int path_init(int dfd, const char *name, unsigned int flags,
struct nameidata *nd, struct file **fp)
{
struct inode *inode = nd->root.dentry->d_inode;
nd->path = nd->root;
nd->inode = inode;
return 0;
}
/*
* Name resolution.
* This is the basic name resolution function, turning a pathname into
* the final dentry. We expect 'base' to be positive and a directory.
*
* Returns 0 and nd will have valid dentry and mnt on success.
* Returns error and drops reference to input namei data on failure.
*/
static int link_path_walk(const char *name, struct nameidata *nd)
{
}
/*
* Handle the last step of open()
*/
static struct file *do_last(struct nameidata *nd, struct path *path,
const struct open_flags *op, const char *pathname)
{
file *filp;
filp = nameidata_to_filp(nd);
}
/**
* nameidata_to_filp - convert a nameidata to an open filp.
* @nd: pointer to nameidata
* @flags: open flags
*
* Note that this function destroys the original nameidata
*/
struct file *nameidata_to_filp(struct nameidata *nd)
{
const struct cred *cred = current_cred();
struct file *filp;
/* Pick up the filp from the open intent */
filp = nd->intent.open.file;
nd->intent.open.file = NULL;
/* Has the filesystem initialised the file for us? */
if (filp->f_path.dentry == NULL) {
path_get(&nd->path);
filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,
NULL, cred);
}
return filp;
}
【3】调用open的入口
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
f->f_op = fops_get(inode->i_fop);
if (!open && f->f_op)
open = f->f_op->open;
if (open)
open(inode, f);/*这里调用了注册的 open 函数*/
}
对设备文件而言就是.
/*
* Dummy default file-operations: the only thing this does
* is contain the open that then fills in the correct operations
* depending on the special file...
*/
const struct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
/*
* Called every time a character special file is opened
*/
static int chrdev_open(struct inode *inode, struct file *filp)
{
struct cdev *p;
struct cdev *new = NULL;
int ret = 0;
spin_lock(&cdev_lock);
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
/*什么时候建立了cdev_map, dev_t and kobject的关系*/
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
/*通过 kobject 得到cdev*/
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
spin_unlock(&cdev_lock);
cdev_put(new);
if (ret)
return ret;
ret = -ENXIO;
filp->f_op = fops_get(p->ops);
if (!filp->f_op)
goto out_cdev_put;
if (filp->f_op->open) {
ret = filp->f_op->open(inode,filp);
if (ret)
goto out_cdev_put;
}
return 0;
out_cdev_put:
cdev_put(p);
return ret;
}
4.对char设备而言,何时赋值 inode的i_fop变量为&def_chr_fops
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode, inode->i_sb->s_id,
inode->i_ino);
}
根据mode对inode赋值:
crash> whatis umode_t
typedef unsigned short umode_t;
SIZE: 2
【1】 从backtrace看init_special_inode何时被调用
在代码中加入: WARN_ON(1);可知init_special_inode被多次调用。
[<c00280c4>] (warn_slowpath_null+0x0/0x2c) from [<c00c2dc4>] (init_special_inode+0x38/0xa8)
[<c00c2d8c>] (init_special_inode+0x0/0xa8) from [<c015b6fc>] (ramfs_get_inode+0xdc/0x134)
r5:e6400690 r4:00002180
[<c015b620>] (ramfs_get_inode+0x0/0x134) from [<c015b8a4>] (ramfs_mknod+0x24/0x9c)
r7:00002180 r6:e64007d0 r5:e64007d0 r4:e6401318
[<c015b880>] (ramfs_mknod+0x0/0x9c) from [<c00b9420>] (vfs_mknod+0x98/0xd8)
r6:e64007d0 r5:e6401318 r4:00000000
[<c00b9388>] (vfs_mknod+0x0/0xd8) from [<c00b9670>] (sys_mknodat+0x15c/0x198)
r8:00000000 r7:00002000 r6:00000001 r5:e6401318 r4:00002180
[<c00b9514>] (sys_mknodat+0x0/0x198) from [<c00b96d0>] (sys_mknod+0x24/0x28)
r8:00000000 r7:c05b6adc r6:0000010e r5:c058e794 r4:00000000
[<c00b96ac>] (sys_mknod+0x0/0x28) from [<c058e7c4>] (default_rootfs+0x30/0x70)
[<c058e794>] (default_rootfs+0x0/0x70) from [<c000858c>] (do_one_initcall+0xb0/0x180)
r4:00000005 r3:e6830000
[<c00084dc>] (do_one_initcall+0x0/0x180) from [<c058d3d4>] (kernel_init+0xec/0x1dc)
[<c058d2e8>] (kernel_init+0x0/0x1dc) from [<c002bd6c>] (do_exit+0x0/0x72c)
2】
[<c00280c4>] (warn_slowpath_null+0x0/0x2c) from [<c00c2dc4>] (init_special_inode+0x38/0xa8)
[<c00c2d8c>] (init_special_inode+0x0/0xa8) from [<c008b8dc>] (shmem_get_inode+0x10c/0x180)
r5:e684a5e8 r4:00002180
[<c008b7d0>] (shmem_get_inode+0x0/0x180) from [<c008bb80>] (shmem_mknod+0x2c/0xbc)
[<c008bb54>] (shmem_mknod+0x0/0xbc) from [<c00b9420>] (vfs_mknod+0x98/0xd8)
r7:00002180 r6:e684a068 r5:e6409ad8 r4:00000000
[<c00b9388>] (vfs_mknod+0x0/0xd8) from [<c00b9670>] (sys_mknodat+0x15c/0x198)
r8:00000000 r7:00002000 r6:00000003 r5:e6409ad8 r4:00002180
[<c00b9514>] (sys_mknodat+0x0/0x198) from [<c00b96d0>] (sys_mknod+0x24/0x28)
r8:c000e0a8 r7:0000000e r6:0001da65 r5:00008c9d r4:0001e6d8
[<c00b96ac>] (sys_mknod+0x0/0x28) from [<c000df00>] (ret_fast_syscall+0x0/0x30)