怎么学linux内核驱动

怎么学linux内核驱动,第1张

怎么学linux内核驱动?1. 分享Linux内核学习和驱动开发的经验。

内核学习

Linux 内核功能越来越完善,如果没有充裕的时间,深入内核并不是很现实。所以建议先读一本内核的书,

第一遍是读,会读的很迷糊;之后反省一下,然后再浏览一下;可以想象一个 OS 是如何运行的,这样可以不

陷入 Linux 内核的细节;最后可以深入自己感兴趣或者需要的那一子系统

推荐 《Linux Kernel Development》

即便是子系统,也是很庞大的。一个省力的方式是网上搜一些相关的文章,便于快速了解这个子系统的运作;

然后结合代码,形成自己的认知,最后做一下总结。如果仅仅是快速了解某一子系统的运作,可以参考一些早期

代码的注解书籍,再深入的时候看看最新的代码实现

对内核的认知是一个反复的过程,一开始并不完善,可能需要反复纠正。不要陷入这种纠错中;而是以后继续

使用和学习过程中,发现了没有弄清楚的地方再深入,毕竟 Linux 内核是不断变化的

还有一个很好的方式是,从系统调用入手,现在这方面的数据不少,而且对系统调用的语义都有讲解,这样可以

间接了解 Linux 系统的一些概念。对系统调用熟悉了,可以根据系统调用的执行过程,来大体了解内核的一个

运作过程;但是跟踪系统调用的时候要注意抓主线,现在内核系统很复杂,一些 code path 上可能会涉及多个

子系统,可以从名字上猜测它们是干什么的,不需要深入,否则会发现精力完全被分散掉了

学习 Linux 内核,一个很重要的是抽象的能力,所谓的抽象这里仅仅是指分清接口和接口的实现。因为 Linux

内核子系统很多,有很多子系统相互渗透,这样 code path 看上去很复杂。阅读代码的时候,为了排除干扰,

需要分清哪些是自己需要看的,哪些是其它子系统的接口,对于其它子系统的接口,先当作它们功能完善不会

出问题好了,这样可以关注重点;打个比方,一个应用程序的代码可能量很大,比如一个 apache 项目,它

包含很多组件,有时候阅读代码的时候会看到不同组件的 API,深入看相关组件实现并不现实,这时候分清主次

对于代码的阅读就很有帮助了,总不能看到了 malloc 就要先把它的实现弄清楚吧,系统调用多者呢

1.首先要了解为什么要学习内核?下图已表明,如果要从事驱动开发或系统研究,就要学习内核。

2.内核的知识就像下面的绳结一样,一环扣一环,我们要解开它们,就必须要先找到线头也就是内核中的函数接口。初学阶段,我们一般不深入的研究内核代码,会使用内核的接口函数就不错了。

3.下面提供了如何学习这些内核函数的方法,就像解绳子一样

4.学习内核的四步法则,思维导图的设计尤为重要,这也是能否学习好内核的关键

5.语言基础也需要扎实,所以需要把C语言巩固巩固

介绍个动态加载模块的过程

在该驱动中,我们假设对键盘的获取是以0.2s为周期执行。源代码如下

static struct timer_list timer///////我们定义的定时器,也许你会问timer_list是什么来的,其实一看名称就应该就知道了,而为什么要用到list那么多定时器呢?其实在linux中还有很多相同的定义,比如说信号,我们定义的也是信号集,你可以定义该list是一个元素的,也可以是多个的。所以对于 timer_list就可以这样描述:在未来某一个特定时刻执行某一系列特定任务的功能。下面我们还会给出内核中timer_list的具体描述,^_^ 好像我的话又说多了

static int Keypad_starttimer(void)

{

init_timer(&timer)//初始化定时器结构

timer.function=Keypad_timer//超时服务程序

timer.expires=jiffies+20//当前时刻加0.2s

add_timer(&timer)

return 0

}

///超时服务程序

static void Keypad_timer(unsigned long data)

{

read_xy()

}

/////////接下来说下timer-list这个数据结构,如果你不感兴趣的话可以跳过,该结构在include\linux\timer.h中定义

struct timer_list

{

struct list_head entry

unsigned long expries

spinlock_t lock

unsigned long magic

void (*function)(unsigned long)

unsigner long data

struct tvec_t_base_s *base

}

七.利用等待队列实现阻塞型I\O

在用户程序执行读 *** 作的时候有可能尚且没有数据可以读取,为此需要让read *** 作等待,直到有数据可以读取,这就是阻塞型i\o,阻塞型io可以通过使用进程休眠方法实现。在无数据可以读取的时候,采用等待队列让进程休眠,直到有数据到达的时候才唤醒进程完成数据的读 *** 作。

在本驱动中的read,若循环队列缓冲区中没有数据,则进程进入休眠态,定时器函数每隔0.2s读取键值一次,将按键状态放入缓冲并且适时唤醒进程读取数据。

等待队列的使用流程如下:

1.声明一个等待队列

2.把当前进程加入到等待队列中

3.把进程的状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE

4.调用schedule,以让出cpu

5.检测所需要的资源是否可用,若是,把当前进程从等待队列中删除,否则转3循环

接下来我们在对read中有关等待队列阻塞实现做具体的解释

static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)

{

DECLEARE_WAITQUEUE(wait,current)//声明等待队列,将当前进程加入到等待队列中

KEY_EVENT t

ulong out_buf[2]

if(head==tail)//当前循环队列中没有数据可以读取

{

if(filp->f_flags &O_NONBLOCK)//假如用户采用的是非堵塞方式读取

return _EAGAIN

add_wait_queue(&queue,&wait)//将当前进程加入等待队列

current->state=TASK_INTERRUPTIBLE//设置当前进程的状态

while((head==tail)&&!signal_pending(current))//假若还没有数据到循环队列并且当前进程没有受到信号(该类信号具体来说是未决的休眠)

{

shedule()//进程调度

current->state=TASK_INTERRUPTIBLE

}

current->state=TASK_RUNNING//该进程恢复执行

remove_wait_queue(&queue,&wait)//移出等待队列

if(head==tail)

return count

t=get_data()//调用get_data()函数,得到缓冲区中的数据,下面将给予详细的 介绍

out_buf[0]=t.status

out_buf[1]=t.click

copy_to_user(buf,&out_buf,sizeof(out_buf))//将得到的键值拷贝到用户数据区

return count

}

}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8984539.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-23
下一篇 2023-04-23

发表评论

登录后才能评论

评论列表(0条)

保存