char 设备驱动程序(一)

本文以自问自答的形式解答了有关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.c
struct 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被多次调用。

1】
[<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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值