Linux驱动开发_ 内核中断处理、定时器、阻塞与非阻塞IO

网友投稿 969 2022-05-30

任务1: 复习: 内核中断处理(按键为例)

裸机的中断(STM32为例):

1.  中断线

2.  中断号(设置优先级、使能)

3.  中断服务函数

4.  建立中断向量表(是中断入口函数地址)

linux操作系统下中断:

1.  编写中断服务函数

2.  注册中断 request_irq

3.  注销中断 free_irq

4.  获取中断号(通过GPIO口编号进行获取)

5.  产生中断的属性: 边沿触发。

6.  中断向量表(在Linux内核不需要写,它是在UBOOT阶段建立的)

Linux系统下中断处理

1.  内核中断服务函数里不能出现会导致系统休眠的函数。比如: mdelay

2.  内核中断处理分为上文和下文(真正做事情的地方)。

3.  内核中断下文一般使用工作队列或者内核的微线程实现。

4.  内核中断上文一般就调度工作队列或者内核微线程。

注明:工作队列和内核微线程里可以使用休眠函数。

查看内核所有中断的信息: [root@tiny4412 ]#cat /proc/interrupts

任务2: 复习: 工作队列

工作队列是将任务推后执行的一种机制,什么时候执行? CPU空闲的时候执行。

工作: 就是一个结构体,结构体里保存了工作对应的函数(函数指针)。

队列: 是一个链表形式,每一个链表的节点就是一个工作结构体。

这个队列由内核的一个线程轮询执行,当内核执行了工作之后,该工作结构体就会从链表节点里删除掉。

注意事项: 如果之前加入的工作已经在工作队列里,还没有被执行,这个时候不能再重复添加。

在Linux内核里有默认的工作队列: 共享工作队列。 也可以自己去创建自己的队列。

练习:

1.  将上课代码全部写一遍: 作业、中断、工作队列

2.  试着使用内核定时器给按键进行消抖。

任务3: 内核定时器

内核定时器特性:

1.  基于软件层的定时器,定时器的定时器不是特别精准。

2.  内核定时器的使用: 注册和注销。

3.  内核定时器是可以无限注册,内部是通过链表实现---->队列。

链表的每一个节点就是一个定时任务,这个节点本身就是一个定时任务结构体,在结构体里就有一个函数指针。

4.  定时器是基于 jiffies(节拍总数) 来实现定时。

计算一个节拍是多少时间? jiffies 1秒钟(1000ms)增加HZ(200)次 , jiffies+1的时间:5m

int cnt=0;

void timer(void) //硬件中断

{

cnt++; //5ms一次 1000000

}

void main(void)

{

int time=cnt+200;

while(cnt==time){}

printf("1234");

}

s64 ktime_to_us(const ktime_t kt); //转换为 us 单位

s64 ktime_to_ms(const ktime_t kt); //转换为 ms 单位

my_time=ktime_get(); //获取当前时间

红外线编码驱动、超声波测距之类的模块。

任务4: 等待队列

等待队列: 在驱动里阻塞进程的一种方式,可以将进程加入到等待队列里休眠。

使用等待队列:

1.  定义一个等待队列头(静态)。

2.  在指定位置将进程休眠(挂起)。

3.  在指定位置将进程唤醒。

等待队列休眠的原理:

任务5: 阻塞与非阻塞IO

在应用可以通过哪些方法(系统调用)得知驱动层资源已经获取成功?

有这3个函数接口: epoll、poll、select -----多路复用IO解决方法。

案例: 网络编程里可以通过以上函数得知是否收到数据。

5.1 Poll函数学习

应用层的函数接口:

#include

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {

int fd; /*文件描述符 */

short events; /*监控的事件 */

short revents; /*驱动层返回的事件 */

};

函数参数:

struct pollfd *fds :保存我们将要监控的文件描述符所有信息。

Linux驱动开发_ 内核中断处理、定时器、阻塞与非阻塞IO

nfds_t nfds :监控的数量(有多少个文件描述符)。

int timeout : >0表示休眠的正常时间单位。=0表示不休眠。<0表示永久休眠。

说明: 时间单位是ms

返回值: 表示产生事件文件描述符数量。

说明: epoll、poll、select 这3个函数共用一个poll驱动。

static unsigned int tiny4412_poll(struct file *file, struct poll_table_struct *poll_table)

{

unsigned int mask = 0; /*返回值: 掩码*/

poll_wait(file,&key_wait,poll_table);

if(tiny4412_key_val)mask=POLLIN;

return mask;

}

5.2 异步通知

如何通知? 发送信号,发送信号需要进程PID号。

应用层如何接收或者发送信号?

1.  C语言代码里可以通过signal函数来捕获信号:

#include

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数:

int signum: 将要捕获信号。

sighandler_t handler: 处理信号的方法(函数指针)。

2.  如何发送信号?

#include

#include

int kill(pid_t pid, int sig);

也可以通过命令:kill [-s signal|-p] [--] pid...

用法1: kill -s <信号名称>

用法2: kill -<信号名称>

3.  有哪些信号发送呢?

[root@wbyq key_interrupt_drv]# kill -l

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP

6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1

11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3

38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13

48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12

53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7

58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

63) SIGRTMAX-1 64) SIGRTMAX

让驱动层代码给进程发送信号需要哪些条件?

1.  驱动层必须得支持异步通知的功能。

2.  驱动层得知道进程的PID号。

驱动层怎么给进程发送信号?

设置文件的状态信息:

#include

#include

int fcntl(int fd, int cmd, ... /* arg */ );

练习:

1.  将等待队列、poll函数驱动、异步IO驱动写一次。

2.  poll函数支持多路复用: 将按键驱动拆分成4个驱动,在应用层使用一个poll函数同时监控4个按键驱动,并读出按键值。

Linux 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:经典神经网络 | Faster R-CNN论文解析
下一篇:【愚公系列】2022年01月 Java教学课程 65-网络编程-概念
相关文章