问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

linux驱动程序结构框架及工作原理分别是什么?

发布网友 发布时间:2022-04-24 20:18

我来回答

1个回答

热心网友 时间:2023-10-09 15:52

一、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,另一种是编译成模块(moles),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。

  int init_mole(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_mole 函数被调用。在这里,init_mole只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。

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

  void cleanup_mole(void)

  {

  unregister_chrdev(test_major,"test");

  }

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

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

  下面编译 :

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

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

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

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

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

  $ 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 = 0; i < 10;i++)

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

  close(testdev);

  }

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

  以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。上述给出了一个简单的字符设备驱动编写的框架和原理,更为复杂的编写需要去认真研究LINUX内核的运行机制和具体的设备运行的机制等等。希望大家好好掌握LINUX设备驱动程序编写的方法。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
在筋骨堂治疗腰椎间盘突出一个疗程后已有好转,隔了三、四天又疼上了... 美的3⃣️匹柜机空调为什么不制热是什么原因? 小票打印机不出字的原因及解决方法如何解决小票打印机无法打印字的问题... 电脑打单打印机出不了小票超市收银台电脑键盘怎么用 交易房屋如何分类 城市规划用途分类该怎么做 规划用途的分类 ...打火机散件设备公司 值得信赖么 ?是不是《骗子》皮包公司? 希望给予... 哈尔滨农垦太阳神打火机散件厂是否骗人 有没有人知道网上那些打火机加工的广告是不是真是可靠?做打火机反销... 关于linux系统驱动 第三者责任险理赔是全部报销吗要注意什么不 交通事故三者险怎么赔偿标准 我注册了货拉拉,但没有拉货的情况下出车祸了,第三者保险公司赔付吗? 出车祸第三者责任险怎么赔偿 我买了第三者100万不计免赔,出事故我全责,保险公司赔付吗? 买了三者险不计免赔,交通事故我全责,是不是由保险公司理赔对方看病的全部费用?车损也买了会赔修车费吗 假如开父亲的车出了事故,第三者保险给赔吗?希望详细说明一下 交通事故中我负主要责任,有强制险和第三者责任险,保险会全赔吗 借朋友的车出了交通事故,第三者商业险应该赔偿吗? 一般第三责任保险是按事故划分的责任赔偿还是保险公司全赔? 开别人车出事故了,第三者保险管赔偿吗? 2021年如果没有买车损险,就相当于没有不计免赔,那么如果出了交通事故,三者险会全额赔付对方吗? 恒大人寿保险位列第几保险公司第几名? ios10.1.1和ios10.2.1哪个版本最好 ip地址10.1.1.8是什么意思?我通过网口连接设备,每次要把ip改成10.1.1.8才能找到设备?为什么? 10.1.1.0 10.1.3.0 10.1.9.0聚合 人寿保险前十名排名 一百米记录是多少? 10.10和10.1哪个大? linux的声卡驱动还有网卡驱动,用不用驱动盘。 liunx自带有吗? linux下要什么驱动?? linux系统自带驱动程序吗?有的话一般是那些?QQ在这个系统上面能正常运行吗 Linux驱动的软件架构? linux驱动哪里最全? 所有的硬件设备都有适用于Linux的驱动程序吗? Linux 驱动 linux 驱动程序有几种?有什么区别 Linux驱动程序有几种加载方式?以及它们之间的区别? 最后一战,法国VS克罗地亚,两支队伍谁会获得冠军 法国队VS克罗地亚队都有谁进球? 对方欠钱多少可以起诉 2018世界杯是首次启用视频助理裁判吗? 别人欠钱不还最低多少钱可以起诉他 请教CCTV-5和CCTV--1每天&quot;重播&quot;世界杯的时间? 多少钱可以起诉欠款人? 世界杯重播的时间是何时?不想熬夜 哪里能找到CCTV-5直播和重播世界杯比赛的时间表? 中央五除了晚上现场直播,白天重播怎么安排? 哪个网站随时都可以看世界杯重播啊?