LINUX驱动程序初体验

2019-07-13 07:15发布

 严格来说已经不算第一次了 大三的时候刚开始学嵌入式LINUX  就写过驱动 不过过了这么久没弄 基本忘光了 从头开始. 这篇文章不讲驱动的框架,而是记录我在编写第一个驱动中遇到的问题. 问题一: 到底哪个先执行     这是一个来自运算符优先级的诡异的问题.是在编写用户空间代码时遇到的.还是先上问题代码: 这段代码的意图在于打开我编写的设备,并开启设备的异步通知机制; 不过在执行write系统调用的时候,用于测试的字符串"s"被打印在了控制台上. ..... 这真是个让人困惑的问题. 这个自符串(虽然只有一个字符的长度)本来是写向设备的.是如何阴差阳错到了控制台上呢...  检查文件描述符fd的值,发现居然是0 .0 代表了控制台设备 是每个进程默认打开的设备 难怪乎了. 可明明设备打开是成功,fd为什么变成了0呢.  问题就出在 if(fd = open("/dev/mod",O_RDWR) < 0) 上面.  原以为赋值运算符的优先级高于<运算 一查才发现原来是倒过来的 所以<运算先被执行了 结果为假 也就是0  这个值又被赋给了 fd......  真相大白... 这个错误虽然是因为错误的估计了运算符的优先级而引起的 却也暴露了用C这种介于高级与低级之间的语言进行编程时给程序员带来的问题--- 数据结构类型太少.  在JAVA之下,标识文件的数据结构类型与标识真假的boolean类型代表两个完全不同的类型 相互赋值时会发生错误 因此象这类错误会在编译器就被发现而不至于引起更让人困惑现象.C语言里, 文件句柄,BOOL值都是用整型值来表示 相互赋值在语法上没有任何问题 于是上面的问题就出现了... 问题二:返回值太重要 重要的可以决定程序是否崩溃     相对与前一个 这个问题隐藏得更深(至少对初学者来说是这样)  问题代码在这里: static int mobile_open(struct inode * inode,struct file * filp)
{
    MOD_INC_USE_COUNT;
    
    
return 1;
}
    看看吧 多么简单的代码 就两句;作为一个驱动的 open 方法的实现,这个函数仅仅增加引用计数值,然后返回.可就是两句代码. 引得其后在用户空间里的代码在调用fcntl系统调用设置设备文件属主的时候出现了segmentation fault .  按照LINUX对驱动程序方法的定义,方法的返回值有重大的意味. 他标志着这次方法的调用是否成功.而我在写这个方法的时候,想当然的以为1代表打开成功. 就给返回了. 而事实恰恰是LINUX规定OPEN方法返回0才代表万事OK.于是,一方面内核认为这是一次失败的设备打开操作,而另一方面却返回给用户空间的OPEN系统调用一个合法的文件描述符... 所以在FCNTL系统调用作用与这个文件描述符的时候,将访问一个没有为其分配空间的数据结构,产生非法访问的错误...     将return 1改为return 0.OK. 问题三: 她和她,WHICH IS YOUR RIGHT ONE?     LINUX最为迷惑开发者之一的地方在与起头文件. 你可以在很多地方看到有include字样的目录. 而GCC会有一个默认的寻找头文件的目录 .在本例中,为了编译出内核一部分的MODULE,必须使用内核代码中的头文件目录.这很简单,我用-I选项就能轻松搞定. 不过,别忘了 ,GCC默认的头文件搜索目录始终默默的存在着, 当我们自己指定的目录出现问题时,这个我们并不注意到的目录里的文件会是我们的恶梦...     本例中,当使用ARM-LINUX-GCC 交叉编译器为手机编译MODULE时,出现类似下面这样的错误: In file included   from  ...................................................... xxx.h    from.........................................................yyy.h   from...........................................................zzz.h Symbol  ABC redefined.... 仔细观察这些问题头文件所在的路径,你会惊讶的发现他们存在于我们指定编译器搜索的路径之外.这就是默认的路径------两套不同的头文件体系交叉被编译器默不作声的交叉使用了. 试想我们想要INCLUDE的是在A目录下的文件A.H.而在B目录下存在一个同名的A.H 并被错误的INCLUDE近来...这会是什么后果...乱套了...      可为什么会发生这种状况呢. 我们用-I/usr/src/arm-linux-src/include选项作为头文件的目录,可头文件还会包含其他头文件,举个例子,这个目录下的ptracr.h会有这样一行 #include 意思是:编译器,你给我把//usr/src/arm-linux-src/include/asm下的ptrace.H给我包含进来 编译器一找,发现没有asm这个目录,这个时候他并不报错,而是在默认的目录里继续寻找是否有个asm目录,他下面是否有个 ptrace.h  一找,哟 还真有 就是他了...本来一个是给用户程序用的 ,另一个是给内核程序用的 就这么混用到了一起 ....       那那个惹出问题的ptrace.h 文件到底该在哪呢 其实asm是一个包含具体体系结构代码的目录,他在/usr/src/arm-linux-src/include以这种形式存在:    asm-arm , asm-x86, asm-mips......     为了编译出给ARM体系结构的代码,asm必须指向的是 asm-arm目录.这个好解决:ln -s ./asm-arm asm 这步昨晚后,所有编译器出错信息全部消失...