Linux内核模块和驱动的编写

Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。从而可以动态的想内核中添加或者删除模块。

  模块不被编译在内核中,因而控制了内核的大小.然而模块一旦被插入内核,他就和内核其他部分一样.这样一来就会曾家一部分系统开销。同时,如果模块出现问题,也许会带来系统的崩溃。

  模块的实现机制:

  启动时,由函数 void inti_modules() 来初始化模块,因为启动事很多时候没有模块.这个函数往往把内核自身当作一个虚模块。

  如由系统需要,则调用一系列以sys 开头的函数,对模块进行操作. 如:

  sys_creat_modules(),sys_inti_modules() ,
  sys_deldte_modules()等等.

  这里会用到一些模块的数据就结构,在/usr/scr/Linux/include/Linux/module.h 中,有兴趣的朋友可以找出来一看块的加入有两种方法:一是手动加入:如:insmod modulename.另一种是根据需要,动态的加载模块:如你执行命令:

  $mount -t msdos /dev/hdd /mnt/d 时.系统便自动加载 FAT模块,以支持MSDOS的文件系统。

  1.模块编程

  写一个模块,必须有一定的多进程编程基础,因为你变得程序不是以一个独立的程序的来运行的。另外,因为,模块需要在内核模式下运行,会遇到在内和空间和用户空间数据交换的问题.一般的数据复制函数无法完成这一个过程。因此系统已入了一些特殊的函数以用来完成内核空间和用户空间数据的交换/

  这些函数有:void put _user (type valude,type *u_addr)

  memcpy_tofs()

  等等,有兴趣的朋友可以仔细的看看所有的函数,以及他们的用法.需要说明的是.模块编程河内核的版本有很大的关系。如果版本不通可能造成,内核模块不能编译,或者.在运行这个模块时,出现不可测结果。如:系统崩溃等。

  明白了这些以后,你就可以尝试着编写内核模块了。对于每一个内核模块来说,必定包含两个函数int init_module() 这个函数在插入内核时启动,在内核中注册一定的功能函数,或者用他的代码代替内和中某些函数的内容(估计这些函数是空的)。因此,内和可以安全的卸载。

  int cleanup_module() 当内核模块谢载时,调用.将模块从内核中清除.

  同其他的程序设计教程一样 ,我们给出一个hello world 的例子

/*hello.c a module programm*/

/* the program runing under kernel mod and it is a module*/

#include" Linux/kernerl.h"

#include"lLinux/module.h"

/* pross the CONFIG_MODVERSIONS*/

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include""Linux/modversions.h"

#end if

/* the init function*/

int init_module()

{

printk(" hello world !\n');

printd(" I have runing in a kerner");

return 1;

}

/* the distory function*/

int cleanup_module()

{

printk(" I will shut down myself in kernerl mod /n)";

retutn 0;

}

  这样一个例子就完成了.我们也写一个makefile 的例子,以适于我们在大程序重的应用。一下是makfile 文件的内容 。

# a makefile for a module

CC=gcc

MODCFLAGS:= -Wall _DMODULE -D_KERNEL_ -DLinux

hello.o hello.c /usr/inculde?Linux/version.h

CC $(MODCFLAGS) 0c hello.c

echo the module is complie completely

  然后你运行make 命令 得到hello.o 这个模块,运行

$insmod hello.o

hello world!

I will shut down myself in kernerl mod

$lsmod

hello (unused)

….

$remmod

I will shut down myself in kernerl mod

  这样你的模块就可以随意的插入和删除了。

  Linux中的大部分驱动程序,是以模块的形式编写的,这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载。

  一个典型的驱动程序,大体上可以分为这么几个部分:

  1.注册设备

  在系统初启,或者模块加载时候,必须将设备登记到相应的设备数组,并返回设备的主驱动号,例如:对快设备来说调用 refister_blkdec()将设备添加到数组blkdev中,并且获得该设备号,并利用这些设备号对此数组进行索引。对于字符驱动设备来说,要使用 module_register_chrdev()来获得祝设备的驱动号,然后对这个设备的所有调用都用这个设备号来实现。

  2.定义功能函数

  对于每一个驱动函数来说,都有一些和此设备密切相关的功能函数,那最常用的块设备或者字符设备来说,都存在着诸如 open() read() write() ioctrol()这一类的操作。当系统社用这些调用时,将自动的使用驱动函数中特定的模块,来实现具体的操作。而对于特定的设备,上面的系统调用对应的函数是一定的。

  如:在块驱动设备中.当系统试图读取这个设备(即调用read()时),就会运行驱动程序中的block_read() 这个函数。

  打开新设备时会调用这个设备驱动程序的device_open() 这个函数.

3.谢载模块

  在不用这个设备时,可以将他卸载,主要是从/proc 中取消这个设备的特殊文件,可用特定的函数实现。

  下面我们列举一个字符设备驱动程序的框架.来说明这个过程.

/* a module of a character device */

/* some include files*/

#include"param.h"

#include"user.h"

#include"tty.h"

#include"dir.h"

#include”fs.h"

/* the include files modules need*/

#include"Linux/kernel.h"

#include"Linux/module.h"

#if CONFIG_MODBERSIONS==1

degine MODBERSIONS

#include" Linux.modversions.h"

#endif

#difine devicename mydevice

/* the init funcion*/

int init_module()

{

int tag=module_register_chrdev(0,mydevice,&Fops);

if (tag<0)

{

printk("the device init is erro!\n");

return 1;

}

return 0;

}

/*the funcion which the device will be used */

int device_open ()

{

…….

}

int device_read ()

{

…….

}

int device_write ()

{

…….

}

int device_ioctl ()

{

…….

}

……

/* the deltter function of this module*/

int cleanup_module()

{

int re=module_unregister_chrdev(tag,mydevice);

if( re<0)

{

printk("erro unregister the module !!\n");

return 1;

}

return 0;

}

(0)

相关推荐

  • Linux内核链表实现过程

    关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: 复制代码 代码如下: struct list_node{stuct list_node *pre;stuct list_node *next;ElemType data; } 即一个链表节点包含:一个指向前向节点的指针.一个指向后续节点的指针,以及数据域共三部分.但查看linux内核代码中的list实现时,会发现其与教科书上的方法有很大的差别.来看看linux是如何实现双链表.双链表节点定义 复制代码 代码如下: struct lis

  • 一张图看尽Linux内核运行原理

    众所周知的是,几乎整个互联网都运行在 Linux 上,从网络协议,到服务器,到你平常访问的绝大多数网站,都能看到它的身影.Linux 内核就是最复杂最流行的开源项目之一.如果你希望学习内核知识,在网上可以搜到无数的资料,但是 Linux 内核还是一个非常难弄明白的项目. 俗话说:一图胜千言,今天我们就为大家介绍一张完整的 Linux 内核运行原理图,通过这张图,你可以很方便地学习内核知识. 在 Linux 内核中,有许多层次.模块.功能调用和函数:要把其中的每一块儿都弄明白很不容易,不过 Mak

  • Linux内核中红黑树算法的实现详解

    一.简介 平衡二叉树(BalancedBinary Tree或Height-Balanced Tree) 又称AVL树.它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1.若将二叉树上结点的平衡因子BF(BalanceFactor)定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1.0和1.(此段定义来自严蔚敏的<数据结构(C语言版)>) 红黑树 R-B Tree,全称是Red-B

  • Linux内核漏洞浅析

    与Windows相比,Linux被认为具有更好的安全性和其他扩展性能.这些特性使得Linux在操作系统领域异军突起,得到越来越多的重视.随着Linux应用量的增加,其安全性也逐渐受到了公众甚或黑客的关注.那么,Linux是否真的如其支持厂商们所宣称的那样安全呢?本期我们请到了启明星辰信息技术有限公司积极防御实验室工程师赵伟,对Linux进行专业的漏洞技术分析. Linux内核精短.稳定性高.可扩展性好.硬件需求低.免费.网络功能丰富.适用于多种cpu等特性,使之在操作系统领域异军突起.其独特的魅

  • Linux内核启动参数详解

    1.环境: Ubuntu 16.04 Linux linuxidc 4.4.0-89-generic #112-Ubuntu SMP Mon Jul 31 19:38:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux 2.查看当前linux内核的启动参数: cat /proc/cmdline 笔者的输出内容如下: BOOT_IMAGE=/boot/vmlinuz-4.4.0-89-generic root=UUID=bef418fa-4202-4513-b39

  • SYN Cookie在Linux内核中的实现

    概述 在目前以IPv4为支撑的网络协议上搭建的网络环境中,SYN Flood是一种非常危险而常见的DoS攻击方式.到目前为止,能够有效防范SYN Flood攻击的手段并不多,而SYN Cookie就是其中最著名的一种.SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明.在很多操作系统上都有各种各样的实现.其中包括Linux.本文就分别介绍一下SYN Flood攻击和SYN Cookie的原理,更重要的是介绍Linux内核中实现SYN Cookie的方式.最后,

  • 浅谈Linux内核创建新进程的全过程

    进程描述 进程描述符(task_struct) 用来描述进程的数据结构,可以理解为进程的属性.比如进程的状态.进程的标识(PID)等,都被封装在了进程描述符这个数据结构中,该数据结构被定义为task_struct 进程控制块(PCB) 是操作系统核心中一种数据结构,主要表示进程状态. 进程状态 fork() fork()在父.子进程各返回一次.在父进程中返回子进程的 pid,在子进程中返回0. fork一个子进程的代码 #include <stdio.h> #include <stdli

  • 解析Linux内核的基本的模块管理与时间管理操作

    内核模块管理 Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 #include <linux/init.h> #include <linux/module.h> static int __init hello_init(void) { printk(KERN_ALERT &

  • Linux内核模块和驱动的编写

    Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难.为了解决这个问题引入了内核机制.从而可以动态的想内核中添加或者删除模块. 模块不被编译在内核中,因而控制了内核的大小.然而模块一旦被插入内核,他就和内核其他部分一样.这样一来就会曾家一部分系统开销.同时,如果模块出现问题,也许会带来系统的崩溃. 模块的实现机制: 启动时,由函数 void inti_modules() 来初始化模块,因为启动事很多时候没有模块.这个函数往往把内核自身当作一个虚模块. 如由系统需要

  • Linux 块设备驱动代码编写

    按照ldd的说法,linux的设备驱动包括了char,block,net三种设备.char设备是比较简单的,只要分配了major.minor号,就可以进行读写处理了.相对而言,block和net要稍微复杂些.net设备姑且按下不谈,我们在以后的博文中会有涉及.今天,我们可以看看一个简单的block是怎么设计的. 为了将block和fs分开,kernel的设计者定义了request queue这一种形式.换一句话说,所有fs对block设备的请求,最终都会转变为request的形式.所以,对于bl

  • 详解linux usb host驱动编写入门

    usb协议是一个复杂的协议,目前涉及到的版本就有usb1.0, usb2.0, usb3.0.大家如果打开kernel usb host目录,就会发现下面包含了ohci,uhci,ehci,xhci,whci等多种形式的控制器驱动.那么,对于我们这些不是很了解usb的开发人员,如何了解usb的代码结构呢? 1.代码分布 drivers/usb目录下面,host目录包括了host驱动代码,core目录包含了主要的api接口代码,而其他目录则主要是device驱动代码. 2.device驱动怎么看

  • 详解linux电源管理驱动编写

    对于嵌入式设备来说,合适的电源管理,不仅可以延长电池的寿命,而且可以省电,延长设备运行时间,在提高用户体验方面有很大的好处.所以,各个soc厂家在这方面花了很多的功夫.下面,我们可以看看linux是如何处理电源管理驱动的. 1.代码目录 drivers/regulator 2.查看目录下的Kconfig文件 menuconfig REGULATOR bool "Voltage and Current Regulator Support" help Generic Voltage and

  • 详解linux 看门狗驱动编写

    看门狗是linux驱动的一个重要环节.某些特殊的设备,有时候需要放在一些环境恶劣的地方,比如电信设备.但是,任何软件都不可能100%没有bug.如何保证软件在遇到严重bug.死机的时候也能正常运行呢,那么看门狗就是有效的一种方法.看门狗一般要求用户定时喂狗,如果一段时间没有喂狗的话,那么系统就会自动重启.今天,我们就来看看这个看门狗驱动怎么编写? 1.代码目录 drivers/watchdog 2.阅读目录下的Kconfig,可以找一个s3c模块macro config HAVE_S3C2410

  • Linux 字符设备驱动框架详细介绍

    Linux 字符设备驱动框架 字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方

  • arm linux利用alsa驱动并使用usb音频设备

    一.背景: arm linux的内核版本是3.13.0 二.准备工作 添加alsa驱动到内核中,也就是在编译内核的时候加入以下选项: 接下来就重新编译内核即可 三.交叉编译alsa-lib和alsa-utils (alsa-utils是一系列的音频设备控制工具,而alsa-lib是alsa-utils依赖的库,所以先将alsa-lib编译好) 3.1交叉编译alsa-lib 3.2交叉编译alsa-utils 四.将三编译好的库及工具都拷贝至arm开发板(笔者通常直接将编译好的都压缩好之后再复制

  • Python实现Linux下守护进程的编写方法

    本文实例讲述了Python实现Linux下守护进程的编写方法,分享给大家供大家参考,相信对于大家的Python程序设计会起到一定的帮助作用.具体方法如下: 1. 调用fork()以便父进程可以退出,这样就将控制权归还给运行你程序的命令行或shell程序.需要这一步以便保证新进程不是一个进程组头领进程(process group leader).下一步,'setsid()',会因为你是进程组头领进程而失败.进程调用fork函数时,操作系统会新建一个子进程,它本质上与父进程完全相同.子进程从父进程继

  • Linux内核设备驱动之Linux内核模块加载机制笔记整理

    #include <linux/moduleparam.h> 1. 模块参数 在驱动定义变量 static int num = 0; //当加载模块不指定num的值时则为0 module_param(变量名, 类型, 权限);类型: byte, int, uint, short, ushort, long, ulong, bool, charp,权限不能有写的权限 传参数: insmod test.ko 变量名1=值1  变量名2=值2 module_param的调用关系如下: #define

  • linux nand flash驱动编写

    很长一段时间,nand flash都是嵌入式的标配产品.nand flash价格便宜,存储量大,适用于很多的场景.现在很普及的ssd,上面的存储模块其实也是由一块一块nand flash构成的.对于linux嵌入式来说,开始uboot的加载是硬件完成的,中期的kernel加载是由uboot中的nand flash驱动完成的,而后期的rootfs加载,这就要靠kernel自己来完成了.当然,这次还是以三星s3c芯片为例进行说明. 1.nand flash驱动在什么地方,可以从drviers/mtd

随机推荐