一、基本概念
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中。用户空间的内存映射采用段页式,而内核空间有自己的规则。
(1)Linux内核地址空间划分
通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G)。注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的。
(2)为什么这么划分?
- 处理器模式不同,权限不同:
对于X86体系的cpu,用户空间代码运行在Ring3横式,内核空间代码运行Ring0模式,对于arm体系的cpu,用户空间代码运行在usr横式,内核空间代码运行在svc模式;
- 安全考虑:
整个系统中有各种资源,比如计箅资源、内存资源和外设资源,而linux是多用户、多进程系统,所以,这些资源必须在受限的、被管理的状态下使用,要不然就陷入了混乱,空间隔离可以保证即便是单个应用程序出现错误也不会影响到操作系统的稳定性。
- 从软件设计思想来看,解除了核心代码和业务逻辑代码的耦合:
内核代码偏重于系统管理;而用户空间代码(也即应用程序)偏重于业务逻辑代码的实现,两者分工不同,隔离也是解耦合。
二、用户空间和内核空间数据传递
用户空间的应用程序,在业务实现时:
(1)比如你只实现个printf("hello world");,并没有和内核进行交互,那么就不会存在两个空间的数据拷贝;
(2)但是实际上,很多的业务实现都要与内核进行交互,各种上网,操作一些字符、块设备,调用open()、write()、read()、ioctl()等等函数,就要用到系统调用。这些都会触发一个中断,并通过中断的方式陷入到内核去执行,但用到的还是用户态进程,然后将进程需要的数据返回到用户空间。
为了解决用户空间和内核空间数据传递的问题,在Linux内核中提供了 2个接口用来实现:
/* 从用户空间拷贝到内核空间 */
static inline long copy_from_user(void *to,
const void __user * from, unsigned long n)
{
might_sleep();
if (access_ok(VERIFY_READ, from, n))
return __copy_from_user(to, from, n);
else
return n;
}
/* 从内核空间拷贝到用户空间 */
static inline long copy_to_user(void __user *to,
const void *from, unsigned long n)
{
might_sleep();
if (access_ok(VERIFY_WRITE, to, n))
return __copy_to_user(to, from, n);
else
return n;
}
下面我们编译一个misc_driver.c驱动程序和misc_test.c应用程序,来验证用户空间和内核空间的数据传递。
(1)misc_driver驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
char kernel_buf[1024] = {0};
static ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
if (copy_to_user(ubuf, kernel_buf, sizeof(kernel_buf)) != 0) {
printk("misc read error!\n");
return -1;
}
printk("misc read success!\n");
return 0;
}
static ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
if (copy_from_user(kernel_buf, ubuf, size) != 0) {
printk("misc write error!\n");
return -1;
}
printk("misc write success!\n");
return 0;
}
static int misc_release(struct inode *inode, struct file *file)
{
return 0;
}
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.read = misc_read,
.write = misc_write,
.release = misc_release
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR, // 动态分配子设备号
.name = "misc_driver",
.fops = &misc_fops
};
static int misc_init(void)
{
int ret;
ret = misc_register(&misc_dev);
if (ret < 0) {
printk("misc register is error\n");
return -1;
} else {
printk("misc register success\n");
}
return 0;
}
static void misc_exit(void)
{
int ret;
ret = misc_deregister(&misc_dev);
if (ret < 0) {
printk("misc deregister is error\n");
} else {
printk("misc deregister success\n");
}
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("zhixiaoxing");
Makefile文件:
#!/bin/bash
obj-m += misc_driver.o
#Kernel源码目录
KDIR := /home/kernel/iTop4412_Kernel_3.0
PWD ?= $(shell pwd)
#make -C 指定调用执行的路径
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *order *.mod.c *symvers
rm -rf ./.tmp_versions
(2)misc_test应用程序:
应用程序需要在开发板上运行,所以需要使用开发板的编译工具链编译,这里我们使用静态编译。
编译指令:arm-none-linux-gnueabi-gcc -o misc_test misc_test.c -static
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define DRIVER_PATH "/dev/misc_driver"
int main(int argc, char **argv)
{
int fd;
ssize_t size;
char r_buf[1024] = {0};
char w_buf[1024] = {0};
fd = open(DRIVER_PATH, O_RDWR);
if (fd < 0) {
printf("file open fail\n");
exit(EXIT_FAILURE);
}
strncpy(w_buf, "hello world!", strlen("hello world!"));
size = write(fd, w_buf, strlen(w_buf));
if (size < 0) {
printf("file write fail\n");
exit(EXIT_FAILURE);
}
size = read(fd, r_buf, sizeof(r_buf));
if (size < 0) {
printf("file read fail\n");
exit(EXIT_FAILURE);
} else {
printf("read data: %s\n", r_buf);
}
exit(EXIT_SUCCESS);
}
(3) 运行结果
上面的驱动程序需要在开发板上安装,可参考我另外一篇博客Linux驱动开发---杂项设备_智小星的博客-CSDN博客,应用程序也是需要使用开发板的编译工具链来编译,并在开发上运行。
root@android:/data/tianyu # ./misc_test
[67370.559988] misc write success!
[67370.561784] misc read success!
read data: hello world!