《嵌入式操作系统》2013年实验指导书

更新时间:2023-09-23 16:32:01 阅读量: IT计算机 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

嵌入式操作系统

实 验 指 导 书

计算机科学与信息工程学院

2012年9月3日

目录

实验一 系统引导实验 ........................................................................................... 1 实验二 IRQ中断处理 ............................................................................................ 3 实验三 LINUX常用命令及工具实验 ................................................................... 7 实验四 MAKEFILE实验 ...................................................................................... 9 实验五 BOOTLOADER实验 ............................................................................. 13 实验六 LINUX内核编译实验 ............................................................................. 16 实验七 驱动程序结构实验 ................................................................................... 19

实验一 系统引导实验

【实验目的】

1. 了解PXA270处理器功能结构 2. 了解系统的基本硬件组成 1. 了解ARM指令集

2. 掌握嵌入式系统的一般引导规律 3. 掌握常见ARM开发工具软件的使用 【实验仪器】

PC机一台,ARM10试验箱一套,以及必要的软件安装包 【实验原理】 1、程序介绍

本章主要通过一个简短的Boot程序向读者揭示如何编写开发板的启动程序,同时本程序也可以用来引导其他章节的示例程序。本程序主要为了让读者能够清晰了解系统复位后如何从0x0开始引导。本引导程序驱动底板上的八盏LED就会向右点亮,不断循环下去。 2、系统复位

对于PXA270处理器来说,系统复位后的PC指针总是为0x0,以本开发板来说,片选信号nCS0所连接的为FLASH 芯片,boot 程序应该被烧写到该FLASH芯片上,且第一条指令应该放在0x0的地址(注意并不是所有的处理器都从地址0x0开始运行,有些处理器是从0xFFFF0开始运行的)。事实上,地址0x0-0x20之间为中断向量表,地址0x0为复位中断例程的入口点,即通过在0x0放一条无条件跳转语句,在系统加电或复位时,在地址0x0 开始跳转,从复位中断例程开始运行下去。 3、程序进入点

因为引导程序是自举的的程序,无需操作系统加载来执行,所以即使不设置初始入口点也可以执行,但这里是有必要对程序进入点进行描述。我们可以将Boot 程序看成是普通的映像文件,假设我们现在已经生成了映像文件,当Boot 映像被操作系统加载时,如何决定映像被执行的第一条指令呢?这里引入初始入口点和普通入口点。初始化入口点定义了映像的第一条被执行的指令,在编译程序时可以添加参数-entry address(或-entry offset+object(area) )来标示初始入口点,如果没有添加该参数,只要源程序中有唯一的伪操作ENTRY,则程序就被默认成初始入口点。即当镜像被烧入FLASH后,以ro_base属性决定的映像位置的第一条指令就是被定义在ENTRY 标示的段的第一条指令,读者可能对此有些模糊,我们这里以给定的程序来分析。在BootLoader代码里的boot.s 汇编文件里,可以发现以下程序: AREA boot ,CODE ,READONLY ENTRY

B Reset_Handler B Undefined_Handler B SWI_Handler B Prefetch_Handler B DataAbort_Handler NOP

B IRQ_Handler B FIQ_Handler

这小段代码标示了一个名叫boot 的代码段,属性为只读,而ENTRY 本来只表示为一个普通入口点,但在Boot 代码中,因为只使用一次ENTRY,所以ENTRY 就被定义为初始化入口点,这里并不是要求所有的源程序中只能使用一次ENTRY,相反可以多次使用ENTRY来标示普通入口点,但多次

使用ENTRY 后,就无法让系统知道镜像的第一条执行的指令在哪里,就必须在编译时增加-entry address参数。读者可以尝试一下代码中都不使用ENTRY或多次使用ENTRY 会发生什么现象。回到上面的代码中,我们发现伪操作ENTRY下有一条无条件跳转指令B,由于AREA 和 ENTRY都是伪操作,在不分配成实质的指令,所以,程序的第一条会被执行的指令就是B 指令,作为Boot 代码,初始入口点是不起作用的,因为它无需被加载而运行,初始入口点保存在ELF 头文件中,该值可被操作系统读取而跳转到初始入口点执行。 【实验内容】 1、分析代码

结合以上说明,对本实验所提供的汇编源代码进行分析,深入理解针对具体的硬件实现,软件是如何配合工作的。

2、程序的编译和下载

打开ADS,执行ProjectàMake ,也可以直接用快捷键F7 进行编译、连接生成映像文件。如图所示:

3、观察系统运行情况,对系统进行源码调试。 【习题与思考题】

1、简述ELF文件的内部层次结构。

2、简述连接器的4 个参数-ro_base,-rw_base,-first,-entry 的意义。 3、简述初始化入口点和普通入口点的区别,分别用在什么场合。

实验二 IRQ中断处理

【实验目的】

√ 学习编写中断处理程序 【实验原理】 1、程序介绍

本章例子主要使用按键实现外部中断,执行中断服务子程序。系统启动后,按12345678 任意键,相应的八段数码管就会点亮。 2、中断向量表

当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。处理器能够准确无误地响应中断,是因为ARM 体系结构里有一个中断向量表,该中断向量表将系统能够响应的7种异常中断类型的“入口地址”登记在一块连续的字节空间内,每种异常中断的“入口地址”占据4 个字节,这里“入口地址”实质是一些跳转指令或者是让PC指针赋值的指令,通常使用B或ldr 指令。简单地说,异常中断发生首先会跳转到中断向量表,此时跳转的位置会由系统根据中断类型来判断,由于中断向量表实质也是跳转指令所组成的指令序列,所以系统会再进行一次跳转,这次跳转便跳到中断处理程序(中断服务例程)的入口。从第一章开始,读者在学习Boot程序的编写时便开始接触中断向量表,Boot 程序的第一条指令b post 就是中断向量表里的第一个单元空间(4 个字节),这个单元空间是用作处理复位异常中断。

由于一条B和Ldr 指令就是32位的指令,即占4 个字节,所以整个中断向量表可以是如下形式: IMPORT Reset_Handler

IMPORT Undef_instrution_Handler IMPORT SWI_Handler IMPORT Prefetch_Handler IMPORT Abort_Handler IMPORT IRQ_Handler IMPORT FIQ_Handler

AREA boot ,CODE ,READONLY B Reset_Handler

B Undef_instrution_Handler B SWI_Handler B Prefetch_Handler B Abort_Handler NOP

B IRQ_Handler B FIQ_Handler

或者使用Ldr指令来实现: AREA boot ,CODE ,READONLY LDR PC,=Reset_Handler

LDR PC,=Undef_instrution_Handler LDR PC,=SWI_Handler LDR PC,=Prefetch_Handler LDR PC,=Abort_Handler NOP

LDR PC,=IRQ_Handler

(2)设置CPU的速度和时钟频率。 clock_enable :/* start.S*/ #if defined(CONFIG_PXA25x) ldr r0, =0x0001FFFF

#elif defined(CONFIG_PXA27x) ldr r0, =0x01FFFFFF #endif

ldr r1, =CKEN str r0, [r1] mov pc, lr

(3)存储控制单元初始化。包括正确地设置系统动静态存储控制器的各个寄存器等。 【实验仪器】

1、装有 Linux 操作系统的 PC 机一台;

2、XSBase270 或 XSBase255 ARM 实验开发平台一套 【实验内容】

1. bootLoader 程序的编译 为了编译 Bootloader 程序,需要事先在目标板上安装交叉编译工具 Toolchain。(见 linux 使用手册 Toolchain 安装部分)。

(1)利用 tar zxvf Boot-XSBase270.tar.gz 指令解压。

[root@localhost BootLoader]$tar zxvf Boot-XSBase270.tar.gz

利用上述命令解压后,bootloader 源代码解压到当前目录中 Boot-XSBase270 文件夹中。 (2)编译。在解压的目录里进行 make 编译。

[root@localhost BootLoader]$ cd Boot-XSBase270 [root@localhost Boot-XSBase270]$make

编译完成后, 在当前目录下会生成 bootloader 映象文件 boot。 2. bootLoader 程序的下载

将 bootloader 的映象文件 boot 拷贝 Jflash-XSBase270 目录下,连好 JTAG 下载器,并利用Jflash-XSBase270 目录中 jflashmm 程序将 bootloader 映象文件 boot 烧写到开发板上。 [root@localhost Boot-XSBase270]$cp boot /root/EmdoorARM/Jflash/Jflash-XSBase270/ [root@localhost Boot-XSBase270]$ cd /root/EmdoorARM/Jflash/Jflash-XSBase270 [root@localhost Boot-XSBase270]$ ./jflashmm boot 3. bootLoader 的使用

Bootloader 命令的使用方法。 help

用法 帮助

描述 简短显示各命令的介绍 参数 无

举例 Bboot> Help Load

用法 load [kernel/ramdisk]

描述 把存放在 FLASH 中的映像文件拷贝到 SDRAM 中。在 Autoboot 过程中会自动运行,把内核映像从 FLASH 加载到 SDRAM 中。 参数 Kernel - 把内核映像从 FLASH 拷贝到 SDRAM 中 Ramdisk- 从 FLASH 中拷贝 RAMDISK 到 SDRAM 举例 Bboot> load kernel bootp

用法 Bootp

描述 运行 bootp 命令用来获取 HOST 主机发送的 BOOTP 的数据包,解析 BOOTP 的数据包,获取本机的 IP 地址 tftp

用法 Tftp 文件名 {address/loader/kernel/root/ramdisk} 描述 通过以太网下载主机的数据或文件到目标平台的 SDRAM 参数 文件名 - 主机平台需要传输的文件名

loader - 把传输到目标平台的文件放置在 SDRAM 的 loader 区域 kernel - 把传输到目标平台的文件放置在 SDRAM 的 kernel 区域 root - 把传输到目标平台的文件放置在 SDRAM 的 root 区域 ramdisk - 把传输到目标平台的文件放置在 SDRAM 的 ramdisk 区域 address - 把传输到目标平台的文件放置在 SDRAM 的指定地址 举例 Bboot> tftp zImage kernel flash

用法 Flash {loader/kernel/root/ramdisk}

描述 把 SDRAM 中的数据烧录到 FLASH 中特定的地址

参数 loader - 把 SDRAM 中的数据烧录到 FLASH 中的 loader 区域 kernel – 把 SDRAM 中的数据烧录到 FALSH 中的 kernel 区域 root – 把 SDRAM 中的数据烧录到 FLASH 中的 root 区域

ramdisk – 把 SDRAM 中的数据烧录到 FLASH 中的 ramdisk 的区域 举例 Bboot> flash kernel erase

用法 erase {loader/kernel/ramdisk/root} 描述 擦除 FALSH 中的相应区域

参数 loader – 擦除 FALSH 中 loder 区域 kernel – 擦除 FLASH 中的 kernel 区域 root – 擦除 FLASH 中的 root 区域

ramdisk – 擦除 FLASH 中的 ramdisk 区域 举例 bboot> erase kernel

实验六 Linux内核编译实验

【实验目的】

1、了解 Linux 内核源代码的目录结构及各目录的相关内容 2、了解 Linux 内核各配置选项内容和作用

3、掌握 Linux 内核配置文件 config.in 的作用 4、掌握 Linux 内核的编译过程

5、掌握将新增内核代码加入到 Linux 内核结构中的方法 【实验原理】

1.内核源代码目录介绍

Linux 内核源代码可以从网上下载(http://www.kernel.org/pub/linux/v2.4)。一般主机平台的 Linux (如红旗 Linux)源代码在根目录下的/usr/src/linux 目录下。内核源代码的文件按树形结 构进行组织的,在源代码树最上层的可以看到如下的一些目录: (1)、arch:arch 子目录包括所有与体系结构相关的内核代码。arch 的每一个子目录都 代表一个 Linux 所支持的体系结构。例如:arm 目录下就是 arm 体系架构的处理器目录,包 含我们使用的 PXA 处理器。 (2)、include:include 子目录包括编译内核所需要的头文件。与 ARM 相关的头文件 在 include/asm-arm 子目录下。 (3)、init:这个目录包含内核的初始化代码,但不是系统的引导代码,其中所包含 main.c 和 Version.c 文件是研究 Linux 内核的起点。 (4)、mm:该目录包含所有独立于 CPU 体系结构的内存管理代码,如页式存储管理 内存的分配和释放等。与 ARM 体系结构相关的代码在 arch/arm/mm 中。 (5)、Kernel:这里包括主要的内核代码,此目录下的文件实现大多数 Linux 的内核函 数,其中最重要的文件是 sched.c。与 Xscale 体系结构相关的代码在 arch/arm-pxa/kernel 目录 中。 (6)、Drives:此目录存放系统所有的设备驱动程序,每种驱动程序各占一个子目录。 (a)、/block:块设备驱动程序。块设备包括 IDE 和 scsi 设备。 (b)、/char:字符设备驱动程序。如串口、鼠标等。 (c)、/cdrom:包含 Linux 所有的 CD-ROM 代码。 (d)、/pci:PCI 卡驱动程序代码,包含 PCI 子系统映射和初始化代码等。 (e)、/scsi:包含所有的 SCSI 代码以及 Linux 所支持的所有的 SCSI 设备驱动程序代码。 (f)、/net:网络设备驱动程序。 (g)、/sound:声卡设备驱动程序。 (7)、lib 目录放置内核的库代码; (8)、net 目录包含内核与网络的相关的代码; (9)、ipc 目录包含内核进程通信的代码; (10)、fs 目录是所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录 支持一个文件系统,如 JFFS2; (11)、scripts 目录包含用于配置内核的脚本文件等。每个目录下一般都有 depend 文件 和一个 makefile 文件,他们是编译时使用的辅助文件,仔细阅读这两个文件对弄清各个文件 之间的相互依托关系很有帮助。

2.内核的配置的基本结构

Linux 内核的配置系统由四个部分组成 (1)、Makefile:分布在 Linux 内核源码中的 Makefile,定义 Linux 内核的编译规则; 顶层 Makefile 是整个内核配置、编译的总体控制文件;

(2)、配置文件(config.in):给用户提供配置选择的功能;.config:内核配置文件,包 括由用户选择的配置选项,用来存放内核配置后的结果; (3)、配置工具:包括对配置脚本中使用的配置命令进行解释的配置命令解释器和配置 用户界面(基于字符界面:make config;基于 Ncurses 图形界面:make menuconfig;基于 xWindows 图形界面:make xconfig) (4)、Rules.make:规则文件,被所有的 Makefile 使用。 3.编译内核的常用命令

精简 Linux 内核常用命令包括:Make Config,dep,clean,mrproper,zImage,bzImage,Modules,Modules_Install (1)、Make config:内核配置,调用./scripts/Configure 按照 arch/i386/config.in 来进行 配置。命令执行后产生文件.config,其中保存着配置信息。下次在做 make config 时将产生 新的.config 文件,原文件 config 更名为 config.old (2)、 make dep:寻找依存关系。产生两个文件. depend 和.hdepend,其中.hdepend 表 示每个.h 文件都包含其他哪些嵌入文件。而.depend 文件有多个,在每个会产生目标文件(.o) 文件的目录下均有,它表示每个目标文件都依赖于哪些嵌入文件(.h)

(3)、make clean:清除以前构核所产生的所有的目标文件,模块文件,核心以及一些 临时文件等,不产生任何文件

(4)、 make rmproper:删除所有以前在构核过程所产生的所有文件,及除了做 make clean 外,还要删除.config,.depend 等文件,把核心源码恢复到最原始的状态。下次构核时必须进行重新配置; (5)、make,make zImage, make bzImage: (a)、make:构核。通过各目录的 Makefile 文件进行,会在各个目录下产生一大堆目 标文件,如核心代码没有错误,将产生文件 vmlinux,这就是所构的核心。并产生映像文件 system.map 通过各目录的 makefile 文件进行。.version 文件中的数加 1,表示版本号的变化。

(b)、make zImage:在 make 的基础上产生压缩的核心映像文件./arch/$(ARCH)/boot/zImage以及./arch/$(ARCH)/boot/compressed目录下产生一些临时文件。

(c)、make bzImage:在make的基础上产生压缩比例更大的核心映像文件./arch/$(ARCH)/ boot/bzImage以及./arch/$(ARCH)/boot/compressed 目录下产生一些临时文件。 4.内核编译过程

(1)make mrproper:删除所有以前在构核过程所产生的所有文件 (2)make menuconfig:内核配置 (3)make dep:寻找依存关系

(4)make zImage:产生压缩的核心映像文件

内核编译完毕之后,生成 zImage 内核映象文件保存在源代码的 arch/arm/boot/目录下。 5.内核配置项介绍

首先切换 linux 源代码所在的目录,并终端输入 make menuconfig,系统弹出基于 Ncurses 内核配置图形界面,便可进行内核选项的配置。 [root@]$make menuconfig 6.添加驱动到 linux 内核

对于一个开发者来说,将自己开发的内核代码添加到Linux内核中,需要有三个步骤。 (1)确定把自己的开发代码放入到内核的位置;

(2)把自己开发的功能增加到Linux内核的配置选项中,使用户能够选择此功能; (3)构建子目录 Makefile,根据用户的选择,将相应的代码编译 Linux 内核中。 【实验仪器】

1、装有 Linux 操作系统的 PC 机一台;

2、XSBase270 或 XSBase255 ARM 实验开发平台一套 【实验内容】

(1)利用光盘上提供的 Linux 源代码,具体分析递归性 makefile 的结构,画出一个从最顶层 makefile 到最底层所经过的文件路径和所需要的变量。

(2)利用光盘上提供的 Linux 源代码,写出编译 Linux 内核的具体过程。

(3)利用光盘上提供的 Linux 源代码,然后用 make menuconfig 命令对内核的配置进行修改,记下具体修改的配置选项并保存退出,然后用diff命令比较.config 和.oldconfig.old两个新旧配置的差别,同时与记下的配置选项进行比较,根据比较结果,写出你的结论。

实验七 驱动程序结构实验

[实验目的]

1、了解Linux 驱动程序的结构

2、掌握Linux 驱动程序常用结构体和操作函数的使用方法 3、初步掌握Linux 驱动程序的编写方法及过程 4、掌握Linux 驱动程序的加载方法 [实验原理]

1.驱动程序介绍

驱动程序负责将应用程序如读、写等操作正确无误的传递给相关的硬件,并使硬件能够做出正确反应的代码。驱动程序像一个黑盒子,它隐藏了硬件的工作细节,应用程序只需要通过一组标准化的接口实现对硬件的操作。

2.Linux设备驱动程序分类

Linux 设备驱动程序在Linux 的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。虽然Linux 内核的不断升级,但驱动程序的结构还是相对稳定。Linux 系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。字符设备是指在存取时没有缓存的设备,而块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access)。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘设备,CD-ROM等。网络设备在Linux 里做专门的处理。Linux 的网络系统主要是基于BSD unix 的socket 机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据传递。系统有支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。 3.驱动程序的结构

应用程序经过系统调用,进入核心层,内核要控制硬件需要通过驱动程序实现,驱动程序相当于内核与硬件之间的“系统调用”。 内核模块

内核模块是Linux 内核的重要组成要素,内核模块能在Linux系统启动之后能够动态进行装载和卸载,因此不需对内核进行重新编译或重启系统就可将内核的一部分替换掉,Linux 内核的所有设备驱动,文件系统,网络协议等可做成模块的形式来提供。在所有的模块中需记录编译的内核版本信息,并与当前执行的内核版本一致。即,模块具有版本依赖性,如果不一样就会出错,当然可以在模块程序中的include之前通过宏定义#define__NO_VERSION__表明不定义模块的版本信息。 内核模块程序与一般应用程序之间主要不同之处是,模块程序没有main()函数,模块程序在装载时调用init_module(void)函数添加到内核中,在卸载时调用voidcleanup_module( )函数从内核中卸载。另外一个应用程序从头到尾只执行一个任务,但一个模块可以把响应未来请求的事务登记到内核中,然后等待系统调用。

init_module( ) register_capability( ) printk( ) . . . . .

cleanup_module( ) unregister_capability( ) insmod rmmod

Module Kernel capabilities[]

图 7-2内核模块程序结构 4.主、从设备号

应用程序通过设备文件系统(devfs)的名字(或节点)访问硬件设备,所有的设备节点在/dev目录下。利用mknod命令生成设备文件系统的节点,但只有超级用户才能生成设备文。Mknod 命令必须要有设备名和设备类型,主设备号(Major Number),次设备号(Minor Number)等3 个参 数。主设备号用于内核区分设备驱动,次设备号用于设备驱动区分设备。一个设备驱动可能控制多个设备。新的设备驱动要有新的主设备号。在内核源代码的Documentation /devices.txt中定义了所有设备的主设备号。在创建设备的时候不要与常用的设备好冲突。

下面/dev/hda1 是设备名,b 表示block设备(c表示字符设备)。127是主设备号,1是次设备号。次设备号可以是0 – 255 之间的值,限制为8bit。 [root@localhost]$ mknod /dev/hda1 b 127 1 [root@localhost]$ ls –al /dev/hda1

[root@localhost]$ brw-rw---- 1 root disk 3 1 Mar 25 12:00 /dev/hda1 5.驱动程序基本框架

如果采用模块方式编写设备驱动程序时,通常至少要实现设备初始化模块、设备打开模块、数据读写与控制模块、中断处理模块(有的驱动程序没有)、设备释放模块和设备卸载模块等几个部分。下面给出一个典型的设备驱动程序的基本框架。,从中不难体会到这几个关键部分是如何组织起来的。 /* 打开设备模块 */

static int xxx_open(struct inode *inode, struct file *file) {

/*????*/ }

/* 读设备模块 */

static int xxx_read(struct inode *inode, struct file *file) {

/*????*/ }

/* 写设备模块 */

static int xxx_write(struct inode *inode, struct file *file) {

/*????*/ }

/* 控制设备模块 */

static int xxx_ioctl(struct inode *inode, struct file *file) {

/*????*/ }

/* 中断处理模块 */

static void xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) {

/* ... */ }

/* 设备文件操作接口 */

static struct file_operations xxx_fops = { read: xxx_read, /* 读设备操作*/ write: xxx_write, /* 写设备操作*/ ioctl: xxx_ioctl, /* 控制设备操作*/ open: xxx_open, /* 打开设备操作*/

release: xxx_release /* 释放设备操作*/ /* ... */ };

static int __init xxx_init_module (void) {

/* ... */ }

static void __exit demo_cleanup_module (void) {

pci_unregister_driver(&demo_pci_driver); }

/* 加载驱动程序模块入口 */ module_init(xxx_init_module); /* 卸载驱动程序模块入口 */

module_exit(xxx_cleanup_module);

6、重要结构体

打开的设备在内核内部由file 结构标识,内核使用file_operation 结构访问驱动程

序函数。file_operation 结构是一个定义在中的函数指针数组。每个文件都 与它自己的函数集相关联。这个结构中的每一个字段都必须指向驱动程序中实现特定 操作的函数。结构如下,详细内容可查阅相关文档。 File结构

File 结构代表一个打开的文件。它在open 时被内核创建,并传递给在该文件上进行 操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会 释放这个数据结构。

结构在/linux/fs.h定义,详细内容查阅相关文档。 struct file_operations { struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

};

驱动程序中常用的函数 [实验仪器]

1、装有Linux 操作系统的PC机一台;

2、XSBase270或XSBase255 ARM实验开发平台一套 [实验内容]

1、dri_arh模块加载实验 编写实验代码

#include #include #include #include #include

int xxx_open(struct inode *inode, struct file *filp); int xxx_release(struct inode *inode, struc file *filp);

ssize_t xxx_read(struct file *filp, char *buff,size_t count, loff_t *offp);

ssize_t xxx_write(struct file *filp, const char *buff, size_t count , loff_t *offp); ??

struct file {

struct list_head f_list; struct dentry *f_dentry; struct vfsmount *f_vfsmnt; struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos;

unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; struct fown_struct f_owner; unsigned int f_uid, f_gid; int f_error;

unsigned long f_version;

/* needed for tty driver, and maybe others */ void *private_data;

/* preallocated helper kiobuf to speedup O_DIRECT */ struct kiobuf *f_iobuf; long f_iobuf_lock; };

#include

static int __init dri_arch_init_module(void) {

printk(\return 0; }

static void __exit dri_arch_cleanup_module(void) {

printk(\}

module_init(dri_arch_init_module); module_exit(dri_arch_cleanup_module); 编译make

使用Makefile文件

CC =/opt/xscalev1/bin/arm-linux-gcc

INCLUDEDIR = /root/xsbase/Xsbase270_Linux_F/Kernel/linux-2.4.21-emdoor/include CFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 CFLAGS += -I.. -I$(INCLUDEDIR) DEBUG =

TARGET = dri_arch OBJS = $(TARGET).o SRC = dri_arch.c All: dri_arch.o

dri_arch.o: dri_arch.c

$(CC) $(CFLAGS) $(DEBUG) -c -o dri_arch.o dri_arch.c clean : rm -rf *.o

下载目标代码到目标板,具体的操作参考使用手册相关部分

挂载目标代码,并查看输出调试信息,本次相关操作均要求到下载的当前目录(含 dri_arch.o)

$insmod dri_arch.o (挂载dri_arch)

$lsmod (查看当前已挂载模块,会看到dri_arch)

$dmesg (查看模块输出信息:This is a simple driver-module!) $rmmod dri_arch (卸载dri_arch)

$lsmod (查看当前已挂载模块,不再看到dri_arch) $dmesg (查看模块输出信息:Goodbye driver-module!) [习题与思考题]

1、 分析如何解决模块加载过程中的内核版本的兼容问题

2、 任选某个linux 内核设备驱动程序进行分析,总结编写设备驱动程序的架构。

本文来源:https://www.bwwdw.com/article/a0zd.html

Top