linux字符设备

字符设备的定义

linux下有三种设备:字符设备、块设备、网络设备等等。它们均以一个文件节点形式显示在文件系统的/dev目录下(crw--w---- 1 root tty 4, 0 7月 11 09:11 tty0 其中c代表字符设备类型)。

字符设备是指设备无需缓冲即可直接进行读写的设备, 如鼠标,键盘,串口设备等, 它与块设备的区别在于是字符操作的基本单位是字节。下面为字符设备结构示意图:

img

字符设备的性质及特点
  • 字符设备属于设备文件系统的一种, 相当于底层硬件向上层提供的逻辑设备文件, 宛如将一个数据端口(数据寄存器)与一个文件对接起来,设备驱动程序直接对文件操作, 于是便直接对端口进行了读写操作。 同样作为文件, 字符设备驱动也必须实现文件的基本的操作open(),close(),write(),read()等,当然终端重定向操作也是支持的。

  • 字符设备文件文件的读写是以单个字节为单位的, 不需要设立硬件缓冲区。 设备像访问字节流一样被操作系统访问。 字节流就像在硬件端口和文件系统搭建起了一个传送管道, 字节逐个通过管道传输并呈现给读写双方。 这个流特性在驱动程序中是以缓冲队列来实现的。例如: 控制台的结构体中的读写缓冲队列

    struct tty_struct {
    struct termios termios;
    int pgrp;
    int stopped;
    void (*write)(struct tty_struct * tty);
    struct tty_queue read_q;               //读队列
    struct tty_queue write_q;              //写队列
    struct tty_queue secondary;            //tty辅助队列(存放规格化后的字符)
    };
    
  • 字符设备由字符设备号标识。字符设备号由主设备号和次设备号构成, 例如/dev/ttyS0的设备号为(4,64); 主设备号标识设备对应驱动程序, 内核通过主设备号将设备和驱动程序一一对应起来, 次设备号由驱动程序使用, 用于驱动程序内部区分设备细节差别使用的代码,内核其他部分不使用它。

字符设备的分类

字符设备主要包括控制终端设备和串行终端设备, 例如控制台和键盘。依据功能和硬件上的差别, 字符终端设备有如下分类:

  • 串行端口终端(/dev/ttSn):使用计算机串行端口连接的终端设备, 串行设备数据传输方式为同一字符8个bit单线传输, 在命令行输入 echo 'hello world' > /dev/ttyS0可将输入写入到对应设备。
  • 伪终端(/dev/ttyp,/dev/ptyp): 对应底层不存在真实的硬件设备, 用于为其他程序提供终端式样的接口,如网络登陆主机时网络服务器和shell程序之间的终端接口。
  • 控制终端(/dev/tty):主设备号为5, 进程控制终端,与进程相关联,如登陆shell进程使用的就是终端/dev/tty
  • 控制台(/dev/ttyn,/dev/consol): 计算机输入输出的显示器,当控制台登陆时, 使用的就是tty1, 而ubuntu 图形界面使用的tty7。
  • 其他类型:现行的linux针对许多不同的设备建有许多其他种类的设备特殊文件,如ISIDIN设备的/dev/ttyIn设备。

具体分类可参看http://www.cnblogs.com/yxmx/articles/1612627.html

字符设备与驱动程序的关系

字符设备与驱动程序的处在不同的层次上, 驱动程序实现了对硬件的控制逻辑, 并将其以统一接口的形式呈现给上层用户。

这样的话, 字符设备驱动程序就是硬件和文件组织系统间的接口, 它或解释底层硬件中断, 翻译并转交给用户进程, 或将用户进程发出的信号传输给硬件, 触发硬件中断。

img

例如,当用户在键盘上键入了一个字符时,会引起键盘中断响应(中断请求信号 IRQ1, 对应中断号INT 33 ),此时键盘中断处理程序就会从键盘控制器读入对应的键盘扫描码,然后根据使用的键盘扫描码映射表译成相应字符,放入 tty 读队列 read_q 中。然后调用中断处理程序的 C 函数 do_tty_interrupt(),并调用写控制台函数 con_write()。此时如果该终端的回显( echo )属性是设置的,则该字符会显示到屏幕上。

并且, 进程对字符设备的读写通过文件系统实现的文件读写操作从用户缓冲区读取一个字符写入写队列, 若写队列已满, 则将写队列输出到控制台显示出来。
整个操作过程见图 7-3 所示 : img

<think>好的,我需要回答用户关于Linux字符设备驱动程序的问题。首先,用户提供了三个引用内容,我需要仔细理解这些内容,并结合自己的知识来组织回答。 首先,引用[1]提到了字符设备驱动程序和用户程序的交互,并给出了一个简单的例子,输出“Hello kernel!”。这说明字符设备驱动的基本功能是实现用户空间与内核空间的通信。我需要强调字符设备是逐字节传输的,如键盘、鼠标等,并解释主次设备号的作用。 引用[2]详细介绍了字符设备驱动的开发步骤,包括设备号的注册、file_operations结构体、设备的分配和注册(如class_create、cdev_add、device_create)。这部分需要分步骤说明驱动程序的开发流程,可能还需要提到关键的数据结构和函数。 引用[3]指出Linux源码中字符设备驱动存放在driver/char目录,用户可以在此添加自己的驱动。这部分需要说明代码存放的位置,并可能给出一个简单的示例代码结构,帮助用户理解如何开始编写驱动。 接下来,我需要按照用户的要求,结构清晰,分步骤回答。系统级指令要求使用中文,数学表达式用$...$,独立公式用$$...$$,但这里可能涉及较少数学内容,但如果有变量或代码,需要正确格式化。例如,设备号的主次部分可以用主设备号$major$和次设备号$minor$表示。 然后,生成相关问题。根据用户提供的示例,问题应该围绕字符设备驱动的开发步骤、关键结构、设备号管理、以及实际应用场景展开。 需要检查是否有遗漏的要点,比如主次设备号的注册方法(静态和动态),file_operations的作用,以及如何创建设备节点等。同时,要确保引用标记正确,比如在提到设备号注册时引用[2],在代码存放位置时引用[3]。 可能需要注意用户可能对如何编写一个简单的字符设备驱动感兴趣,所以给出一个代码示例的框架会更好,如引用[1]中的示例,但需要确认是否在回答中需要包含代码。用户提供的示例问题中有代码块,所以可以适当加入代码示例,但需要符合引用内容中的例子。 最后,确保回答真实可靠,参考了提供的引用内容,并且结构清晰,分点说明。同时,生成的相关问题要围绕主题,帮助用户深入理解。</think>### Linux字符设备驱动程序介绍 Linux字符设备驱动程序是内核模块,负责管理以**字节流**形式传输数据的硬件设备(如键盘、鼠标、串口等)。其核心功能包括设备号管理、文件操作接口实现以及用户空间与内核空间的交互。 #### 1. 设备号与设备文件 - **主设备号**和**次设备号**: 每个字符设备通过唯一的设备号标识,格式为$major:minor$,其中$major$标识设备类型,$minor$区分同类设备的不同实例。 设备号可通过静态分配(如`register_chrdev_region`)或动态注册(如`alloc_chrdev_region`)获取[^2]。 #### 2. 文件操作接口 - **struct file_operations**: 该结构体定义了设备文件的操作函数指针,如`open`、`read`、`write`、`release`等。例如: ```c static struct file_operations fops = { .owner = THIS_MODULE, .read = device_read, .write = device_write, .open = device_open, .release = device_release }; ``` 用户程序调用`read()`时,内核通过该结构体找到对应的驱动函数`device_read()`[^2]。 #### 3. 驱动注册与设备节点 - **注册流程**: 1. **分配设备号**:动态或静态分配。 2. **关联操作接口**:通过`cdev_init(&cdev, &fops)`绑定`file_operations`。 3. **添加字符设备**:使用`cdev_add(&cdev, devno, 1)`注册到内核。 4. **创建设备节点**:借助`class_create()`和`device_create()`在`/dev`目录下生成设备文件[^2][^3]。 #### 4. 示例代码框架 字符设备驱动通常位于内核源码的`drivers/char/`目录[^3]。以下是一个简化版驱动程序: ```c #include <linux/module.h> #include <linux/fs.h> static int major; static struct class *drv_class; static int driver_open(struct inode *inode, struct file *file) { printk("Device opened\n"); return 0; } static struct file_operations fops = { .open = driver_open, }; static int __init init_driver(void) { major = register_chrdev(0, "my_device", &fops); //动态分配主设备号 drv_class = class_create(THIS_MODULE, "my_class"); device_create(drv_class, NULL, MKDEV(major, 0), NULL, "mydev"); return 0; } static void __exit exit_driver(void) { device_destroy(drv_class, MKDEV(major, 0)); class_destroy(drv_class); unregister_chrdev(major, "my_device"); } module_init(init_driver); module_exit(exit_driver); ``` 编译加载后,用户程序可通过`/dev/mydev`与驱动交互[^1]。 #### 5. 关键应用场景 - 实现简单硬件控制(如LED、按键)。 - 提供定制化数据传输接口(如传感器数据读取)。 - 虚拟设备开发(如内存模拟设备)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值