linux驱动(网摘)

2024-04-27 17:18
文章标签 linux 驱动 网摘

本文主要是介绍linux驱动(网摘),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux驱动程序入门—Hello World

分类: linux

1、引言
    记得在学习VC++和C语言的时候,一开始都会以一个HELLO WORLD的例子作为演示,将学者逐渐引入殿堂,这个几乎成了计算机编程语言学习必经的一个入门之路。
    当然,在学习linux编程的时候也是这样,下面的例子应该是再熟悉不过了:
    首先用VI编写一个C程序:vi hello.c
    #include  "stdio.h"
    int main()
   {
         printf("hello world!!!/n");
         return 0;
    }
    接着用GCC进行编译:gcc -o hello hello.c
    最后运行该程序:./hello
    在终端上你会看到:hello world!!!
   上面的是在操作系统基础上进行的用户应用程序的开发。然而对于linux驱动程序的开发是绝然不同的,因为驱动程序的开发是运行在内核空间的,而应用程序是运行在用户空间的。虽然hello world是一个简单得不能再简单的程序,但是对于嵌入式linux驱动程序的初学者来说,通过这个过程的操作可以对linux驱动程序开发的过程和其中的一些概念有一个深刻的认识。所以,我在这里也就以前学习linux的基础上整理了一下,写了这篇博客。一方面是自己对这方面知识的回顾和巩固,另一方面更是希望这里的内容能给大家提供那么一点点有用的信息,小弟心里就很高兴了。当然希望有高手可以做下评价和指导,及时纠正小弟的错误,谢谢先。

2、概念
    驱动程序作为系统内核的一部分,它工作在核心态,而应用程序工作在用户态。也就是说,程序不能直接通过指针,把用户空间的数据地址传递给内核(因为MMU映射的地址根本不一样)。要想在应用程序和驱动程序之间传递数据(指针),就需要经过转换。把用户态“看到”的空间地址转换成内核态可访问的地址。Linux系统提供了一系列方便的函数实现这种转换,如get_user、put_user、copy_from_user、copy_to_user等,它们自己负责访问权限的检查,使用时,不需要关系更多的问题。
    Linux内核把驱动程序划分为3种类型:字符设备块设备和网络设备

    字符设备和块设备可以像文件一样被访问。它们的主要区别不在于能否seek,而是在于系统对于这两种类型设备的管理方式。应用程序对于字符设备的每一个I/O操作,都会直接传递给系统内核对应的驱动程序;而应用程序对于块设备的操作,要经过系统的缓冲区管理,间接传递给驱动程序处理。块设备的这种管理方式是为存储提供优化的;而字符设备的管理方式是为操作提供优化的。至于网络设备,它在Linux系统中是一类比较特殊的设备它不像字符设备或块设备那样通过对应的设备文件节点去访问,内核也不再通过read和write等调用去访问网络设备。Linux的网络系统主要是基于BSD UNIX的套接字机制,在系统和驱动程序之间有专门的数据结构进行数据传输,系统支持对数据发送和数据接收缓存,提供流量控制机制,提供更多的协议支持。


      在linux系统中,驱动程序都做成模块的形式,也就是module。简单的说,一个模块提供一个功能,这些模块是可以按照需要随时装入内核空间和从内核空间卸载的。因此,内核模块是为了给内核动态增减功能而设计的,并不仅仅是限于驱动程序。

 

关于内核模块初始化(加载)函数
    当用户输入命令“insmod 模块文件名”(或者其他方式)加载内核模块时,系统会检测此模块能否被加载,如果能被加载,内核调用模块的初始化函数。在linux 2.4中,内核模块的初始化函数名为init_module()。但如果驱动程序需要编译进内核,则初始化函数不能与内核的其他部分(包括其他内核模块)的函数同名。这样,如果程序可能编译到内核中时就比较麻烦。不过在这个头文件中定义了宏module_init(),用来屏蔽两者的差别,事实上,使用这个宏可以使驱动程序向上兼容linux 2.6,而兼容linux 2.0也比较方便
 
关于内核模块清除(卸载)函数
     当用户输入命令“rmmod 模块文件名”(或者其他方式)卸载内核模块时,此时,系统会检测此模块是否能被卸载,内核将调用模块清除函数。在linux 2.4中,清除函数的函数名称为cleanup_module()。但如果驱动程序需要编译进内核,则初始化函数不能与内核的其他部分(包括其他内核模块)的函数同名。这样,如果程序可能编译到内核中时就比较麻烦。不过在这个头文件中定义了宏module_exit(),用来屏蔽两者的差别。  

3、实例
    因为内核模块需要加载到内核空间,所以其程序的编写与一般应用程序不同,在里面再也找不到类似main()这样的入口函数,下面对应函数相应的源代码hello.c,介绍一个驱动模块的写法。
 
#ifndef __KERNEL__
    #define __KERNEL__
#endif
#ifndef MODULE
    #define MODULE
#endif
 
#include <linux/module.h>  //所有模块都需要的头文件
#include <linux/sched.h> 
#include <linux/kernel.h>
#include <linux/init.h>        // init和exit相关宏


MODULE_LICENSE("GPL");
 
int text_init(void){
    printk("<0>Hello World!");
    return 0;
}
 
void text_cleanup(void){
   printk("<0>Goodbye World!");
}
 
module_init(text_init);             //注册加载时执行的函数
module_exit(text_cleanup);     //注册卸载时执行的函数
 
4、调试
    一个Linux内核模块需包含模块初始化和模块卸载函数,前者在insmod的时候运行,后者在rmmod的时候运行。初始化与卸载函数必须在宏module_init和module_exit使用前定义,否则会出现编译错误。
    程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。
    编译:
    gcc -c -I /usr/src/linux-2.4/include/ hello.c
    运行:
    insmod hello.o
    终端上会显示:
    localhost kernel:Hello World!
    同时在/proc/modules里面会看到相应的设备信息:
    more /proc/modules
    你会看到(或许后面的数值不一样):
    hello           844   0 (unused)
    .......
    卸载驱动程序:
    rmmod hello
    你将会在终端上面看到:
    localhost kernel:Goodbye World!  
 
5、注意:
<1>在gcc编译选项中增加-c
<2>在gcc编译选项中定义两个宏:-DMODULE -D__KERENL__
  或直接在源文件中定义这两个宏:
  #define MODULE
  #define __KERNEL__
<3>在源文件中包括module.h文件:
  #include  "linux/module.h"
<4>假定你现在运行的内核的源码目录绝对路径是MyKernelSrcPath,在gcc编译时增加选项:
  -I $MyKernelSrcPath/include (如-I /usr/src/linux/include)
<5>某些时候用insmod -f能够成功加载,但需谨慎使用。
<6>如果看不到用printk打印的信息,可以用dmesg命令看。
<7>打印消息受级别的限制,消息级别可以通过printk设置,如:
  printk(" 《n》something");  
  假设控制台的消息级别为m, 当n
  这样一方面可以提高要打印消息本身的级别(数字越小级别越高),
  另一方面可以改变控制台的消息级别(可从1到8),如改为8可用以下命令:
  # echo "8" > /proc/sys/kernel/printk

这篇关于linux驱动(网摘)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/941079

相关文章

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

ubuntu16.04如何部署dify? 在Linux上安装部署Dify的技巧

《ubuntu16.04如何部署dify?在Linux上安装部署Dify的技巧》随着云计算和容器技术的快速发展,Docker已经成为现代软件开发和部署的重要工具之一,Dify作为一款优秀的云原生应用... Dify 是一个基于 docker 的工作流管理工具,旨在简化机器学习和数据科学领域的多步骤工作流。它

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

Linux系统调试之ltrace工具使用与调试过程

《Linux系统调试之ltrace工具使用与调试过程》:本文主要介绍Linux系统调试之ltrace工具使用与调试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、ltrace 定义与作用二、ltrace 工作原理1. 劫持进程的 PLT/GOT 表2. 重定

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

嵌入式Linux之使用设备树驱动GPIO的实现方式

《嵌入式Linux之使用设备树驱动GPIO的实现方式》:本文主要介绍嵌入式Linux之使用设备树驱动GPIO的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、设备树配置1.1 添加 pinctrl 节点1.2 添加 LED 设备节点二、编写驱动程序2.1

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

Linux搭建单机MySQL8.0.26版本的操作方法

《Linux搭建单机MySQL8.0.26版本的操作方法》:本文主要介绍Linux搭建单机MySQL8.0.26版本的操作方法,本文通过图文并茂的形式给大家讲解的非常详细,感兴趣的朋友一起看看吧... 目录概述环境信息数据库服务安装步骤下载前置依赖服务下载方式一:进入官网下载,并上传到宿主机中,适合离线环境

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将