最全Linux驱动开发八股文(十)

你好,我是拉依达。

这是我的Linux驱动开发八股文详细解析系列

本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。

全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料

现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。

【下面是拉依达推荐学习相关专栏:】
一、Linux驱动学习专栏:拉依达的Linux驱动八股文 - 牛客网
二、Linux应用学习专栏:拉依达的Linux应用八股文 - 牛客网
【我的嵌入式学习和校招经验】 拉依达的嵌入式学习和秋招经验-CSDN博客
嵌入式学习规划/就业经验指导,可私信咨询

———————————————————————————————————————————————————

十一、设备控制接口(ioctl)

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。有些命令是实在找不到对应的操作函数, 拓展一些file_operations给出的接口中没有的自定义功能,则需要使用到ioctl()函数。一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。

alt

11.1 应用层

需要规定一些命令码,这些命令码在应用程序和驱动程序中需要保持一致。应用程序只需向驱动程序下发一条指令码,用来通知它执行哪条命令。

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, (...)arg);
  • fd:文件描述符

  • request:命令码,应用程序通过下发命令码来控制驱动程序完成对应操作。

  • (...)arg:可变参数arg,一些情况下应用程序需要向驱动程序传参,参数就通过arg来传递。只能传递一个参数,但内核不会检查这个参数的类型。那么就有两种传参方式:只传一个整数,传递一个指针

  • 返回值:如果ioctl执行成功,它的返回值就是驱动程序中ioctl接口给的返回值,驱动程序可以通过返回值向用户程序传参。但驱动程序最好返回一个非负数,因为用户程序中的ioctl运行失败时一定会返回-1并设置全局变量errorno。

11.2 驱动层

驱动程序的ioctl函数体中,实现了一个switch-case结构,每一个case对应一个命令码,case内部是驱动程序实现该命令的相关操作。

#include <linux/ioctl.h>
long (*unlocked_ioctl) (struct file * fp, unsigned int request, unsigned long args);
long (*compat_ioctl) (struct file * fp, unsigned int request, unsigned long args);
  • inode和fp用来确定被操作的设备

  • request就是用户程序下发的命令

  • args就是用户程序在必要时传递的参数

  • 返回值:可以在函数体中随意定义返回值,这个返回值也会被直接返回到用户程序中。通常使用非负数表示正确的返回,而返回一个负数系统会判定为ioctl调用失败。

  • unlocked_ioctl在无大内核锁(BKL)的情况下调用。64位用户程序运行在64位的kernel,或32位的用户程序运行在32位的kernel上,都是调用unlocked_ioctl函数。

  • compat_ioctl是64位系统提供32位ioctl的兼容方法,也在无大内核锁的情况下调用。即如果是32位的用户程序调用64位的kernel,则会调用compat_ioctl。如果驱动程序没有实现compat_ioctl,则用户程序在执行ioctl时会返回错误Not a typewriter。

  • 在字符设备驱动开发中,一般情况下只要实现 unlocked_ioctl 函数即可,因为在 vfs 层的代码是直接调用 unlocked_ioctl 函数

12.2 ioctr应用和驱动的协议

ioctl函数的第二个参数 cmd 为用户与驱动的协议理论上可以为任意 int 型数据,,但是为了确保该协议的唯一性,ioctl 命令应该使用更科学严谨的方法赋值,在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段,如下图所示:

alt

  • dir(direction):ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据

  • type(device type):设备类型,占据 8 bit,也称为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识

  • nr(number):命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增

  • size:与体系结构相关,ARM下占14bit(_IOC_SIZEBITS),如果数据是int,内核给这个赋的值就是sizeof(int)。

在内核中,提供了宏接口以生成上述格式的 ioctl 命令

#include/uapi/asm-generic/ioctl.h
 
#define _IOC(dir,type,nr,size) \
    (((dir)  << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr)   << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

宏 _IOC() 衍生的接口来直接定义 ioctl 命令

#include/uapi/asm-generic/ioctl.h
 
/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
  • _IO(type, nr):用来定义不带参数的ioctl命令

  • _IOR(type,nr,size):用来定义用户程序向驱动程序写参数的ioctl命令

  • _IOW(type,nr,size):用来定义用户程序从驱动程序读参数的ioctl命令

  • _IOWR(type,nr,size):用来定义带读写参数的驱动命令

内核还提供了反向解析 ioctl 命令的宏接口:

// include/uapi/asm-generic/ioctl.h
/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
  • _IOC_DIR(nr) :提取方向

  • _IOC_TYPE(nr) :提取幻数

  • _IOC_NR(nr) :提取序数

  • _IOC_SIZE(nr) :提取数据大小

十二、中断机制

中断是指 CPU 在执行程序的过程中, 出现了某些突发事件急待处理, CPU 必须暂停当前程序的执行转去处理突发事件, 处理完毕后又返回原程序被中断的位置继续执行。

12.1 中断API函数

  1. 获取中断号函数 每个中断都有一个中断号,通过中断号即可区分不同的中断。在 Linux 内核中使用一个 int 变量表示中断号

    或者中断号, 中断信息一般写到了设备树里面, 可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号。

    unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
    
    • dev: 设备节点
    • index:索引号, interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
    • 返回值:中断号

    使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号

    int gpio_to_irq(unsigned int gpio)
    
    • gpio: 要获取的 GPIO 编号
    • 返回值: GPIO 对应的中断号
  2. 申请中断函数 Linux 内核中要想使用某个中断是需要申请的, request_irq 函数用于申请中断, request_irq函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。 request_irq 函数会激活(使能)中断,所以不需要手动去使能中断

    int request_irq(unsigned int irq,
                    irq_handler_t handler,
                    unsigned long flags,
                    const char *name,
                    void *dev)
    
  • irq:要申请中断的中断号

  • handler:中断处理函数,当中断发生会执行此中断处理函数

  • flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志

  • name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字

  • dev: 如果将 flags 设置为 IRQF_SHAREDdev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。

  • 返回值: 0 中断申请成功,负值中断申请失败,如果返回-EBUSY 表示中断已经被申请了

    中断标志

    alt

  1. 中断释放函数 中断使用完成以后就要通过 free_irq 函数释放掉相应的中断如果中断不是共享的,free_irq 会删除中断处理函数并且禁止中断

    void free_irq(unsigned int irq,void *dev)
    
    • irq: 要释放的中断号
    • dev:如果中断设置为共享(IRQF_SHARED),此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉
  2. 中断处理函数

    使用 request_irq 函数申请中断的时候需要设置中断处理函数

    irqreturn_t (*irq_handler_t) (int, void *)
    
    • 第一个参数:要中断处理函数要相应的中断号

    • 第二个参数:一个指向 void 的指针,是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构

    • 返回值:irqreturn_t 类型

      irqreturn_t 类型定义如下所示:

      enum irqreturn {
          IRQ_NONE = (0 << 0),
          IRQ_HANDLED = (1 << 0),
          IRQ_WAKE_THREAD = (1 << 1),
      };
      typedef enum irqreturn irqreturn_t;
      

      irqreturn_t 是个枚举类型, 一共有三种返回值。 一般中断服务函数返回值使用如下形式

      return IRQ_RETVAL(IRQ_HANDLED)
      
  3. 中断使能和禁止函数

    enable_irq 和 disable_irq 用于使能和禁止指定的中断。

    void enable_irq(unsigned int irq)
    void disable_irq(unsigned int irq)
    void disable_irq_nosync(unsigned int irq)
    
    • irq:要禁止的中断号

    • disable_irq 函数要等到当前正在执行的中断处理函数执行完才返回, 因此需要保证不会产生新的中断, 并且确保所有已经开始执行的中断处理程序已经全部退出。

    • disable_irq_nosync 函数调用以后立即返回, 不会等待当前中断处理程序执行完毕。

    使能/关闭全局中断

    local_irq_enable()
    local_irq_disable()
    
    • local_irq_enable 用于使能当前处理器中断系统,
    • local_irq_disable 用于禁止当前处理器中断系统。
    • 一般不能直接简单粗暴的通过这两个函数来打开或者关闭全局中断,这样会使系统崩溃。

    在打开或者关闭全局中断时,要考虑到别的任务的感受,要保存中断状态,处理完后要将中断状态恢复到以前的状态

    local_irq_save(flags)
    local_irq_restore(flags)
    
    • local_irq_save 函数用于禁止中断,并且将中断状态保存在 flags 中。
    • local_irq_restore 用于恢复中断,将中断到 flags 状态。
#嵌入式##校招##八股文##Linux##linux驱动#
拉依达的Linux驱动八股文 文章被收录于专栏

你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。

全部评论

相关推荐

腾讯-暑期实习-推荐算法-初试(已offer)暑期实习的面经现在才有空发出来哈哈哈个人背景:双985Timeline:2.25投递,3.4通知初试,3.5初试,3.8复试,3.10&nbsp;HR面,3.12&nbsp;HR电话沟通offer,3.15&nbsp;正式offer邮件面试部门:PCG腾讯会议面了一个半小时,过程如下:1.先简单介绍一下自己。2.挑一个你觉得最能体现你的能力的项目经历展开讲讲。我挑了我正在投稿的论文来讲。然后面试官让我先介绍一下研究任务的背景。因为面试官对我做的任务不了解,所以我几乎是边讲边给他解释一些生疏的概念(在这种时候怎么简短有效地向别人解释新概念就很体现个人表达能力了)。之后就是深挖项目,问的很细,处理的数据集是什么格式,模型输入是什么,样本是什么,模型怎么训练的,full-batch还是mini-batch,有监督还是无监督,数据集太大为什么用&nbsp;CPU&nbsp;训练不用&nbsp;GPU,怎么优化等等(氪金,买&nbsp;v100&nbsp;卡、mini-batch、分布式多卡训练)。然后问我&nbsp;F1-macro&nbsp;指标怎么计算的(F1是precision&nbsp;和&nbsp;recall&nbsp;的调和平均,F1-macro&nbsp;和&nbsp;F1-micro&nbsp;求平均的计算方式略有不同)。我看你的&nbsp;AUC&nbsp;指标挺高的,你觉得这样的性能提升幅度算大吗(AUC&nbsp;的提升幅度比较小,一点点的提升都是突破)?AUC&nbsp;指标的数值意义是什么,不用库函数的话具体计算公式是什么(具体计算方式我只记得一个大概的要做排序什么的了,面试官说基本上是这样)。3.对推荐系统感兴趣吗?了解推荐算法吗?(因为我的简历里有写了解具体的推荐算法),自己挑一个算法展开讲讲(我挑了&nbsp;YouTubeDNN&nbsp;进行介绍)。然后问&nbsp;YouTubeDNN&nbsp;和&nbsp;DSSM&nbsp;的区别是什么(我从两者双塔结构的区别、对&nbsp;Item&nbsp;embedding&nbsp;处理的区别进行了分析)。4.面试官口头表述出了两道&nbsp;medium&nbsp;代码题,都是动态规划:)力扣对应题目见下方。我是直接在&nbsp;vscode&nbsp;上写然后讲思路的。5.可以实习多久,什么时候可以开始实习,有在考虑其他公司的机会吗?我说这个不太方面透露,面试官笑了:)所以这个问题一般怎么回答比较好。有什么想问的问题,我问了一下部门的业务方向是什么,面试官说是QQ的部门,做QQ短视频推荐的,然后让我打开QQ看一下就知道了。我打开QQ找了一下才看到,面试官说你是不是把QQ卸了:)我说我平时比较忙,QQ基本上只用聊天功能哈哈哈哈哈哈哈(好吧)。力扣对应题目:【1143.&nbsp;最长公共子序列】(原题,代码见图片)给定两个字符串&nbsp;text1&nbsp;和&nbsp;text2,返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列,返回&nbsp;0。一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。例如,&amp;amp;quot;ace&amp;amp;quot;&nbsp;是&nbsp;&amp;amp;quot;abcde&amp;amp;quot;&nbsp;的子序列,但&nbsp;&amp;amp;quot;aec&amp;amp;quot;&nbsp;不是&nbsp;&amp;amp;quot;abcde&amp;amp;quot;&nbsp;的子序列。两个字符串的公共子序列是这两个字符串所共同拥有的子序列。(我直接用二维&nbsp;dp&nbsp;秒了,然后面试官问我&nbsp;text1&nbsp;和&nbsp;text2&nbsp;的当前字符不相等时的状态转移方程求&nbsp;max&nbsp;为什么不加一个&nbsp;dp[i-1][j-1],我还想了一点时间怀疑自己的代码,最后说&nbsp;dp[i-1][j-1]&nbsp;的情况已经包含在了&nbsp;dp[i-1][j]&nbsp;和&nbsp;dp[i][j-1]&nbsp;里了。然后面试官问我如果要求最长公共子序列具体的序列是什么怎么求,然后我就蒙了,思考了很久,觉得可以用&nbsp;dfs&nbsp;爆搜,然后还是用&nbsp;dp&nbsp;的话可不可以将&nbsp;dp&nbsp;数组的&nbsp;int&nbsp;改成&nbsp;string,但是这样字符不相等的时候状态会分裂,所以应该比较难做,最后面试官说把这个当做课后题我回去再思考一下吧)【最长公共子串】(好像没找到&nbsp;string&nbsp;类型的原题,但是有数组类型原题【718.&nbsp;最长重复子数组】,代码见图片)直接把上一题【1143.&nbsp;最长公共子序列】的求公共子序列改成求公共子串。(面试官还是继续从上面那题展开考我变体,问我把求公共子序列改成求公共子串要怎么求。我一开始还想着用扩展&nbsp;kmp&nbsp;(z函数)&nbsp;解,但是发现这样需要把其中一个字符串的所有子串先求出来,多此一举,就还是用二维&nbsp;dp&nbsp;秒了。其实代码就是把【1143.&nbsp;最长公共子序列】的第二个状态转移方程变一下,然后用一个&nbsp;maxLen&nbsp;实时更新求到的最长公共子串)复盘:1.前面经过几次面试,&nbsp;现在对面试的流程和自己的简历内容已经比较熟悉了,但是因为对推荐算法的知识是新学的,所以遗忘很快,需要抽时间复习一下。2.这次直接出了两道中等动态规划题,差点招架不住,因为动态规划真的是我最弱的知识点,动态规划的题只要一难一变化我很容易就歇菜:)所以还是多练练动态规划吧:) #腾讯#&nbsp;&nbsp;#暑期实习#&nbsp;&nbsp;#面经#&nbsp;&nbsp;#算法#
查看4道真题和解析
点赞 评论 收藏
分享
8.7日投递&nbsp;上海的嵌入式软件8.12通知,8.29下午进行一面(提前了超多时间通知)一面内容如下:1.&nbsp;了解个人基本情况2.&nbsp;实习项目3.&nbsp;最新项目的最大挑战4.&nbsp;GPIO的配置5.&nbsp;高阻态的理解6.&nbsp;项目中TIMER的作用7.&nbsp;中断具体处理,如何找到服务函数(回答的不好,应该是中断到达后判断优先级以及是否使能,之后中断向量表查找并跳转到ISR,执行后返回中断现场)8.&nbsp;CAN总线的理解9.&nbsp;FreeRTOS的任务,是否有竞争抢占10.&nbsp;抢占过程中,资源如何保证(并没过多考虑,使用双缓存)11.&nbsp;C编译过程,编译后有多少个内存段(就是操作系统里面学的内存空间分布)12.&nbsp;全局变量,局部变量位置13.&nbsp;栈的作用(函数调用管理,局部变量存储,控制流,递归调用,空间高效,调试支持)14.&nbsp;实习遇到的困难15.&nbsp;求职方向,对公司的了解,期望薪资等16.&nbsp;经典反问一面完成后,大概一天给发邮件通知通过,参加笔试,双机位。笔试内容:1.&nbsp;选择,就是一些基础的开发还有底层相关的八股文2.&nbsp;代码,代码难度也不高,基本都打卡题难度加起来大概半小时左右肯定就能做完了。周末做的笔试,周一下午打电话通知通过了,选择二面时间,然后发邮件确认。目前就是抽时间做一个简单的介绍PPT,然后准备一下。各位可以参考一面和笔试的经验。#秋招##通信硬件人笔面经互助##经纬恒润求职进展汇总##经纬恒润##嵌入式#
查看16道真题和解析 投递经纬创投等公司10个岗位 通信硬件人笔面经互助
点赞 评论 收藏
分享
点赞 评论 收藏
分享
1 4 评论
分享
牛客网
牛客企业服务