Linux实现驱动模块传参过程解析_Linux

来源:脚本之家  责任编辑:小易  

按照《2113linux设备驱动开发详解》一书中的步5261骤实现经典例子"hello,4102world!"的例子。具体步骤如下1653:=============================================1.源码如下:/** hello.c -- the example of printf "hello world!" in the screen of driver program*/#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");/* declare the license of the module ,it is necessary */static int hello_init(void){ printk(KERN_ALERT "Hello World enter!\n"); return 0;}static int hello_exit(void){ printk(KERN_ALERT "Hello world exit!\n");}module_init(hello_init); /* load the module */module_exit(hello_exit); /* unload the module */进入目录:[root@Alex_linux /]#cd /work/jiakun_test/moduletest[root@Alex_linux moduletest]# vi hello.c然后拷入上面书上的源码。2.编译代码:1>.首先我在2.4内核的虚拟机上进行编译,编译过程如下:[root@Alex_linux moduletest]#gcc -D__KERNEL__ -I /usr/src/linux -DMODULE -Wall -O2 -c -o hello.o hello.c其中-I选项指定内河源码,也就是内核源码树路径。编译结果:hello.c:1:22: net/sock.h: No such file or directoryhello.c: In function `hello_init':hello.c:6: warning: implicit declaration of function `printk'hello.c:6: `KERN_ALERT' undeclared (first use in this function)hello.c:6: (Each undeclared identifier is reported only oncehello.c:6: for each function it appears in.)hello.c:6: parse error before string constanthello.c: In function `hello_exit':hello.c:11: `KERN_ALERT' undeclared (first use in this function)hello.c:11: parse error before string constanthello.c: At top level:hello.c:13: warning: type defaults to `int' in declaration of `module_init'hello.c:13: warning: parameter names (without types) in function declarationhello.c:13: warning: data definition has no type or storage classhello.c:14: warning: type defaults to `int' in declaration of `module_exit'hello.c:14: warning: parameter names (without types) in function declarationhello.c:14: warning: data definition has no type or storage class在网上查询有网友提示没有引入kernel.h解决:vi hello.c在第一行加入:#include <linux/kernel.h>再次编译仍然报KERN_ALERT没有声明修改编译条件-I,再次编译:[root@Alex_linux moduletest]#gcc -D__KERNEL__ -I /usr/src/linux -DMODULE -Wall -O2 -c -o hello.o hello.c[root@Alex_linux moduletest]#lshello.c hello.o Makefile[root@Alex_linux moduletest]#2>.接着我尝试在2.6内核的虚拟机上进行编译编译过程如下:[root@JiaKun moduletest]# lshello.c makefile[root@JiaKun moduletest]# vi hello.c[root@JiaKun moduletest]# makemake -C /mylinux/kernel/2.4.18-rmk7 M=/home/alex/test/moduletest modulesmake: *** /mylinux/kernel/2.4.18-rmk7: No such file or directory. Stop.make: *** [modules] Error 2[root@JiaKun moduletest]# vi makefile [root@JiaKun moduletest]# makemake -C /usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modulesmake[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'scripts/Makefile.build:17: /home/alex/test/moduletest/Makefile: No such file or directorymake[2]: *** No rule to make target `/home/alex/test/moduletest/Makefile'. Stop.make[1]: *** [_module_/home/alex/test/moduletest] Error 2make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'make: *** [modules] Error 2[root@JiaKun moduletest]# mv makefile Makefile[root@JiaKun moduletest]# makemake -C /usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modulesmake[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686' CC [M] /home/alex/test/moduletest/hello.o Building modules, stage 2. MODPOST CC /home/alex/test/moduletest/hello.mod.o LD [M] /home/alex/test/moduletest/hello.komake[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'[root@JiaKun moduletest]# lshello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile Module.symverswww.zgxue.com防采集请勿采集本网。

在驱动的模块中声明一下你要传递的参数名称,类型和权限

如何编写Linux设备驱动程序 回想学习Linux操作系统已经有近一年的时间了,前前后后,零零碎碎的一路学习过来,也该试着写的东西了。也算是给自己能留下一点

module_param(变量的名称,类型,权限);

应该下载与系统中安装的rpm包相同版本的源代码,再进行ko的编译。 因为下载其他版本的源代码可能比系统中的代码新,或者旧,都有可能引入其他问题。 模块的

#include <linux/init.h>#include <linux/module.h>static char *p_name = "Usr";module_param(p_name, charp, S_IRUGO);MODULE_PARM_DESC(p_name, "This is a char * string.");static int __init hi_init(void){ printk(KERN_INFO "Hi enter %s\n",p_name); return 0;}module_init(hi_init);static void __exit hi_exit(void){ printk(KERN_INFO "Hi exit %s\n ",p_name);}module_exit(hi_exit);MODULE_AUTHOR(" libra13179 ");MODULE_LICENSE("GPL v2");

一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽

KVERS = $(shell uname -r)# Kernel modulesobj-m += hi.o# Specify flags for the module compilation.#EXTRA_CFLAGS=-g -O0build: kernel_moduleskernel_modules: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules# @echo $(KVERS)clean: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

很多情况下,我们期望通过参数来控制我们的驱动的行为,比如由于系统的不同,而为了保证我们驱动有较好的移植性,我们有时候期望通过传递参数来控制我们驱动的

不设置时候使用默认的

以装载和卸载模块为例: 1、首先输入代码 #include #include 2、然后输入下方的代码: static int my_init(void)

root@lin-virtual-machine:/home/lin/hi# insmod ./hi.koroot@lin-virtual-machine:/home/lin/hi# cat /var/log/syslog | grep Hi...Jul 25 15:11:38 lin-virtual-machine kernel: [ 203.238178] Hi enter Usr...

使用设置时候

root@lin-virtual-machine:/home/lin/hi# insmod hi.ko p_name='God'

加载模块后,都会在/sys/modules下生成一个模块的文件夹,文件夹下会有一个parameters文件夹,

里面包含的就是以参数名命名的文件节点,里面保存的就是我们设置的值,例如在p_name中

root@lin-virtual-machine:/home/lin/hi# cat /sys/module/hi/parameters/p_name

God

一、Linux device driver 的概念  系统调用是2113操作系统内核和应5261用程序之间的接口,设备驱4102动程序是操作系统内核和机器硬件之1653间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能:  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 = 0; i < 10;i++)  printf("%d\n",buf[i]);  close(testdev);  }  编译运行,看看是不是打印出全1   以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。上述给出了一个简单的字符设备驱动编写的框架和原理,更为复杂的编写需要去认真研究LINUX内核的运行机制和具体的设备运行的机制等等。希望大家好好掌握LINUX设备驱动程序编写的方法,这个才1W字 难度太大2113。。。 而且说得明白都可5261以出书了。。。大体上就是挂载到操作4102系统的1653抽象接口。驱动是以模块化加载到操作系统中,首先init()函数启动模块,就是到相应的硬件驱动抽象接口注册,并进行一系列的抽象类的交互,如果是总线,则要向更高级的总线挂载,如PCI,没有2.4内核时代那么简单了哦,之后就是probe(),这个过程是在你要使用的时候启动的,是驱动程序和驱动设备互相绑定的一个过程,也就是特定的设备都会有相应的驱动来绑定,之后就是什么read,write,ioctl等一系列的操作,这些都是具体的硬件操作,但是必须给操作系统提供统一接口就是那些read,write。。来进行硬件封装。这些read,write,ioctl就是具体要参照芯片的datesheet操作而且要参考具体芯片的特性,例如nandflash,norflash,cfcard,sdcard每个都不一样。就如我们写裸机程序一样操作这些硬件,如果要加载操作系统,我们则必须要按照一定的规范写。吃饭了 就写这么多。。。本回答被网友采纳内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • apache实现禁止中文浏览器访问与301重定向的方法
  • linux负载均衡总结性说明 四层负载和七层负载有什么区别
  • linux deb包解压、修改等操作方法代码示例
  • 帮助你排序文本文件的 awk 命令行或脚本(推荐)
  • ubuntu系统下matplotlib中文乱码问题的解决方法
  • linux环境搭建之安装/配置tomcat的方法
  • linux zabbix自定义监控及报警实现过程解析
  • 通过配置.htaccess文件实现子目录绑定二级域名的方法
  • linux系统下oracle11gr2静默安装的经验分享
  • ubuntu配置tftp服务的步骤小结
  • 解释一下linux驱动程序结构框架及工作原理
  • 如何编译一个linux下的驱动模块
  • linux驱动select驱动实现中参数传递的问题
  • 如何编译linux驱动模块
  • 如何编写一个简单的linux内核模块和设备驱动程序
  • 关于linux驱动模块.ko的编写问题
  • linux驱动程序结构框架及工作原理分别是什么?
  • 什么是Linux设备驱动程序代码module
  • 如何编写Linux 驱动程序
  • linux加载驱动模块
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全星外虚拟主机华众虚拟主机linuxwin服务器ftp服务器dns服务器tomcat nginxzabbix云和虚拟化服务器其它首页服务器linux内核设备驱动之内存管理笔记整理linux内核设备驱动之内核的时间管理笔记整理linux内核设备驱动之字符设备驱动笔记整理linux内核设备驱动之虚拟文件系统笔记整理linux内核设备驱动之内核的调试技术笔记整理linux内核设备驱动之内核中链表的使用笔记整理linux内核设备驱动之proc文件系统笔记整理详解linux 摄像头驱动编写apache实现禁止中文浏览器访问与301重定向的方法linux负载均衡总结性说明 四层负载和七层负载有什么区别linux deb包解压、修改等操作方法代码示例帮助你排序文本文件的 awk 命令行或脚本(推荐)ubuntu系统下matplotlib中文乱码问题的解决方法linux环境搭建之安装/配置tomcat的方法linux zabbix自定义监控及报警实现过程解析通过配置.htaccess文件实现子目录绑定二级域名的方法linux系统下oracle11gr2静默安装的经验分享ubuntu配置tftp服务的步骤小结apache开启.htaccess及.htaccessservice temporarily unavailabllinux下实现免密码登录(超详细)apache rewrite url重定向功能的linux下用cron定时执行任务的方法apache性能测试工具ab使用详解centos 6.4安装配置lamp服务器(a阿里云服务器ping不通解决办法(centos+nginx+php+mysql详细配置apache you don""t have permisslinux下因为系统编码问题造成乱码的快速解记一次linux服务器入侵应急响应(小结)linux 配置静态ip的方法通过配置.htaccess文件实现子目录绑定二级apache no space left on device: mod_relinux系统下使用rpm方式安装最新mysql5.7详解ubuntu安装cmake的几种方式linux关于透明大页的使用与禁用介绍linux下tcpdump命令解析及使用详解centos 8.0.1905 安装 zabbix4.4版本 (已
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved