复习day02
1.模块参数
内核模块xxx.c文件中申请的全局变量声明为模块参数
insmod xxx.ko mpint=10l
/sys/module/xxx/parameters/mpint
2.内存管理
逻辑地址
虚拟地址
物理地址
linux 编程时使用到的都是虚拟地址
linux内核对内存管理的最小单位是页。
kmalloc/kfree
vmalloc/vfree
__get_free_pages/free_pages
申请内存失败的时候不能够直接退出,需要释放之前申请的内存
flags:GFP_KERNEL,在申请内存过程中可能会产生睡眠
GFP_ATOMIC,不会导致睡眠
3.内核链表(include/linux/list.h)
list_add
list_del
list_for_each
INIT_LIST_HEAD
list_entry:招到那个成员变量然后返回指针
————————————————————————————————————————
1.内核定时器
时钟中断
由系统的定时硬件以周期性的时间间隔变身,返回间隔(也就是频率)由内核根据常数HZ来确定
HZ常数
它是一个不体系结构无关的常数,可以配置50-1200之间,可以在内核中配置
tick
它是HZ的倒数,也就是每变生一次硬件定时器中断的时间间隔。如HZ为200,tick为5毫秒
jiffies:
它是linux核心变数(32位变数 unsigned long),它被用记录自开机以来,已经过了多少个tick.
每变生一次硬件定时器中断,jiffies变数就会被加1
HZ:常数,决定了时钟中断发生的频率 (1s中发生的次数)
tick:发生时钟中断的时间间隔 tick=1/HZ
jjffies:核心变数
struct timer_list {
....
unsigend long expires;超时时间
void (*function)(unsigned long)调用函数
unsigned long data;传递参数
......
}
#include<linux/init.h>
#include<linux/module.h>
#include<linux/timer.h>
MODULE_LICENSE("GPL");
/*定时器时间间隔*/
#define BARK_TIMER 5
#define BARK_TIMES 10
/*定义定时器变量*/
struct timer_list bigdog_timer;
void bigdog_timer_handler(unsigned long data){
/*记录该函数被调用的次数*/
static int bark_count=0;
printk("wang wang~~");
if(bark_count<BARK_TIMES){
bigdog_timer.expires=jiffies+HZ*BARK_TIMER;
bigdog_timer.data++;
add_timer(&bigdog_timer);
bark_count++;
}
}
int __init kerneltimer_init(void){
bigdog_timer.expires=jiffies+HZ*BRAK_TIMER;
//注意此处与应用空间不同,不知道什么时候进行安装
//
bigdog_timer.function=bigdog_timer_handler;
bigdog_timer.data=100;
/*初始化定时器变量*/
init_timer(&bigdog_timer);
/*添加定时器到内核中去*/
add_timer(&bigdog_timer);
return 0;
}
/*初始化函数中对内核的影响在退出的时候都要取消*/
void __exit kerneltimer_exit(void){
/*从内核中删除指定的定时器*/
del_timer(&bigdog_timer);
}
module_init(kerneltimer_init);
module_exit(kerneltimer_exit);
# #
#
#
#
重启tftp服务
sudo /etc/init.d/tftpd-hpa restart
# #
#
#
#
2.系统调用
ANSI C fopen(标准C的库函数)
UNIX C open(系统调用)
用户态的程序一般情况下是不能访问内核的资源
只有通过中断或者系统调用才能从用户态进入内核态
unistd.h
2.6.35内核中系统调用的个数是366个
linux下的驱动程序是为应用程序提供服务的
驱动程序在内核态执行
系统调用的原理(实现方式)
应用程序首先使用适当的值填充寄存器,
然后调用一个特殊的指令,
跳转到内核某个固定的位置。
内核根据应用程序所填充的固定的值来找到相应的函数执行。
适当的值:arch/arm/asm/unistd.h
特殊的指令:arm->SWI(软中断)
X86 0x80
固定位置:在ARM体系结构中,应用程序跳转到的位置是
entry-common.S ->vector_swi
会使用到sys_call_table(call.S
)
用户空间编程 fd=open("aa.txt",..);
5->寄存器
SWI
----------------------
vector_swi
sys_call_tablep[5]
sys_open
SYSCALL_DEFINE3(open,const char __user*,........)//这是一个宏
添加一个新的系统调用
1)添加新的内核函数
vi arch/arm/kernel/sys_arm.c
文件最后添加
asmlinkage int sys_add (int x ,int y)
{
printk("enter sys add!\n");
return x+y;
}
2)更新头文件 arm/arm/include/asm/unistd.h
vi arch/arm/include/asm/unistd,h
nu:394 添加第366个系统调用
#define
__NR_add
(__NR__SYSCALL_BASE+366)
3)更新系统调用表(calls.S)
vi arch/arm/kernel/calls.S
CALL(sys_add)
4)重新编译内核
make
5)写一个用户程序测试该代码
#include<stdio.h>
//标准库中的实现使用以下的方法
int add(int x.int y){
return syscall(366,x,y);
}
int main(){
int result=0;
result=syscall(366,12,13);
printf("sys_add call result =%d\n",result);
}
6)编译
arm-linux-gcc calltest.c -o calltest
3.字符设备框架
按照硬件特性分类
字符设备(顺序读写,不带缓冲区)
块设备(读写顺序不固定,带有读写缓冲)
网络设备
struct cdev{
struct kobject kobj;
//内嵌的kobject对象
struct module *owner;
//所属模块
struct file_operations *ops;//文件操作结构体
struct list_head list;
dev_t dev;
//设备号
unsigned int count;
}
每一个cdv都有一个设备号
设备号(32bits)=主设备号(高12bits)+次设备号(低20bits)
主设备号:代表一个类型的设备号
次设备号:用于区分该设备中的不同个体
设备号的选取
->静态分配
看看现在内核中哪些主设备号没有被使用,选择一个使用
cat /proc/devices
Documentation\devices
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
MODULE_LICENSE("GPL");
#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
dev_t dev=0;//dev_t 就是unsigned int为了内核方便的移植
int __init cdd_init(void){
int ret=0;
/*生成一个设备号*/
//dev = CDD_MAJOR<<20 +CDD_MINOR;
dev=MKDEV(CDD_MAJOR,CDD_MINOR);
/*
* 注册设备号
*ret =register_chrdev_region(dev_t from ,unsigned int count,char* name);
*from.要注册的起始设备号
*count,连续注册的设备号个数
*name,the name of the device or driver
*失败返回负数
*/
ret =register_chrdev_region( dev ,CDD_COUNT,"cdddemo");
if(ret<0){
printk("register_chrdev_region failed\n");
goto failure_register_chrdev;
}
return 0;
failure_register_chrdev:
return ret;
}
void __cdd_exit(void){
/*注销设备号*/
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
module_exit(cdd_exit);
->动态分配
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
MODULE_LICENSE("GPL");
#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
int __init cdd_init(void){
int ret =0;
if(cdd_major){
//静态方式分配设备号
dev=MKDEV(CDD_MAJOR,CDD_MINOR);
ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
}else{
/*动态分配设备号
*
*
* ret =all_chrdev_region(dev_t* dev,unsigend bassminor,
count,const char* name)
*/
ret =all_chrdev_region(&dev,cdd_minor,0,CDD_COUNT,cdd_demo);
}
if(ret<0){
prink("register_chrdev_region failed!\n");
goto failure_register_chrdev;
}
/*获取主设备号*/
cdd_major=MAJOR(dev);
return 0;
failure_register_chrdev:
return ret;
}
void __exit cdd_exit(void){
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
moudle_exit(cdd_exit);
cdev的操作函数
void
cdev_init(struct cdev*,struct file_operations*)//初始化cdev
struct cdev* cdev_alloc(void);
void cdev_put(struct cdev *p);
int
cdev_add(struct cdev* ,dev_t ,unsigned );//添加到内核中
void cdev_del(struct cdev *);
struct file_operations *fopen{}
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
MODULE_LICENSE("GPL");
#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
/*定义cdev类型的变量*/
struct cdev cdd_cdev;
struct file_operations cdd_fops={
.owner = THIS_MODULE,
};
int __init cdd_init(void){
int ret =0;
if(cdd_major){
//静态方式分配设备号
dev=MKDEV(CDD_MAJOR,CDD_MINOR);
ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
}else{
/*动态分配设备号*/
ret =all_chrdev_region(&dev,cdd_minor,CDD_COUNT,cdd_demo);
}
if(ret<0){
prink("register_chrdev_region failed!\n");
goto failure_register_chrdev;
}
/*获取主设备号*/
cdd_major=MAJOR(dev);
/*初始化cdev
*cdev_int(struct cdev* dev,const struct file_operations* fops)
*/
cdev_int(&dev,&cdd_fops) ;
/*添加cdev到内核中
*cdev_add(struct cdev*p,dev_t dev ,unsigned count)
*/
ret=cdev_add(&cdev,dev,CDD_COUNT);
if(ret<0){
printk("cdev_add failed!\n");
goto failure_cdev_add;
}
return 0;
failure_cdv_add:
unregister_chrdev_region(dev,CDD_COUNT);
failure_register_chrdev:
return ret;
}
void __exit cdd_exit(void){
/*从内核中删除cdev*/
cdev_del(&cdd_cdev);
/*注销设备号*/
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
moudle_exit(cdd_exit);
test.c
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
int fd =0;
int main(void){
fd=open("/dev/cdd",O_RDWR);
if(fd<0){
printf("open failed!\n");
return -1;
}
printf("open successed fd =%d",fd);
while(1);
return 0;
}
手工创建设备节点文件
mknod /dev/cdd
c
248
0
字符设备
主设备号
次设备号
lsmod 显示内核装载模块 insmod remod
ls /dev/cdd -l