Linux内核态和用户态的区别

当一个任务(进程)执行系统调用而陷入内核代码中执 行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中 执行。
当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。
当进程在执行用户自己的代码时,则称其处于用户运行态(用 户态)。即此时处理器在特权级最低的(3级)用户代码中运行。

当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核 态。因为中断处理程序将使用当前进程的内核栈。这与处于内核态的进程的状态有些类似。 

内核态与用户态是操作系统的两种运行级别,跟intel cpu没有必然的联系,intel cpu提供Ring0-Ring3三种级别的运行模式,Ring0级别最高,Ring3最低。Linux使用了Ring3级别运行用户态,Ring0作为 内核态,没有使用Ring1和Ring2。Ring3状态不能访问Ring0的地址空间,包括代码和数据。

Linux进程的4GB地址空间,3G-4G部 分大家是共享的,是内核态的地址空间,这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据。
用户运行一个程序,该程序所创建的进程开始是运 行在用户态的,如果要执行文件操作,网络数据发送等操作,必须通过write,send等系统调用,这些系统调用会调用内核中的代码来完成操作,这时,必 须切换到Ring0,然后进入3GB-4GB中的内核地址空间去执行这些代码完成操作,完成后,切换回Ring3,回到用户态。这样,用户态的程序就不能 随意操作内核地址空间,具有一定的安全保护作用。

至于说保护模式,是说通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程的地址空间中的数据。 

在内核态下,CPU可执行任何指令,在用户态下CPU只能执行非特权指令。当CPU处于内核态,可以随意进入用户态;而当CPU处于用户态,只能通过中断 的方式进入内核态。
一般程序一开始都是运行于用户态,当程序需要使用系统资源时,就必须通过调用软中断进入内核态.





内核态和用户态中sscanf的区别 

需求:将一个16进制的字符转转换成16进制的数值存储起来。譬如:"ABCD1234"转换之后,用一个字符数组存储,其值依次为0xAB,0xCD,0x12,0x34.
实现方法:考虑使用sscanf进行格式输入到buf中,将每两个16进制字符转换成一个char型的数字。
环境: kerne 2.6.18.3, gcc 4.1.2

本文欢迎自由转载,但请标明出处和本文链接,并保持本文的完整性。
CU: Godbach
Blog:http://blog.chinaunix.net/u/33048/index.html
Dec 30, 2009

现在用户空间测试一下,代码usscanf.c:

/*
*Test sscanf func in userspace
*/
#include <stdio.h>
#include <string.h>

#define HEXSTR_LEN 16

int main(void)
{

char hexstr[HEXSTR_LEN+1] = "1234567890ABCDEF";
unsigned char hex[HEXSTR_LEN/2] = {0x00};
int tmp = 0;
int i = 0;

printf("hex str: %s\n", hexstr);
printf("hex val: ");
for(i = 0; i < strlen(hexstr)/2; i++)
{
sscanf(hexstr + 2 * i, "%2x", &tmp);
hex[i] = (unsigned char)tmp;
printf("%.2x ", hex[i]);
tmp = 0;
}
printf("\n");
return 0;
}
测试结果如下:
[root@localhost ksscanf]# ./usscanf
hex str: 1234567890ABCDEF
hex val: 12 34 56 78 90 ab cd ef

这样的结果如预期,正确的将16进制字符串进了转换。

那么在内核态呢,内核态也有封装的sscanf供调用。那么我们同时在内核态下测试,代码ksscanf.c如下:

/*
*Test sscanf func in kernel
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>

#define HEXSTR_LEN 16

static int __init init(void)
{

char hexstr[HEXSTR_LEN+1] = "1234567890ABCDEF";
unsigned char hex[HEXSTR_LEN/2] = {0x00};
int tmp = 0;
int i = 0;

printk("sscanf test module init...\n");

printk("hex str: %s\n", hexstr);
printk("hex val: ");
for(i = 0; i < strlen(hexstr)/2; i++)
{
sscanf(hexstr + 2 * i, "%2x", &tmp);
hex[i] = (unsigned char)tmp;
printk("%.2x ", hex[i]);
tmp = 0;
}
printk("\n");
return 0;
}


static void __exit fini(void)
{
printk("sscanf test module exit...\n");
return ;
}
module_init(init);
module_exit(fini);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Godbach");
MODULE_DESCRIPTION("This is a LKM for testing sscanf.");
测试该模块的代码,执行结果如下:
Dec 30 15:03:49 localhost kernel: sscanf test module init...
Dec 30 15:03:49 localhost kernel: hex str: 1234567890ABCDEF
Dec 30 15:03:49 localhost kernel: hex val: ef ef ef ef ef ef ef ef
发现内核态转换的16进制数值全是ef,很明显是有问题的。经过多次测试并转换过程中打印tmp的数值发现,内核转换时一直用的是hexstr的最后八个字符,即“90ABCDEF”,然后用unsigned char进行截断,所以结果就是ef,hex的8个字节都是ef。

看来用户态和内核态使用sscanf时对已有字符串读取的方向不一样,用户态直接从hexstr的开头取自取字符,而内核态则取字符串最后有效的若干个16进制字符。

因此,有这样应用的时候,一定要注意这样的问题。

如何避免或者按照我们的意愿执行呢?
那就每次将hexstr中取2个字符到一个新的缓存hex_ch[2]中,然后对hex_ch执行sscanf,这样每次仅仅转换两个字符,就没有上述的问题了。
这里不再显示用户态修改后的执行结果,而仅贴出内核态修改后的代码和执行结果:
代码如下:

/*
*Test sscanf func in kernel
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>

#define HEXSTR_LEN 16

static int __init init(void)
{

char hexstr[HEXSTR_LEN+1] = "1234567890ABCDEF";
unsigned char hex[HEXSTR_LEN/2] = {0x00};
int tmp = 0;
int i = 0;
unsigned char hex_ch[2] = {0x00};
printk("sscanf test module init...\n");

printk("hex str: %s\n", hexstr);
printk("hex val: ");
for(i = 0; i < strlen(hexstr)/2; i++)
{
memset(hex_ch, 0x00, 2);
memcpy(hex_ch, hexstr + 2 * i, 2);
sscanf(hex_ch, "%2x", &tmp);
hex = (unsigned char)tmp;
printk("%.2x ", hex);
tmp = 0;
}
printk("\n");
return 0;
}


static void __exit fini(void)
{
printk("sscanf test module exit...\n");
return ;
}
module_init(init);
module_exit(fini);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Godbach");
MODULE_DESCRIPTION("This is a LKM for testing sscanf.");
执行结果如下:
Dec 30 15:30:25 localhost kernel: sscanf test module init...
Dec 30 15:30:25 localhost kernel: hex str: 1234567890ABCDEF
Dec 30 15:30:25 localhost kernel: hex val: 12 34 56 78 90 ab cd ef 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值