第六章 高级字符驱动程序操作(一)

1.ioctl

用户空间:int ioctl(int fd, unsigned long cmd, ...);

驱动程序的ioctl原型:int (*ioctl) (struct inode *inode ,struct file *filp, unsigned int cmd, unsigned long arg);

2.ioctl命令号
2.1

命令号在系统范围内应该 唯一。

老方法:16位 高8位为与设备相关的magic number, 低8位为设备内部唯一的一个序列号码

新方法:4位段。  参考include/asm/ioctl.h 和 Documentation/ioctl-number.txt

             type           8位宽        magic number

             number     8位宽        序数

             direction   位掩码       传输方向

             size           涉及的用户数据大小,体系结构相关 

2.2构造命令号的一些宏

<linux/ioctl.h>中包含的<asm/ioctl.h>中有这些宏的定义

构造命令号的宏 :_IO(type,nr) ;  _IOR(type,nr,datatype) ; _IOW(type,nr,datatype) ; _IOWR(type,nr,datatype)

解开命令号的宏 :_IOC_DIR(nr) ;_IOC_TYPE(nr) ; _IOC_NR(nr) ; _IOC_SEZE(nr);

2.3预定义命令号

如果ioctl编号冲突,应用程序的行为将无法预测。

预定义命令分为三组:

(1)可以用于任何文件(普通、设备、FIFO和套接字)的命令

(2)只用于普通文件的命令

(3)特定于文件系统类型的命令

设备驱动只需考虑组(1):magic number 为 “T”

FIOCLEX ; FIONCLEX ; FIOASYNC ;FIOQSIZE ;FIONBIO

3.使用ioctl参数

地址验证  <asm/uacces.h>中声明

      int access_ok(int type , const void *addr , unsigned long size );

除了copy_from_usr和copy_to_usr函数外,还可以使用为最常用的数据大小(1,2,4,8字节)优化过的一组函数:

<asm/uaccess.h>中定义

put_user(datum,ptr)  ;__put_user(datum,ptr)  会根据ptr的类型,视情况 传递1,2,4,8字节

get_user(local,ptr)  ; __get_ptr(local,ptr)
4.权能(capability)与受限操作

内核专为许可管理使用权能,导出了两个系统调用capget和capset,这样就可以从用户空间来管理权能。

对驱动程序开发者来说,有意义的权能:

    CAP_DAC_OVERRIDE

    CAP_NET_ADMIN

    CAP_SYS_MODULE

    CAP_SYS_RAWIO

    CAP_SYS_ADMIN

    CAP_SYS_TTY_CONFIG

用户组权能的检查 capable函数 <sys/sched.h>中定义 int capable(int capability)

5.阻塞型I/O

5.1 休眠的几条规则

第一,不要在原子上下文中(如 拥有自旋锁、seqlock 等 ,但拥有信号量时允许)进入睡眠

第二,不能对唤醒之后的状态进行假设,休眠中一切皆有可能。

第三,确定某个地方会唤醒,否则进程不能进入休眠。

5.2等待队列

linux中,一个等待队列通过一个“等待队列头(wait queue head)”来管理 ,即wait_queue_head_t 结构体

<linux/wait.h>中定义

静态定义方法 DECLARE_WAIT_QUEUE_HEAD(name);

动态定义方法 wait_queue_head_t   my_queue;

                       init_waitqueue_head(&my_queue);

5.3简单休眠

linux内核中最简单的休眠方法是 wait_event宏,有一下几个变体:

      wait_event(queue,condition)

      wait_event_interruptible(queue,condition)

      wait_event_timeout(queue,condition,timeout)

      wait_event_interruptible_timeout(queue,condition,timeout)

  其中,queue为值传递,condition为布尔表达式。

休眠的另一个过程为唤醒。用来唤醒的基本函数是wake_up,有多种形式,其中两个如下

      void wake_up (wait_queue_head_t *queue);

      void wake_up_interruptible(wait_queue_head_t *queue);

实践中,一般配对使用 ,使用wait_event时使用wake_up ,使用wait_event_interruptible时用wake_up_interruptible

5.4

阻塞和非阻塞型操作

     显示的非阻塞I/O由filep->f_flags中的O_NONBLOCK标志决定。这个标志在<linux/fcntl.h>中定义,此头文件包含于<linux/fs.h>. 阻塞与非阻塞可看下面的 一个阻塞I/O示例 的代码

一个阻塞I/O示例 P153

5.5高级休眠

将简单休眠中的宏,函数的实习进行拆分,用一些更低层的函数来实现,需要对Linux的等待队列机制有更深入的理解。

5.5.1进程如何休眠

step1:分配并初始化一个wait_queue_t结构,然后将其加入到对应的等待队列。

step2:设置进程的状态(<linux/sched.h>中定义),将其标记为休眠。void set_current_state(int new_state);

step3:放弃处理器。这之前,我们要检查休眠等待的条件,防止引入竞态。if(!condition) schdule();

5.5.2手工休眠过程

step1:建立并初始化一个等待队列入口。

          通常 DEFINE_WAIT(my_wait);

          或者wait_queue_t my_wait;  init_wait(&my_wait);

step2:将进程的等待队列入口添加到等待队列中,并设置进程状态。

            void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);

step3:在调用prepare_to_wait之后,进程即可调用schedule,当然这之前,为避免竞态,需要判断休眠等待条件。

P158程序实例

5.5.3独占等待

避免“疯狂群兽”行为。p160

5.5.4唤醒的相关细节

当一个进程被唤醒时,实际的结果有等的队列入口中的一个函数控制。默认的唤醒函数将进程设置为可运行状态,并且如果该进程具有更高优先级,则会执行一次上下文切换程序来切换到该进程。

6.poll和select

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值