首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Linux的内核空间和用户空间是怎么回事儿?驱动程序位于内核空间中,与用户空间的数据传递通过copy_to_user和copy_from_user进行;Ring 0和Ring 3是啥意思?

  • 25-03-05 04:42
  • 4303
  • 7685
blog.csdn.net

01-内核空间与用户空间是怎么回事儿

  1. 内核空间(Kernel Space):

    • 是操作系统的核心部分,负责管理硬件资源和提供系统服务。
    • 驱动程序作为内核的一部分运行在内核空间。
    • 在内核空间中,代码可以直接访问硬件设备和系统资源(如内存、I/O 端口等)。
  2. 用户空间(User Space):

    • 是普通应用程序运行的地方,与内核空间隔离。
    • 应用程序通过系统调用(如 read, write, ioctl 等)与内核交互,但不能直接访问内核空间中的资源。
  3. 内核空间的特点

    • 特权模式:内核代码运行在 CPU 的高特权级(通常是 Ring 0),可以直接访问硬件。
    • 无内存保护:内核代码可以访问任意内存地址,因此需要开发者小心避免越界访问。
    • 直接硬件访问:可以操作硬件设备的寄存器和内存,但需要遵循硬件协议。
    • 实时性更高:由于内核空间代码直接运行在内核中,响应时间比用户空间应用更短。
    • 注意:内核空间具有高特权,但也需要更加谨慎的编程,以避免影响系统稳定性。

02-内核空间和用户空间分别有自己的堆、栈、数据段、代码段

关于这个问题的详细说明见我的另一博文: http://iyenn.com/rec/1709613.html

03-Linux系统中的Ring 0和Ring 3是啥意思?

可简单的理解为内核空间就是Ring 0,用户空间就是Ring 3,下面是详细解释:

在Linux系统中,Ring 0和Ring 3是与CPU的特权级别(Privilege Level)相关的概念,来源于x86架构的硬件设计。它们是CPU保护模式下的不同运行级别,用于区分操作系统和应用程序的权限,以确保系统的安全性和稳定性。

特权级别(Rings)的基本概念

x86架构定义了4个特权级别,从Ring 0到Ring 3:

  • Ring 0:最高权限级别(内核态,Kernel Mode)。
  • Ring 1 和 Ring 2:中间权限级别(很少使用)。
  • Ring 3:最低权限级别(用户态,User Mode)。

在实际使用中,现代操作系统(包括Linux)通常只使用Ring 0和Ring 3。


Ring 0(内核态,Kernel Mode)

  1. 作用:

    • 用于运行操作系统的内核代码和驱动程序。
    • 拥有对硬件资源的完全访问权限(包括CPU指令集、内存、I/O设备等)。
    • 可以执行特权指令(例如访问硬件寄存器、控制中断等)。
  2. 特性:

    • 没有任何限制,可以直接操作硬件和管理资源。
    • 一旦出现错误(例如非法访问内存),可能导致整个系统崩溃。
  3. 典型例子:

    • Linux内核代码。
    • 驱动程序代码。

Ring 3(用户态,User Mode)

  1. 作用:

    • 用于运行用户程序(应用程序),例如Web浏览器、文本编辑器等。
    • 通过系统调用(System Call)与内核交互,获取服务(如文件读写、网络通信)。
  2. 特性:

    • 权限受限,无法直接访问硬件或执行特权指令。
    • 任何试图越权操作都会被CPU拦截,并可能触发异常。
    • 提高系统的稳定性和安全性:用户程序崩溃不会影响系统内核。
  3. 典型例子:

    • 用户空间程序,例如ls、cat等命令。
    • 应用程序和库(如glibc)。

两者的区别和联系

特性Ring 0 (内核态)Ring 3 (用户态)
权限最高权限,无限制最低权限,受严格限制
运行位置操作系统内核及驱动程序用户空间程序及库
硬件访问可直接访问硬件资源需通过内核提供的接口
指令执行可执行所有指令只能执行非特权指令
稳定性和安全性易受错误影响(整个系统)崩溃只影响当前程序

Linux系统的设计

  1. Linux将内核运行在Ring 0,用户进程运行在Ring 3。
  2. 用户进程通过系统调用进入内核模式(Ring 0),请求内核服务。
  3. 通过这种机制,Linux系统在性能与安全性之间达到了良好的平衡:
    • 核心功能运行在高权限模式,效率高。
    • 应用程序运行在低权限模式,减少错误和恶意代码的风险。

04-驱动程序运行于内核空间

当驱动程序通过 insmod 或 modprobe 加载到内核中后,便成为内核的一部分,并运行在内核空间。当用户程序通过系统调用(如 open, read, write)访问驱动程序时,内核会切换到内核空间执行驱动程序中的相应函数(如 file_operations 中的 read, write)。

05-请给出一段典型的运行于用户空间的程序

下面这段代码就是典型的运行于用户空间的程序


#include 
#include 
#include 
#include 
#include 
#include 

/*
 * ./hello_drv_test -w abc
 * ./hello_drv_test -r
 */
int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w \n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	
	close(fd);
	
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

06-内核空间和用户空间怎么样实现数据传递

驱动程序中所有的变量(包括全局变量和局部变量)都属于内核空间中的数据,假设在假如在驱动程序中定义了一个全局变量型的字符数组:

static char kernel_buf[1024];
  • 1

由于在Linux中,内核空间和用户空间是隔离的,所以用户空间的程序是不能直接访问或修改这个数组的数据的。

通常用内核函数copy_to_user和copy_from_user实现数据传递,下面介绍这两个函数。
备注:也可以使用用户空间中的内核函数mmap调用驱动程序中的mmap函数将驱动程序中的数据对应的内存映射到用户空间中,关于这一点见下面的博文:
http://iyenn.com/rec/1709550.html
http://iyenn.com/rec/1709459.html

07-函数copy_to_user和copy_from_user

copy_to_user 和 copy_from_user 是 Linux 内核中专门用于在用户空间和内核空间之间交换数据的媒介函数。它们是实现数据传递的核心 API,能够在内核和用户空间之间安全地复制数据,同时避免直接访问用户空间内存可能引发的安全和稳定性问题。


为什么需要这些函数?

在 Linux 中,用户空间和内核空间是分离的:

  1. 用户空间(User Space):
    • 普通程序运行的空间。
    • 对内核空间没有直接访问权限。
  2. 内核空间(Kernel Space):
    • 负责底层操作系统功能。
    • 必须对用户空间进行严格保护,避免恶意或意外行为影响系统稳定性。

由于用户空间与内核空间是隔离的,直接访问对方内存可能导致非法内存访问。因此,Linux 提供了安全的 API,比如 copy_to_user 和 copy_from_user,以确保数据的安全传递。


两个函数的作用

copy_to_user
  • 功能:将内核空间的数据复制到用户空间。
  • 使用场景:用户调用 read 系统调用时,驱动程序通过该函数将数据传递给用户程序。
copy_from_user
  • 功能:从用户空间复制数据到内核空间。
  • 使用场景:用户调用 write 系统调用时,驱动程序通过该函数接收用户程序的数据。

两个函数的参数和返回值

copy_to_user 原型
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
  • 1
  • to: 用户空间的目标地址(缓冲区)。
  • from: 内核空间的源地址(缓冲区)。
  • n: 要复制的字节数。
copy_from_user 原型
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
  • 1
  • to: 内核空间的目标地址(缓冲区)。
  • from: 用户空间的源地址(缓冲区)。
  • n: 要复制的字节数。
返回值
  • 如果复制成功,返回 0。
  • 如果复制失败,返回未成功复制的字节数(通常是因为地址无效或权限问题)。

工作流程

write 数据流:用户空间 → 内核空间
  1. 用户空间程序调用 write(fd, user_buf, size),将数据写入设备文件。
  2. 内核将 user_buf 地址和大小传递到驱动程序的 write 函数。
  3. 驱动程序通过 copy_from_user 从用户空间的 user_buf 复制数据到内核空间缓冲区。
read 数据流:内核空间 → 用户空间
  1. 用户空间程序调用 read(fd, user_buf, size),从设备文件读取数据。
  2. 驱动程序通过 copy_to_user 将内核空间缓冲区中的数据复制到用户空间的 user_buf。
  3. 用户程序接收数据。

安全性保障

  1. 地址合法性检查:

    • 这两个函数会检查用户空间地址是否有效,防止非法内存访问。
    • 如果地址越界或权限不足,会返回未成功复制的字节数。
  2. 数据隔离:

    • 避免直接访问用户空间地址引发的错误或安全漏洞。
    • 通过受控的方式完成数据交换。
  3. 内存保护:

    • 确保内核无法被用户空间直接操作,防止系统崩溃或数据泄露。

示例

用户空间程序
#include 
#include 
#include 
#include 

int main() {
    char write_data[] = "Hello, Kernel!";
    char read_data[128] = {0};
    int fd = open("/dev/hello", O_RDWR);

    if (fd < 0) {
        perror("Failed to open device");
        return -1;
    }

    // 写数据到设备
    write(fd, write_data, strlen(write_data));

    // 从设备读取数据
    read(fd, read_data, sizeof(read_data));
    printf("Data from kernel: %s\n", read_data);

    close(fd);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
驱动程序(运行于内核空间)
// kernel_buf由于是驱动程序中的数组,所以属于内核空间中的数组,它里面存储的数据属于内核空间中的数据
static char kernel_buf[128];

static ssize_t hello_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    size_t to_copy = min(sizeof(kernel_buf), size);
    size_t not_copied = copy_from_user(kernel_buf, buf, to_copy);

    if (not_copied)
        return -EFAULT;

    printk("Kernel received: %s\n", kernel_buf);
    return to_copy;
}

static ssize_t hello_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    size_t to_copy = min(sizeof(kernel_buf), size);
    size_t not_copied = copy_to_user(buf, kernel_buf, to_copy);

    if (not_copied)
        return -EFAULT;

    printk("Kernel sent: %s\n", kernel_buf);
    return to_copy;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

小结

  • copy_to_user 和 copy_from_user 是用户数据与内核数据交换的媒介函数。
  • 它们通过安全的方式完成用户空间和内核空间之间的数据传递,避免直接访问带来的风险。
  • 这些函数广泛用于 Linux 驱动程序中,确保内核和用户程序的可靠通信。
昊虹嵌入式技术交流群
QQ群名片
注:本文转载自blog.csdn.net的昊虹AI笔记的文章"https://blog.csdn.net/wenhao_ir/article/details/144950319"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top