跟着简叔学的,可以B站搜索简说linux
1.32位系统内核空间和用户空间的默认大小
内核空间运行在高地址空间,用户空间在低地址空间。之所以要做这样的划分。出于安全考量,内核需要更高的权限,已屏蔽用户区的不安全操作。从软件设计思想来开,内核代码偏重于系统管理;用户空间的代码偏重于业务逻辑代码的实现。 注意这只是逻辑地址,并不是物理地址。这中间有一个映射的过程。
陷入内核态一般有三种情况:
系统调用,定时器中断,外设中断 处理完中断再返回用户空间的应用程序
2.x86段页式内存管理荷叶表映射机制
linux内核页表映射机制:线性地址如何转为物理地址? - 哔哩哔哩 (bilibili.com)
两步走,逻辑地址转化成线性地址,再转化成物理地址。部分架构逻辑地址就是线性地址,基址+offset 找到线性地址空间,通过把基址设为0来实现。那么从线性地址转化成逻辑地址就是我们说的页表映射。。
不同进程有不同的页目录表,所以他们可以不冲突的访问相同的逻辑地址(在他们各自的视角里),因为他们的页目录表不一样。 线性地址 总共32位(页表目录索引找到页表10位,页表索引找到物理地址基址10位,偏移12位)
3.对之前写的hello驱动的读写函数进行简单修改
代码改动如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//
ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{
printk(KERN_EMERG"hello_write\r\n");
int writenlen = 0;
writenlen = BUFFER_MAX>s?s:BUFFER_MAX;
if(copy_from_user(buffer,u,writenlen))
{
return -EFAULT;
}
return writelen;
}
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{
printk(KERN_EMERG"hello_read\r\n");
int readlen;
readlen = BUFFER_MAX>s?s:BUFFER_MAX;
if(copy_to_user(u,buffer,readlen))
{
return -EFAULT;
}
return readlen;
}其中,
copy_from_user
与copy_to_user
实现了用户空间和内核空间的数据拷贝。驱动程序肯定是在内核空间const char __user *u
则是用户空间的地址。所以看起来可能是反的,因为以前我们的视角是用户程序在用户空间。而且要注意,copy_from_user
与copy_to_user
他们的参数顺序哦。重新对驱动代码进行编译,插入。然后编译测试代码,进行测试,产生了段错误。
1
2
33
open successe
Segmentation fault写入操作发生段错误,经过检查发现,原来驱动代码中,定义了内核空间的buffer指针,却没有开辟空间。
将
1
char * buffer;
替换为
1
char buffer[BUFFER_MAX];
重新测试。
发现success拼写错了,无伤大雅无伤大雅。。。。。
代码表示两个用户程序,读写内核空间数据。
|
—- | —-