字符型设备学习笔记

来源:互联网 发布:西门子840d编程指令 编辑:程序博客网 时间:2024/05/02 18:13


应用程序在使用文件接口进行操作时,首先触发一个系统调用进入内核态,VFS层寻找具体的设备进而调用设备的相应处理函数。



图1  字符型设备层次结构



当新建一个字符型设备时,通常需要提供文件操作函数表(即常见的open,release,read,write)等等。同时需要提供一个设备号,设备号包括主设备号和从设备号。

二者都要记录到一个叫cdev的结构体,最终在注册改设备的时候,将这个结构体保存在cdev_map表中。


图2 字符型设备表



用户在使用内核注册的字符型设备前,需要创建一个字符型设备文件。这个设备通常放在/dev/目录中,当然发放在其它位置也可以工作。

mknod命令可以用来创建一个字符型设备文件。创建核心点包括两个:一是根据文件系统策略创建一个inode节点,二是将设备号填入这个inode相应字段。

mknod创建字符型设备流程
1. mknod /dev/simple_cdev c 250 0
2. sys_mknodat
3. vfs_mknod
4. shmem_mknod
5. shmem_get_inode
6. init_special_inode
注意:4、5可能因不同文件系统而是用的函数不同,但最终都会到6
在调用init_special_inode时,前期的函数已经在相应目录下创建了一个inode节点,
此时需要做的是:
1) 在inode节点中记录设备号inode->i_rdev = rdev
2) 在inode节点中记录字符型设备默认open操作函数,    
   所有的字符设备在被open时首先调用这个默认的chrdev_open函数,之后再调用设备自己的open函数

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;
    }
    ......
}

const struct file_operations def_chr_fops = {
    .open = chrdev_open,
    .llseek = noop_llseek,
};


注册一个字符型设备
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    ......
    error = kobj_map(cdev_map, dev, count, NULL,
             exact_match, exact_lock, p);
    ......
}


图3 创建字符型设备文件


在使用字符型设备的时候,是借助对应的字符型设备文件完成的。

应用程序首先要open字符型设备。

字符型设备OPEN流程

1. open     ---- libc
2. sys_open ---- syscall
3. do_sys_open
4. do_filp_open
5. path_openat
6. do_last
7. finish_open
8. do_dentry_open
9. chrdev_open ---- 位于def_chr_fops中定义
   9.1 根据inode节点记录的设备号(i_rdev)从字符设备列表(cdev_map)查找设备。
   9.2 如果找不到,则返回
   9.3 如果找到,则完成两个主要任务:
       9.3.1 将filp指向该设备的fops函数列表,filp是文件系统维护的一个结构,很多文件操作都要基于它
       9.3.2 inode节点成员i_cdev指向该设备结构体


之后可进行读写等操作

字符型设备READ流程
1. read      ---- libc
2. sys_read  ---- syscall
3. vfs_read
4. filp->f_op->read 也就是 cdev->ops->read

字符型设备WRITE/CLOSE/...流程与READ类似。



图4 字符型设备的数据结构关系图







----------------------------  测试代码 ---------------------------- 
/*
 * a simple char device driver
 *
 * Copyright (C) 2014 PAN Guolin guolinp@gmail.com
 *
 * Licensed under GPLv2 or later.
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#define SIMPLE_CDEV_MAJOR 250

static struct cdev cdev_obj;
static unsigned int data;

static int simple_cdev_open(struct inode *inode, struct file *filp)
{
    data = 0;
    printk(KERN_INFO "Open simple char device, data=%d\n", data);
    return 0;
}

static int simple_cdev_release(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "Release simple char device\n");
    return 0;
}

static ssize_t simple_cdev_read(struct file *filp, char __user * buf, size_t size, loff_t * ppos)
{
    int count = sizeof(data);

    if (copy_to_user(buf, (char *)&data, count))
        return -EFAULT;
    else
        printk(KERN_INFO "Read %u bytes(s)\n", count);

    return count;
}

static ssize_t simple_cdev_write(struct file *filp, const char __user * buf, size_t size, loff_t * ppos)
{
    int count = sizeof(data);

    if (copy_from_user((char *)&data, buf, count))
        return -EFAULT;
    else
        printk(KERN_INFO "Written %u bytes(s)\n", count);

    return size;
}

static const struct file_operations simple_cdev_fops = {
    .owner = THIS_MODULE,
    .read = simple_cdev_read,
    .write = simple_cdev_write,
    .open = simple_cdev_open,
    .release = simple_cdev_release,
};

static int __init simple_cdev_init(void)
{
    int ret;
    dev_t devno = MKDEV(SIMPLE_CDEV_MAJOR, 0);

    ret = register_chrdev_region(devno, 1, "simple_cdev");
    if (ret < 0) {
        printk(KERN_INFO "Error %d register_chrdev_region %d", ret, devno);
        return ret;
    }

    cdev_init(&cdev_obj, &simple_cdev_fops);
    cdev_obj.owner = THIS_MODULE;

    ret = cdev_add(&cdev_obj, devno, 1);
    if (ret)
        printk(KERN_INFO "Error %d adding simple cdev%d", ret, devno);

    return ret;
}

module_init(simple_cdev_init);

static void __exit simple_cdev_exit(void)
{
    cdev_del(&cdev_obj);
    unregister_chrdev_region(MKDEV(SIMPLE_CDEV_MAJOR, 0), 1);
}

module_exit(simple_cdev_exit);

MODULE_AUTHOR("PAN Guolin <guolinp@gmail.com>");
MODULE_LICENSE("GPL v2");

# mknod /dev/simple_cdev c 250 0

# echo hello > /dev/simple_cdev
Open simple char device, data=0
Written 4 bytes(s)
Release simple char device

# cat /dev/simple_cdev
Open simple char device, data=0
Read 4 bytes(s)
...
Release simple char device




0 0
原创粉丝点击