[linux0.11/mm/memory.c]
// 验证线性地址是否可写
void write_verify(unsigned long address)
{
unsigned long page;
// 如果对应页表为空的话,直接返回
if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
return;
page &= 0xfffff000;
page += ((address>>10) & 0xffc);
// 经过运算后page为页表项的内容,指向实际的一页物理地址
if ((3 & *(unsigned long *) page) == 1) // 验证页面是否可写,不可写则执行un_wp_page,取消写保护.
un_wp_page((unsigned long *) page);
return;
}
但是如果每次在用户空间复制数据时,都要做这种检查是很浪费时间的,毕竟坏指针是很少
存在的,在新内核中的做法是,在从用户空间复制数据时,取消验证指针合法性的检查,
只多地址范围的检查,就象access_ok()所做的那样,一但碰上了坏指针,就要页异常出错处理
程序去处理它了.我们去看看do_page_fault函数.
[arch/asm-i386/mm/fault.c/do_page_falut()]
fastcall void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
...
...
if (!down_read_trylock(&mm->mmap_sem)) {
if ((error_code & 4) == 0 &&
!search_exception_tables(regs->eip))
goto bad_area_nosemaphore;
down_read(&mm->mmap_sem);
}
...
...
bad_area_nosemaphore:
...
no_context:
if (fixup_exception(regs))
return;
...
...
}
error_code保存的是出错码,(error_code & 4) == 0代表产生异常的原因是在内核中.
它调用fixup_exception(regs)来处理这个问题.既然出错了,那么如何来修复它呢?
先看下fixup_exception()函数的实现:
[arch/asm-i386/mm/extable.c]
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
...
fixup = search_exception_tables(regs->eip);
if (fixup) {
regs->eip = fixup->fixup;
return 1;
}
...
}
[kernel/extable.c]
const struct exception_table_entry *search_exception_tables(unsigned long addr)
{
const struct exception_table_entry *e;
e = search_extable(__start___ex_table, __stop___ex_table-1, addr);
if (!e)
e = search_module_extables(addr);
return e;
}
[/lib/extable.c]
const struct exception_table_entry *
search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
mid = (last - first) / 2 + first;
if (mid->insn < value)
first = mid + 1;
else if (mid->insn > value)
last = mid - 1;
else
return mid;
}
return NULL;
}
在内核中有个异常出错地址表,在地址表中有个出错地址的修复地址也气对应,它结构如下:
[/include/asm-i386/uaccess.h]
struct exception_table_entry
{
unsigned long insn, fixup;
};
insn是产生异常指令的地址,fixup用来修复出错地址的地址,也就是当异常发生后,用它的
地址来替换异常指令发生的地址。__copy_user_zeroing中的.section __ex_table代表异常出错
地址表的地址,.section .fixup代表修复的地址。他们都是elf文件格式中的2个特殊节。
".section __ex_table,\"a\"\n" \
" .align 4\n" \
" .long 4b,5b\n" \
" .long 0b,3b\n" \
" .long 1b,6b\n"
4b,5b的意思是当出错地址在4b标号对应的地址上时,就转入5b标号对应的地址去接着运行,
也就是修复的地址。依次类推。所以理解这一点后,fixup_exception()函数就很容易看明白了
就是根据出错地址搜索异常地址表,找到对应的修复地址,跳转到那里去执行就ok了。
ok,到这里copy_from_user函数也就分析完了,如果有什么不明白的话,可以通过阅读
/usr/src/linux/Documentation/exception.txt来得到更多关于异常处理方面的知识。
copy_from_user的具体实现 2
精选 转载
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Unity 特性[SerializeReference]搭配CustomEditor实现可生成具体子类并显示子类内容的Inspector面板
先看一下官方对新增特性[SerializeReference]的描述https://docs.unity3d.com/cn/2019.4/ScriptReference/SerializeReference.html简而言之: 默认情况下,不支持多态字段,用 [SerializeReference] 修饰字段可指示 Unity“按引用”而非“按值”序列化字段。借助这个特性可以实现字段的多
多态 SerializeReference U3D Unity3d