Linux 内核空间和用户空间通信
Linux 操作系统将 4GB 的虚拟地址空间分为两部分:最高的 1GB 字节(从虚拟地址 0xC0000000 到 0xFFFFFFFF)供内核使用,称为“内核空间”;而将较低的 3GB 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF)供各个进程使用,称为“用户空间”。在嵌入式设计中,经常需要进行内核空间和用户空间的信息交互。
1. 处理器状态
处理器总是处于以下三种状态之一:
A. 内核态,运行于进程上下文,内核代表进程运行于内核空间;
B. 内核态,运行于中断上下文,包括硬中断和软中断;
C. 用户态,运行于用户空间。
1.2 不同状态的限制
根据上面的状态分类,内核空间和用户空间之间的信息交互就分为两类,即中断上下文内核态空间与进程空间信息交互;进程上下文内核态空间和进程空间信息交互。
内核态环境进入内核态的方式局限性说明:
在进程上下文中,通过系统调用进入内核态,内核态代码与该进程相关。内核空间和进程空间的虚拟地址不同,不能直接传递信息。
中断上下文:
硬件触发中断,或内核中挂接软中断。不与特定的进程相关。内核空间和进程空间的虚拟地址不同,不能直接传递信息。中断不能睡眠,不能运行引起阻塞的函数。
2. 各种通信方式
2.1 信号
在进程中使用函数 signal() 或 sigaction() 安装信号时指定了关联的函数。在内核空间相进程发送信号,从内核空间返回进程空间时检查并执行相应的关联函数。在进程中可以使用 pause() 函数进入睡眠,在有信号产生并执行了相应的关联函数后进程被唤醒,继续执行。可以使用这种方式实现内核空间和用户空间的同步。
2.2 信号量
虽然原理一样,但内核空间和用户空间的信号量是完全两套系统,所以信号量不能用于内核空间和用户空间信息交互。
2.3 无名管道
无名管道只适用于有关系的进程之间通信。不能用于内核空间和用户空间信息交互。
2.4 get_user()/put_user()
get_user(x, ptr) :本函数是在内核中被调用,获取用户空间指定地址的数值(一个字节或字)并保存到内核变量 x 中,ptr 为用户空间的地址。
put_user(x, ptr) :在内核中被调用,将内核空间的变量 x 的数值(一个字节或字)保存到用户空间指定地址处,prt 为用户空间地址。
2.5 copy_from_user()/copy_to_user()
主要应用于设备驱动中读写函数中,通过系统调用触发,在当前进程上下文内核态运行(即当前进程通过系统调用触发)。
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
这些函数用于进程上下文内核态空间,即通常在系统调用函数中使用该函数,如设备驱动中 ioctl 函数中。
在 Linux 内核空间和用户空间通信中,需要使用特殊的函数和方法来实现信息交互,例如 signal、get_user()/put_user()、copy_from_user()/copy_to_user() 等。这些函数和方法可以帮助开发者更好地理解和实现 Linux 内核空间和用户空间的通信。