第一篇:嵌入式linux驱动学习总结
调用gcc: export PATH=$PATH:/usr/local/arm/3.4.1/bin 用gcc编译:arm-linux-gcc –o run adc.c 查看gcc: arm-linux-gcc –v 配置NFS 安装NFS Ubuntu上默认是没有安装NFS服务器的,首先要安装NFS服务程序: sudo apt-get install nfs-kernel-server 安装nfs-kernel-server时,apt会自动安装nfs-common和portmap 这样,宿主机就相当于NFS Server 配置NFS(1)配置portmap 方法1: 编辑/etc/default/portmap, 将-i 127.0.0.1 去掉.我用的是第一种方法!但不知道具体什么意思
方法2: $ sudo dpkg-reconfigure portmap , 对Should portmap be bound to the loopback address? 选N.(2)配置/etc/hosts.deny 禁止任何host(主机)能和你的NFS服务器进行NFS连接,加入: ### NFS DAEMONS portmap:ALL lockd:ALL mountd:ALL rquotad:ALL statd:ALL(3)配 置/etc/hosts.allow 允许那些你想要的主机和你的NFS服务器建立连接。下列步骤将允许任何IP地址以192.168.2开头的主机(连接到NFS服务器上),也可以指定 特定的IP地址,加入: ### NFS DAEMONS portmap: 192.168.1.lockd: 192.168.1.rquotad: 192.168.1.mountd: 192.168.1.statd: 192.168.1./etc/hosts.deny 和 /etc/hosts.allow 设置对portmap的访问.采用这两个配置文件有点类似“mask”的意思.现在/etc/hosts.deny中禁止所有用
户对portmap的访问.再在/etc/hosts.allow 中允许某些用户对portmap的访问.sudo /etc/init.d/portmap restart 重启portmap daemon.(4)配置/etc/exports NFS挂载目录及权限由/etc/exports文件定义 比如我要将将我的/opt/FriendlyARM/mini2440/root_nfs目录让用户的IP共享, 则在该文件末尾添加下列语句:
/opt/FriendlyARM/mini2440/root_nfs *(rw,sync,no_root_squash)NFS客户端能够共享NFS服务器/opt/FriendlyARM/QQ2440/root_nfs目录内容.且有读,写权限, 并且该用户进入
/opt/FriendlyARM/mini2440/root_nfs目录后的身份为root 最好加上sync, 否则 $ sudo exportfs-r 时会给出警告, sync是NFS的默认选项.showmount-e 查看NFS server的export list.若更改了/etc/exports, 运行 sudo exportfs-r 更新
运行 /etc/init.d/nfs-kernel-server restart 重启nfs服务
(5)测试NFS 可以尝试一下挂载本地磁盘
我前面开始时已经提到了我的主机IP是192.168.1.101 我现在试把/opt/FriendlyARM/mini2440/root_nfs目录挂载到/mnt目录下 mount-t nfs 192.168.1.101:/opt/FriendlyARM/mini2440/root_nfs /mnt 就101和:之间的空格我耗了1个小时
成功的挂载上的话你会在/mnt目录下看到root_nfs这个文件夹下的内容
Arm9之家账户:李向阳85 安装ncurses
ncurses是字符终端下屏幕控制的基本库。可能很多新开发的程序都不使用了,不过如果要编译一些老程序,还经常遇得到。
安装:ncurses
http://ftp.gnu.org/pub/gnu/ncurses/
./configure--without-debug #以及其它自己需要的参数
make
make install
3,继续你的make menuconfig。
图形界面出来了。
允 23:22:22
小允 23:22:31
小允 23:22:40 有用不同的名字的吗 寂寞沙洲冷 23:22:42 我把drivers下面的Makefile都添加上了 寂寞沙洲冷 23:23:17 叫misc devices 可以吧 小允 23:23:22 2410ADC_driver和2410ADC_FEATURESnengbutongma 小允 23:23:33 这两个必须一样 小允 23:23:42 这才是Kconfig的工作原理
第二篇:嵌入式linu学习心得
嵌入式Linux学习心得
1、Linux命令
ls:查看目录-l以列表方式查看;ls –l 与ll的功能一样 pwd: 查看当前的目录
cd:改变当前操作目录cd /直接跳到根目录 cd..回到上一级目录 cat: 打印显示当前文件的内容信息
mkdir:创建目录
fdisk: 查看硬盘分区信息,-l以列表方式查看
->代表是链接文件,类似window下的快捷方式。
cp: 复制命令,例子cp 文件名 /home/dir/
mv: 移动或改名,如mv sonf.confsonf.txt(改名)移动:mv sonf.conf / rm:删除命令,如rm –f test.c;如删除目录rm –fr d
man:查看某个命令的帮助,man 命令
2、各系统目录的功能
drw—r—w--:d代表是目录,drw代表当前用户的权限,r代表组用户的权限,w代表其它用户的权限。x代表有执行权限。
/boot/gruff.conf: 启动引导程序
/dev:brw—rw--:b代表是块设备。Linux设备有三种,块设备(b开头)、字符设备(c开头)、网络设备。had代表第一个硬盘,hdb代表第二个硬盘。Hdb2代表第二块硬盘的第二个分区。3,67代表主设备为3,从设备为67./etc:存放的是系统的配置文件。Inittab文件存放不同启动方式下必须启动的进程。Inittab文件中有6个启动level,wait中对应着6个level的目录,respawn代表当一个进程被意外终止了,但会自动启动的进程,如守护进程。rc.d目录中存放了一个rc.sysinit文件,里面存放系统初始化配置信息。/etc还有一个vsftpd里面存放tcp、ftp的配置。
/home : 用户目录,存放用户的文件,/lib:存放库文件,后缀为so的文件代表动态链接库。
/lost+found:系统意外终止,存放一些可以找回的文件。
/mnt:挂载外部设备,如挂载光驱:mount –t /dev/cdrom/mnt/cdrom,如
果在双系统中,要查看windows中D盘的文件,首先应该将D盘的文件映射过来,mount –t /dev/hda2/mnt/windows/d
/opt:用户安装的应用程序
/proc:是系统运行的映射,比较重要。里面的文件数字代表进程号。每个进程号目录下包含进程的基本信息。还有其他信息,如cpuinfo等,内核支持的文件系统filesystem等。系统支持的中断interrupts,iomen代表内存分配情况。ioport存放IO端口号。还有分区信息,modole信息,状态信息,版本信息
对于Linux的设备驱动程序,有两种加载模式,一种是直接加载进linux内核,一种是以模块的方式加载到内核。
/sbin: 系统管理的一些工具。如poweroff关机工具。
/usr: 安装系统时很多文件放在此目录下面,包含一些更新等,include包含的头文件,lib 是Linux的库文件,src包含Linux2.4的内核源码
/var:存放是临时变量
3、
第三篇:嵌入式学习总结
嵌入式学习总结
一.基本信息...................................................................................................................1 二.嵌入式系统的组成..................................................................................................1 三.嵌入式的操作系统特点..............................................................................................1 四.嵌入式系统的文件系统..........................................................................................2 五.嵌入式文件系统的特点...........................................................................................2
一.基本信息
嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分。通常,嵌入式系统是一个控制程序存储在ROM中的嵌入式处理器控制板。事实上,所有带有数字接口的设备,如手表、微波炉、录像机、汽车等,都使用嵌入式系统,有些嵌入式系统还包含操作系统,但大多数嵌入式系统都是是由单个程序实现整个控制逻辑。
二.嵌入式系统的组成
一个嵌入式系统装置一般都由嵌入式计算机系统和执行装置组成,嵌入式计算机系统是整个嵌入式系统的核心,由硬件层、中间层、系统软件层和应用软件层组成。执行装置也称为被控对象,它可以接受嵌入式计算机系统发出的控制命令,执行所规定的操作或任务。执行装置可以很简单,如手机上的一个微小型的电机,当手机处于震动接收状态时打开;也可以很复杂,如SONY 智能机器狗,上面集成了多个微小型控制电机和多种传感器,从而可以执行各种复杂的动作和感受各种状态信息。
三.嵌入式的操作系统特点
(1)可裁剪性。支持开放性和可伸缩性的体系结构。
(2)强实时性。EOS实时性一般较强,可用于各种设备控制中。(3)统一的接口。提供设备统一的驱动接口。
(4)操作方便、简单、提供友好的图形GUI和图形界面,追求易学易用。
(5)强稳定性,弱交互性。嵌入式系统一旦开始运行就不需要用户过多的干预、这就要负责系统管理的EOS具有较强的稳定性。嵌入式操作系统的用户接口一般不提供操作命令,它通过系统的调用命令向用户程序提供服务。
(6)固化代码。在嵌入式系统中,嵌入式操作系统和应用软件被固化在嵌入式系统计算机的ROM中。(7)更好的硬件适应性,也就是良好的移植性
四.嵌入式系统的文件系统
通用操作系统的文件系统通常具有以下功能:
提供用户对文件操作的命令。
提供用户共享文件的机制。
管理文件的存储介质。
提供文件的存取控制机制,保障文件及文件系统的安全性。
提供文件及文件系统的备份和恢复功能。
提供对文件的加密和解密功能。
五.嵌入式文件系统的特点
1兼容性。嵌入式文件系统通常支持几种标准的文件系统,如FAT32、○JFFS2、YAFFS等。
2实时文件系统。除支持标准的文件系统外,为提高实时性,有些嵌入 ○式文件系统还支持自定义的实时文件系统,这些文件系统一般采用连续的方式存储文件。
3可裁剪、可配置。根据嵌入式系统的要求选择所需的文件系统,选择 ○所需的存储介质,配置可同时打开的最大文件数等。
4支持多种存储设备。嵌入式系统的外存形式多样了,嵌入式文件系统 ○需方便的挂接不同存储设备的驱动程序,具有灵活的设备管理能力。同时根据不同外部存储器的特点,嵌入式文件系统还需要考虑其性能、寿命等因素,发挥不同外存的优势,提高存储设备的可靠性和使用寿命。
总结:嵌入式是一门以应用为中心,以计算机为基础,软件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗等严格要求的专用计算机系统。
第四篇:基于嵌入式Linux的IPMI驱动程序设计
基于嵌入式Linux的IPMI驱动程序设计
2012-04-28
丁四华,张志政,东南大学
摘 要:针对Linux内核下通用IPMI协议实现部分过于复杂、繁琐、占用过多内存资源,不利于某些简单嵌入式应用场合的不足,提出了通过在FPGA逻辑模拟I2C通道的HOST单板与IPMC子卡的硬件环境上,采用经过简化的IPMI请求/应答消息格式,借助专业Linux的I2C核心框架,实现了相应的IPMI驱动程序功能,方便了管理员监测、管理、诊断系统状态,并根据系统崩溃时的状态,来及时恢复系统。本文详细描述了简化的IPMI协议原理,以及基于Linux内核I2C核心框架的IPMI驱动的各功能模块的设计原理,并详细描述了IPMI驱动功能模块的数据收发流程。研究结果表明,改进的简化IPMI驱动功能模块有效的降低了系统的复杂度、节省了内存资源,达到了精简嵌入式应用系统的目的。关键词:Linux;IPMI协议;FPGA;IPMC子卡;I2C总线 1.引言
IPMI(Intelligent Platform Management Interface)是一种无代理的智能平台管理接口,是由Intel 等公司推出的一个重要的开放标准。IPMI定义了管理员如何监测系统硬件状态,控制系统组件和检索重要系统事件的日志以进行远程管理和恢复。
近年来,广泛采用IPMI协议来监测硬件系统的状况,例如温度、风扇、电压和硬件错误(存储、网络等)和机箱防盗,刀片支持等。由于IPMI可以独立于操作系统之外,即使操作系统已经暂停或服务器已经关闭,管理员照样可以监测、管理、诊断和恢复系统。
针对Linux 内核自带的IPMI驱动过于复杂、繁琐、占用过多内存资源的不足,本文根据ATCA(Advanced Telecom Computing Architecture)方案设计,采用简化的IPMI协议来管理ATCA机框内各硬件单板的物理地址与状态。ATCA平台的HOST单板通过I2C总线接口同IPMC(Intelligent Platform Management Controller)子卡通信,从而获得启动所需的单板物理地址等消息, HOST 单板和IPMC子卡之间采用IPMI协议进行通信。本文讨论了该方案在Linux 下IPMI 驱动程序功能的设计。2.IPMI 协议原理
HOST单板和IPMC子卡之间的硬件接口通过I2C总线来通信,软件上采用IPMI协议。HOST单板不能直接获取单板的相关物理信息,必须要通过IPMI 协议从IPMC子卡上获取。
下面描述IPMI 协议设计要点。
(1)IPMI协议采用Request/Response 的模式,我们通常把IPMI的请求消息称为命令,通过采用Request/Response 模式可使IPMI消息在不同的传输通道上传送,在我们的系统中,采用了I2C总线作为物理上面的传输通道进行消息的传送,上面传输的消息的格式按照IPMI协议规定的消息格式。
(2)IPMI命令是一个功能命令的集合,通过IPMI消息中的Network Function字段来表示,这些命令集合中包括了与事件相关的一些命令集合。通过在命令集合里面不同的字段的不同含义来代表该条消息的具体请求。
(3)IPMI中所有的请求消息中都包括一个网络功能(Network Function),命令(command),以及可选的数据(data)字段。IPMI的响应消息和IPMI的请求消息采用同样的消息格式。IPMI消息分为请求和应答两种消息,具体的消息格式如下所示:
图1 IPMI请求消息格式
图2 IPMI应答消息格式
在IPMI消息中,一次请求消息的最大长度不能超过32Bytes,如图1中所示,在这最长的32个BYTES的消息结构中,包括了接收该消息的地址(Responser Address),自身的地址(Requester Address),消息中的NetFn/LUN字段总共是一个BYTE,前面6个bits表示的是NetFn,后面的2个bits表示的请求方的LUN,Seq/Lun字段与NetFn/LUN字段相似,只不过前面的6个bits表示的是序列号,command字段用来表示具体的命令,data字段中存放的是具体的数据,最大长度不能超过25bytes,除了上面的三个字段外,还有两个校验和字段,分别用来对消息头和消息体来进行校验。
应答消息(参见图2)和请求消息在结构上面相同,在回应请求的应答消息中用该将请求的地址和响应的地址字段相互调换,序列号保持不变,NetFn字段加1,LUN填写的是将请求消息中的字段互换。例如在HOST通过IPMC获取32bits的物理信息,HOST按照请求的消息格式发送一条请求到IPMC,IPMC收到后,按照响应的消息的格式发送应答到HOST,并将物理信息放到应答消息的DATA字段中,HOST接收到回应后就可以从DATA字段中直接获取到相关的数据信息。3.Linux下的IPMI驱动设计
Linux下IPMI模块驱动采用分层设计,详细设计说明见各以下各小节。3.1 Linux下的IPMI驱动架构
Linux下IPMI模块驱动分为两部分,一部分为I2C驱动模块,位于Linux内核态,为IPMI驱动提供底层Linux 硬件驱动;另一部分为BSP子系统PMI接口封装层,位于Linux用户态,为上层应用提供IPMI模块初始化、消息发送、消息接收接口。模块位置参见图3。
图3 Linux下的IPMI驱动架构
3.2 硬件环境描述
I2C总线是PHILIPS(飞利浦)公司推出的两线式串行总线,用于连接微控制器及其外围设备,具有简单、高效等特点。由于其接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片引脚的数量,降低了互联成本,特别适用于嵌入式产品。
HOST单板通过FPGA(Field Programable Gate Array)逻辑模拟的I2C模块与IPMC子卡通过FPGA逻辑模拟的I2C模块通信,该I2C模块目前只支持主发,从收功能。参见图4。另外,在该条I2C总线上,可以同时挂接其它的I2C设备,例如,温度监控器等。
图4 HOST单板与IPMC子卡通信图示
3.3 Linux I2C驱动模块设计
Linux下IPMI软件模块代码在整个Linux系统中属于字符设备驱动,按照模块主要功能来划分,整个驱动大体可以分为以下五个模块:
(1)I2C核心框架功能实现子模块;(2)FPGA逻辑模拟I2C设备驱动子模块;
(3)FPGA逻辑模拟I2C适配器驱动子模块;
(4)I2C设备方法子模块;
(5)IPMI用户态接口封装子模块。3.3.1 I2C核心框架功能实现子模块
该模块提供了核心数据结构的定义、I2C适配器驱动和设备驱动的注册、注销管理,I2C通信方法上层的、与具体适配器无关的代码、检测设备地址的上层代码等。
内核中I2C 相关代码可以分为三个层次(参见图5):
(1)I2C core框架:提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。
(2)I2C总线适配器驱动:定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。
(3)I2C设备驱动:定义描述具体设备的i2c_client和可能的私有数据结构、借助I2C core提供的函数接口完成设备在内核的注册,并实现具体的功能,包括read, write以及ioctl等对用户层操作的接口。
总体而言,Linux中I2C总线的驱动分为两个部分:总线驱动(BUS)和设备驱动(DEVICE)。I2C core与I2C总线适配器驱动完成了硬件上的主机总线驱动(BUS),而I2C driver则实现了从机设备驱动。在设计中,I2C core提供的接口是统一的,不需要修改,我们只需要实现特定I2C总线适配器驱动和I2C设备驱动,这样大大提高了系统的可移植性。
图5 Linux内核I2C框架模块关系图示
3.3.2 FPGA 逻辑模拟I2C 设备驱动子模块
该模块描述具体设备的i2c_client和可能的私有数据结构、借助I2C框架的i2c_probe函数实现注册设备的attach_adapter方法、提供设备可能使用的地址范围、以及设备地址检测成功后创建i2c_client数据结构的回调函数。这部分功能主要由bsp_ipmi_drv.c文件来描述。3.3.3 FPGA逻辑模拟I2C适配器驱动子模块
FPGA模拟I2C适配器驱动子模块,定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C 适配器上的I2C总线通信方法,并由i2c_algorithm 数据结构进行描述。这部分功能主要由bsp_ipmi_adap.c文件来描述。
3.3.4 I2C设备方法子模块
该模块提供创建I2C适配器的/dev/ipmi/%d 设备节点,提供IPMI设备访问方法(read & write, open & release, ioctl)等,这部分功能主要由bsp_ipmi_devintf.c文件来描述。3.3.5 IPMI用户态接口封装子模块 该模块主要实现I2C通信从设备的选取、I2C设备方法系统调用的封装,和IPMI消息封装、数据收发的格式化等操作。这部分功能主要由bsp_ipmi_api.c文件来描述。
3.3.6 采用I2C框架的IPMI驱动的数据收发流程
IPMI模块驱动采用内核I2C核心框架,HOST单板IPMI模块驱动的数据收发流程分为四个层次(参见图6)
数据发送流程:
(1)上层用户调用Linux用户态IPMI消息发送函数BSP_IpmiSend()函数向IPMC子卡发送消息;
(2)BSP_IpmiSend()函数通过write系统调用切换到Linux内核态,调用I2C设备接口驱动层的write方法实现ipmcdev_write()函数;
(3)ipmcdev_write()函数通过调用I2C核心层i2c_master_send()函数来发送消息;
(4)i2c_master_send()函数最后调用具体I2C适配器驱动层的i2c_ipmc_xfer()函数来操作I2C总线,该函数受到I2C框架总线锁的保护,任何时刻只能由一个进程访问,符合硬件I2C总线的实际情况。
(5)i2c_ipmc_xfer()函数根据入参调用ipmc_write()函数来向I2C 硬件设备发送数据。
数据接收流程:
(1)上层用户调用Linux用户态IPMI消息接收函数BSP_IpmiRecv()函数接收来自IPMC子卡的消息;
(2)BSP_ IpmiRecv()函数通过read系统调用切换到Linux内核态,调用I2C设备接口驱动层的read方法实现ipmcdev_read()函数;
(3)ipmcdev_read()函数通过调用I2C核心层i2c_master_recv()函数来接收消息;
(4)i2c_master_read()函数最后调用具体I2C适配器驱动层的i2c_ipmc_xfer()函数来操作I2C总线,该函数受到I2C框架总线锁的保护,任何时刻只能由一个进程访问,符合硬件I2C总线的实际情况。
(5)i2c_ipmc_xfer()函数根据入参调用ipmc_read()函数来阻塞接收来自I2C硬件设备的数据。
图6 IPMI模块采用I2C核心框架的数据收发流程
4.总结
本文主要介绍了简化的嵌入式Linux的IPMI驱动程序设计。通过全文可以看出,基于FPGA逻辑模拟I2C通道的HOST 单板与IPMC 子卡的硬件环境,借助Linux内核稳定而又专业的I2C核心框架,使得设计和实现基于I2C核心框架的简化IPMI驱动变得非常容易。同时本文详细描述了IPMI协议的原理和简化后的消息格式。通过该驱动,管理员可以方便的监测、管理、诊断系统状态,并恢复系统。总之,改进的简化IPMI驱动功能模块有效的降低了系统的复杂度、节省了内存资源,达到了精简嵌入式应用系统的目的。
第五篇:基于嵌入式Linux的设备驱动程序设计
基于嵌入式Linux的设备驱动程序设计
Linux为是一个成熟而稳定的操作系统。将Linux植入嵌入式设备具有众多的优点,包括可剪裁和容易移植等,所以Linux操作系统在嵌入式领域获得了广泛的应用。嵌入式Linux一直是嵌入式领域的研究热点,与PC架构不同,嵌入式系统的硬件具有多样性和差异性,嵌入式系统的开发需要对特定系统进行硬件设计,同时还要针对这些硬件来编写驱动程序。Linux内核就是通过驱动程序来同外围设备打交道的,系统设计人员必须为每个设备编写驱动程序,否则设备无法在操作系统下正常工作。设备驱动程序设计的基本概念与模型
设备驱动程序是操作系统内核与机器硬件之间的接口,它为应用程序屏蔽了硬件的细节,在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,设计驱动程序是内核的一部分,可以实现以下功能:
对设备初始化和释放;
把数据从内核传送到硬件,以及从硬件读取数据;
读取应用程序传送给设备文件的数据,以及回送应用程序请求的数据;
检测和处理设备出现的错误。
前面已经提到驱动程序的作用,而编写驱动程序就是构造一系列可供应用程序调动的函数(包括open、release、read、write、llseek、ioctl等)。在用户自己的驱动程序中,首先要根据驱动程序的功能,实现file_operations结构中的函数,不需要的函数接口可以直接在file_operations结构中初始化为NULL;file_operations变量会在驱动程序初始化时注册到系统内部。当操作系统对设备操作时,会调用驱动程序注册的file_operations结构中的函数指针。
以下是嵌入式linux2.4设备驱动程序的最简模型。
具体实现前面定义的函数时,需注意下面几点:
1)在test_init函数中要通过调用register_chrdev()函数来向内核注册字符设备驱动程序。如果是块设备,则还需调用mmmap()进行地址空间的映射,再调用register_blkdev()函数来向内核注册块设备驱动程序,在Linux系统中,对中断的处理是属于系统核心部分,因而如果设备与系统之间以中断方式进行数据交换,则必须把该设备的驱动程序作为系统核心的一部分,也就是说设备驱动程序要通过调用request_irq()函数来申请中断,通过free_irq()函数来释放中断(在test_cleanup中实现)。
2)open()函数和release()函数的具体实现有着一定的对应性,在open()函数中主要是执行打开设备时的一些初始化代码,如果该驱动程序需要管理多个设备,那么还要获取从设备号,根据从设备号来判断需要操作的设备,其中,从设备号可通过调用函数MINOR(inode->i_rdev)来获得,然后再调用宏MOD_INC_USE_COUNT来使得驱动程序使用计数器加1,而在release()函数中则要进行相反的处理。即调用宏MOD_DEC_USE_COUNT来减小驱动程序使用计数器。
3)归根到底,驱动函数的实现就是调用内核所支持的函数(包括内核提供的API和用户自己定义的寄存器操作函数)来完成对设备的操作,虽然嵌入式系统设备的种类众多,不同设备操作的具体实现方法不可能相同,但是Linux操作系统提供了一系列特殊API,为开发内核驱动程序带来了很大的方便,调用这些API时需要注意的是:通常情况下,应用程序是通过内核接口访问驱动程序的(这是驱动程序的主要使用方式),因此驱动程序需要与应用程序交换数据,但是操作系统内核和驱动程序在内核空间中运行,而用户程序在用户空间中运行,用户程序不能访问内核空间,操作系统内核和驱动程序也不能使用指针或memcpy()等常规的C库函与用户空间传输数据,造成这种状况的主要原因是linux操作系统使用了虚拟内存机制,使用了虚拟内存机制后,用户空间的内存可能被换出,当内核使用用户空间指针时,对应的页面可能已经不在内存中了,因此在使用调用函数时要注意:设备驱动程序在申请和释放内存时不是调用malloc()和free(),而调用kmalloc()和kfree();用于内核空间与用户空间进行数据拷贝的函数主要有access_ok()(检查某内存空间是否有权访问),copy_to_user()和put_usr()(内核函数向用户空间传输数据),copy_from_user()和get_user()(用户空间向内核空间传输数据);关于内核空间与I/O空间的数据交换,不同体系结构的处理器对I/O的处理方式也不同,x86系列处理器中,I/O与内存完成不同,它是分开编址的,访问它要使用专用的指令;而对ARM体系结构的处理器来说,则是不区分I/O和内存,统一编址的,可以使用同样的指令访问,在驱动程序中可以使用一系列函数来访问I/O口,如outb()、outw()、outl()inb()、inw()、inl()、outsb()、outsw()、outsl()、insb()、insw()和insl()等。
Linux2.6与2.4内核驱动程序的区别
为了彻底防止对正在被使用的内核模块进行错误操作,linux2.6内核在加载和导出内核模块方面都较2.4内核有所改进,避免了用户执行将导致系统崩溃的操作(例如强制删除模块等)。同时,当驱动程序需要在多个文件中包含 头文件时,不必定义宏来检查内核的版本。与2.4内核相比,2.6内核在可扩展性、吞吐率等方面有较大提升,其新特性主要包括:使用了新的调度器算法;内核抢占功能显著地降低了用户交互式应用程序;多媒体应用程序等类似应用程序的延迟;改进了线程模型以及对NPTL的支持,显著改善了虚拟内存在一定成程度负载下的性能;能够支持更多的文件系统;引进了内存池技术;支持更多的系统设备,在2.4内核中有约束大型系统的限制,其支持的每一类设备的最大数量为256,而2.6内核则彻底打破了这些限制,可以支持4095种主要的设备类型,且每个单独的类型又可以支持超过一百万个的子设备;支持反向映射机制(reverse mapping),内存管理器为每一个物理页建立一个链表,包含指向当前映射页中每个进程的页表条目的指针。该链表叫PTE链,它极大的提高了找到那些映射某个页的进程的速度。
Linux操作系统的设备驱动程序是在内核空间运行的程序,其中涉及很多内核的操作,随着Linux内核版本的升级,驱动程序的开发必然也要作出相应的修改,总之,在linux2.6内核上编写设备驱动程序时具体要注意以下几个方面:
1)Linux2.6内核驱动程序必须由MODULE_LICENSE(“Dual BSD/GPL”)语句来定义许可证,而不能再用2.4内核的MODULE_LICENSE(“GPL”)。否则,在编译时会出现警告提示。
2)Linux2.6内核驱动程序可以用int try_module_get(&module)来加载模块,用module_put()函数来卸载模块,而以前2.4内核使用的宏MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT则可不用。
3)前面给出的字符型设备驱动程序模型中结构体file_operations的定义要采用下面的形式。这是因为在Linux内核中对结构体的定义形式发生了变化,不再支持原来的定义形式。
4)就字符型设备而言,test_open()函数中向内核注册设备的调用函数register_chrdev()可以升级为int register_chrdev_region(dev_t from,unsigned count,char * name),如果要动态申请主设备号可调用函数int alloc_chrdev_region(dev_t * dev,unsigned baseminor,unsigned count,char * name)来完成;原来的注册函数还可以用,只是不能注册设备号大于256的设备,同理,对于块设备和网络设备的注册函数也有着相对应的代替函数。
5)在声明驱动程序是否要导出符号表方面有着很大的变化。当驱动程序模块装入内核后,它所导出的任何符号都会变成公共符合表的一部分,在/proc/ksyms中可以看到这些新增加的符号。通常情况之下,模块只需实现自己的功能,不必导出任何符号,然而,如果有其他模块需要使用模块导出的符号时,就必须导出符号,只有显示的导出符号才能被其他模块使用,Linux2.6内核中默认不导出所有的符号,不必使用EXPORT_NO_SYMBOLS宏来定义;而在2.4内核中恰恰相反,它默认导出所有的符号,除非使用EXPORT_NO_SYMBOLS,因此在上面给出的范例中可以省略去该定义语句。
6)Linx内核统一了很多设备类型,同时也支持更大的系统和更多的设备,原来Linux2.4内核中的变量kdev_t已经被废除不可用,取而代之的是dev_t。它拓展到了32位,其中包括12位主设备号和20位次设备号。调用函数为unsigned int iminor(struct inode * inode)和unsigned int imajor(struct inode * inode),而不再用Linux2.4版本中的int MAJOR(kdev_t dev)和int MINOR(kdev_t dev)。
所有的内存分配函数不再包含在头文件 中,而是包含在 中,而原来的 已经不存在。所以当在驱动程序中要用到函数kmalloc()或kfree()等内存分配函数时,就必须要定义头文件 而不是。同时,前面提到的申请内存和释放内存函数的具体参数也有了一定的改变,包括:分配标志GFP_BUFFER被取消,取而代之的是GFP_NOIO和GFP_NOFS;新增了_GFP_REPEAT、_GFP_NOFAIL和_GFP_NORETRY分配标志等,使得内存操作更加方便。
8)因为内核中有些地方的内存分配是不允许失败的,所以为了确保这种情况下得成功分配,linux2.6版本内核中开发了一种称为“内存池”的抽象。内存池其实相当于后备的高速缓存,以便在紧急状态下使用。要使用内存池的处理函数时,必须包含头文件。内存池处理函数主要有以下几个:mempool_t *mempool_create()、void*mempool_alloc()、void mempool_free()、int mempool_resize();
另外值得一提的是:2.6内核为了区别以.o为扩展名的常规对象文件,将内核模块的扩展名改为.ko,所以驱动程序最后是被编译为ko后缀的可加载模块,在应用程序中加载驱动程序模块时要注意。结语
驱动程序的开发作为嵌入式linux系统开发过程当中最重要的环节之一,与硬件特性和操作系统的内核有着紧密的联系。随着linux内核版本的升级,内核驱动程序必然要作出相应的改进,相信随着嵌入式Linux系统在各个领域中的广泛应用,具有可抢占实时性的Linux2.6内核必定会在嵌入式领域大显身手。本文会对广大的驱动程序开发人员有一定的帮助。