解释一下linux驱动程序结构框架及工作原理

解释一下linux驱动程序结构框架及工作原理,第1张

一、Linux device driver 的概念

系统调用是 *** 作系统内核和应用程序之间的接口,设备驱动程序是 *** 作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象 *** 作普通文件一样对硬件设备进行 *** 作。设备驱动程序是内核的一部分,它完成以下的功能:

1、对设备初始化和释放;

2、把数据从内核传送到硬件和从硬件读取数据;

3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;

4、检测和处理设备出现的错误。

在Linux *** 作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O *** 作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。

已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。

最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。

二、实例剖析

我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。

由于用户进程是通过设备文件同硬件打交道,对设备文件的 *** 作方式不外乎就是一些系统调用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:

STruct file_operatiONs {

int (*seek) (struct inode * ,struct file *, off_t ,int)

int (*read) (struct inode * ,struct file *, char ,int)

int (*write) (struct inode * ,struct file *, off_t ,int)

int (*readdir) (struct inode * ,struct file *, struct dirent * ,int)

int (*select) (struct inode * ,struct file *, int ,select_table *)

int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long)

int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *)

int (*open) (struct inode * ,struct file *)

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

int (*fsync) (struct inode * ,struct file *)

int (*fasync) (struct inode * ,struct file *,int)

int (*check_media_change) (struct inode * ,struct file *)

int (*revalidate) (dev_t dev)

}

这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write *** 作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。

下面就开始写子程序。

#include <linux/types.h>基本的类型定义

#include <linux/fs.h>文件系统使用相关的头文件

#include <linux/mm.h>

#include <linux/errno.h>

#include <asm/segment.h>

unsigned int test_major = 0

static int read_test(struct inode *inode,struct file *file,char *buf,int count)

{

int left用户空间和内核空间

if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )

return -EFAULT

for(left = count left >0 left--)

{

__put_user(1,buf,1)

buf++

}

return count

}

这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf 是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf是否可用。这就用到函数verify_area。为了验证BUF是否可以用。

static int write_test(struct inode *inode,struct file *file,const char *buf,int count)

{

return count

}

static int open_test(struct inode *inode,struct file *file )

{

MOD_INC_USE_COUNT模块计数加以,表示当前内核有个设备加载内核当中去

return 0

}

static void release_test(struct inode *inode,struct file *file )

{

MOD_DEC_USE_COUNT

}

这几个函数都是空 *** 作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。

struct file_operations test_fops = {?

read_test,

write_test,

open_test,

release_test,

}

设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。

int init_module(void)

{

int result

result = register_chrdev(0, "test", &test_fops)对设备 *** 作的整个接口

if (result <0) {

printk(KERN_INFO "test: can't get major number\n")

return result

}

if (test_major == 0) test_major = result/* dynamic */

return 0

}

在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行 *** 作的函数的指针。

如果登记成功,返回设备的主设备号,不成功,返回一个负值。

void cleanup_module(void)

{

unregister_chrdev(test_major,"test")

}

在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test在系统字符设备表中占有的表项。

一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。

下面编译 :

$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c –c表示输出制定名,自动生成.o文件

得到文件test.o就是一个设备驱动程序。

如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后

ld ?-r ?file1.o ?file2.o ?-o ?modulename。

驱动程序已经编译好了,现在把它安装到系统中去。

$ insmod ?–f ?test.o

如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。要卸载的话,运行 :

$ rmmod test

下一步要创建设备文件。

mknod /dev/test c major minor

c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。

用shell命令

$ cat /proc/devices

就可以获得主设备号,可以把上面的命令行加入你的shell script中去。

minor是从设备号,设置成0就可以了。

我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

main()

{

int testdev

int i

char buf[10]

testdev = open("/dev/test",O_RDWR)

if ( testdev == -1 )

{

printf("Cann't open file \n")

exit(0)

}

read(testdev,buf,10)

for (i = 0i <10i++)

printf("%d\n",buf[i])

close(testdev)

}

编译运行,看看是不是打印出全1

以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。上述给出了一个简单的字符设备驱动编写的框架和原理,更为复杂的编写需要去认真研究LINUX内核的运行机制和具体的设备运行的机制等等。希望大家好好掌握LINUX设备驱动程序编写的方法。

/ 根目录。Linux文件系统的最底层目录。/etc 服务主配置文件目录,像apache网站服务、postfix电子邮局服务等等各种服务的主配置文件全部放在这个目录里。/var 服务运行调用文件和输出文件的存放目录,如mysql数据库服务运行时,库文件存放的目录就在/var下。/boot 系统启动文件存放的目录/root 系统超级管理员用户根目录。root用户登录之后就在这个目录。/home 系统普通用户根目录。所有的普通用户在创建时默认都会在/home下创建相应的根目录,目录名字就是用户名字。/dev 系统认到的所有硬件设备的印象文件和2进制配置文件都在这里/mnt 系统挂载外部文件系统的入口目录,比如光驱,每次放入新的光盘(外部文件系统),要进行挂载才能进入光盘内读取资料,进入光盘就是从/mnt下的cdrom目录进来的,那个cdrom就是光盘的入口。/usr 各种应用程序的安装目录,像windows下的program files

安装之前请确认a. 管理员账号权限登录;b. 关闭防火墙;c. 设置固定IP。设置成自动获取IP会导致流媒体服务器系统不能正常工作;

d. 确认 *** 作系统为CentOS或Redhat。

安装方法如下:

1. 解压 800LiMediaServer-3.0-setup.tar.gz 到非 /usr/local下的任意目录

2. 打开终端, 然后在终端里面把目录切换到你的解压目录中. (及切换到install文件所在的文件夹)

3. 终端里面输入 ./install

根据安装提示设置相应的参数:

分别为:

设置本机(本服务器)的ip地址或者域名

设置后台web管理服务器端口

设置http串流服务器端口

设置rtmp服务器端口

设置rtmfp服务器端口awrthe

设置后台web管理数据库端口

设置内部服务器程序通信端口

设置web管理系统的管理员密码

重复

设置web管理系统的超级管理员密码

重复

请保证您所设置的端口全部可用,并加入到防火墙允许列表中

设置结束后,自行配置服务器运行参数,并启动服务

打开并通过浏览器访问 http://本机的ip地址:web管理服务器端口,如:http://192.168.1.160:8085

附例子:

a. 解压缩在linux系统里。 (以把安装包解压缩到桌面为例)

b. 打开终端

c. 输入cd 桌面 然后回车

d. 输入cd 800LiMediaServer-3.0 然后回车

e. 输入 ./install 然后回车

等待创建系统账户,然后进行系统配置,默认情况下,确认的话,直接点击回车即可。

f.设置管理员及超管密码。 输入时候,屏幕不会显示,输入完成后,直接点击回车即可。

g. 软件安装后,软件的安装目录为 /user/local/800li-media-server


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

原文地址:https://54852.com/yw/7254571.html

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

发表评论

登录后才能评论

评论列表(0条)

    保存