打印等级:
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING"<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
控制台相关的打印级别:
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
cat /proc/sys/kernel/printk 查看当前打印级别
/lib/modules ls 查看当前加载的是哪个内核 并找到makefile 的当前路径
如何去运行相关模块:
sudo insmod xxx.ko :自动执行入口函数
查看内核打印信息:dmesg
如何删除当前模块:
sudo rmmod xxx :自动执行出口函数
清除内核打印信息:
sudo dmesg -c:清除打印信息的同时,将打印信息,显示在屏幕上
sudo dmesg -C:清除打印信息的同时,不显示打印信息在屏幕上
查看当前内核中存在的模块:
lsmod modinfo xxx.ko 可以查看到很多info
关于type的赋值:
type:bool,invbool,charp,int,long,short,uint,ulong,ushort;
module_param_named(named_out,name_in,type,perm)
参数:name_out:加载模块时,参数的名字
name_in:模块内部变量的名字
type:参数的类型
perm:访问权限
传递字符串到全局字符数组:
加载模块时传递字符串到一个全局字符数组module_param_string(name,string,len,perm)
参数:name:加载模块时,参数的名字
string:模块内部字符数组的名字
len:模块内部字符数组
perm:访问权限
传递数组型参数 :
加载模块时传递参数到模块的数组中module_param_array(name,type,num,perm)
参数:name:加载模块时,参数的名字
type:模块数组的数据类型
num:传递数组成员的个数,NULL不关心用户传递的参数个数
perm:访问权限
参数描述信息:MODULE_PARM_DESC( 参数名称,"参数描述信息");
sudo insmod module.ko num=1 module_out=8 str="hello" my_array=9,8,7,6,0
modinfo xxx.ko 查看依赖的模块
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
-C :获取编译该模块的内核的makefile 路径, M:获取待编译模块所在的路径
EXPORT_SYMBOL(待导出的符号);
生成:Module.symvers 该文件中记录了 add的函数名 , 函数的指针
export.h-->/usr/src/linux-headers-3.5.0-23-generic/Module.symvers: addr + symbol + module + EXPORT_SYMBOL
/proc/kallsyms
typedef void (*lpFunction)();
lpFunction lpReset = (lpFunction)0xF000FFF0;
lpReset();
1.获取到 module.symvers insmode xxx.ko
2.完成对应的使用 extern int add(int a, int b); extern int sub(int a, int b);
3.完成, 编译, 插入到内核
dev_t :设备号的数据类型
dev_t from ;
int major ,minor;
获取主设备号major: major = MAJOR(from);
获取次设备号minor: minor = MINOR(from);
利用主次设备号获取设备号: from = MKDEV(major , minor);
struct inode {
------>某一个具体的文件--->设备节点--->/dev/xxx
const struct inode_operations *i_op;//对文件、设备节点操作时的相关操作方法 的集合
dev_t i_rdev; //设备号
const struct file_operations *i_fop; //操作方法
union {
struct pipe_inode_info *i_pipe;//网络相关的
struct block_device *i_bdev; //块设备的抽象结构体
struct cdev *i_cdev; //字符设备的抽象结构体
};
};
struct cdev {//字符设备对象
//struct kobject kobj;//linux设备模型的底层组成
struct module *owner;//表征其所归属的模块对象
const struct file_operations *ops;//对于字符设备对象相关操作的方法集合,open,close,read,write,llseek
dev_t dev;//该字符设备对象对应的设备号 -------------》 对应 inode 中的 i_rdev container_of(inode->i_rdev, xxxxx);
};
[1]设备号的申请
[1.1]自动申请设备号
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
功能:自动申请设备号
参数:dev:对应的设备号
baseminor:起始次设备号
count:申请次设备的个数
name:名称
返回值:成功:0 失败:errno
[1.2]手动申请(可能存在设备号的冲突)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手动申请设备号
参数:from:对应的设备号
count:申请次设备的个数
name:名称
返回值:成功:0 失败:errno
[2]申请得到对应的字符设备对象
struct cdev *cdev_alloc(void)
功能:申请cdev对象
参数:无
返回值:成功:struct cdev 的指针 失败:NULL
[3]填充字符设备对象的相关属性成员
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:初始化cdev[完成cdev与之相关联的操作方法集合的绑定]
参数:cdev:字符设备的指针对象
fops:操作方法集合
返回值:无
[4]字符设备对象的注册
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:注册字符设备对象
参数:p:字符设备的指针对象
dev:该字符设备对象的设备号
count:注册的字符设备的个数
返回值:成功 : 0 失败:errno
---------------------------exit------------------------------
[1]设备号的注销
void unregister_chrdev_region(dev_t from, unsigned count)
功能:注销设备号
参数:from:对应的设备号
count:注销次设备号的个数
返回值:无
[2]void cdev_del(struct cdev *p)
功能:注销字符设备对象
参数:p:对应的字符设备对象
返回值:无
(0)字符设备驱动另一种方式
[1]字符设备驱动的简化版
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
功能:完成字符设备框架的初始化编写
参数:major:主设备号 (major= 0 完成设备号部分的自动申请 )
name:设备对应的名称 cat /proc/devices
fops:该设备的对应的操作方法集合
返回值:成功:0 /major 失败:errno
static inline void unregister_chrdev(unsigned int major, const char *name)
功能:注销字符设备框架(的退出部分编写)
参数:major:主设备号 (major= 0 完成设备号部分的自动申请 )
name:设备对应的名称 cat /proc/devices
返回值:无
(1)读写
用户空间------->内核空间的访问
(1)系统调用【read,write】
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
头文件:include <linux/uaccess.h> //read write是针对用户来讲的 copy_to_user copy_from_user是针对内核来讲的
read:将底层数据传输给用户层 copy_to_user
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
功能:传输数据到用户空间
参数:to:用户空间的数据存储地址
from:内核空间的地址
n:传输数据的大小
返回值:成功:0 失败:传输数据的个数
write:将用户空间数据传输给底层 copy_from_user
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
功能:将用户空间的数据写入内核空间
参数:to:内核空间的地址
from:用户空间的地址
n:传输数据的大小
返回值:成功:0 失败:传输数据的个数
(2)控制(32bits被分为四部分)
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
功能:根据不同的命令码执行不同的操作
参数:d:文件描述符
request:命令码
...:视情况再填充 ,[地址]
返回值:成功:0 失败:-1
头文件:#include <asm-generic/ioctl.h>
ioctl:
底层实现的机制:
(1)完成unlocked_ioctl的底层实现:switch case
(2)完成不同命令码的封装
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
功能:封装命令码
参数:type:命令码的一个类型 【幻数】,一般使用相关字符来标志 eg: #define type 'a'
nr: 命令代码参数 eg:0 , 1 , 2...
/*
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
linux-3.14/Documentation/ioctl/ioctl-number.txt记录了内核中可能被占用的一些命令码
*/
ioctl命令码组成: 32bit
_________________________________________
| 方 向 | 数据尺寸 | 设备类型 | 序列号 |
|----------|----------|----------|--------|
| 2 bit | 8~14 bit | 8 bit | 8 bit |
幻数 一般使用字符来表示 eg:# define type ‘a’
Nr:命令代码参数
#define TYPE 'A'
#define ONE _IO(TYPE, 0)
#define TWO _IO(TYPE, 1)
(3)自动创建设备节点[/proc/sys/kernel/hotplug]--->udev
头文件:#include <linux/device.h>
结构体:
struct class {
const char *name;//类名 /sys/class/xxx
struct module *owner;//所归属的模块
//struct kobject *dev_kobj;//linux设备模型相关-->生成文件夹
};
struct device {
struct device *parent;//该设备的parent
struct device_private *p;//存放该设备的私有数据
//struct kobject kobj;//linux设备模型相关-->生成文件夹
const char *init_name; /* 设备名称 */
//const struct device_type *type;//设备类型
//struct bus_type *bus; /* 该设备挂接的总线*/
//struct device_driver *driver; /*支持当前设备的驱动对象*/
dev_t devt; /* 设备号*/
struct class *class;//当前设备归属于哪一个类
//void (*release)(struct device *dev);//当设备移除时,会被调用
};
(1)struct class *class_create(struct module *owner,const char *name)
功能:创建class
参数:owner:THIS_MODULE
name:对应的设备类/*/sys/class/xxx*/
返回值:成功:struct class指针对象 失败:errno指针
IS_ERR(errno):判断其是否申请得到了对应的指针,如果说errno该指针是正确的话,那么IS_ERR会返回0,反之非0
PTR_ERR(errno):将错误指针,转换成具体的错误码
/*
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)
*/
(2)void class_destroy(struct class *cls)
功能:销毁对应的class
参数:cls:struct class的指针对象
返回值:无
(3)struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
功能:创建对应的device
参数:class:struct class的指针对象
parent:设备父节点 如果没有 NULL
devt:设备号
drvdata:私有数据 没有的话 NULL
fmt:对于待生成的设备节点的命名格式 /dev/demo0 /dev/demo1 "%s%d"
...:"demo" i
返回值:成功:struct device指针对象 失败:errno指针
(4)void device_destroy(struct class *class, dev_t devt)
功能:销毁对应的device
参数:class:struct class的指针对象
devt:设备号