linux缺頁異常處理--內核空間[v3.10]
缺頁異常被觸發通常有兩種情況——
1.程序設計的不當導致訪問了非法的地址
2.訪問的地址是合法的,但是該地址還未分配物理頁框
下面解釋一下第二種情況,這是虛擬內存管理的一個特性。盡管每個進程獨立擁有3GB的可訪問地址空間,但是這些資源都是內核開出的空頭支票,也就是說進程手握着和自己相關的一個個虛擬內存區域(vma),但是這些虛擬內存區域並不會在創建的時候就和物理頁框掛鉤,由於程序的局部性原理,程序在一定時間內所訪問的內存往往是有限的,因此內核只會在進程確確實實需要訪問物理內存時才會將相應的虛擬內存區域與物理內存進行關聯(为相應的地址分配頁表項,並將頁表項映射到物理內存),也就是說這種缺頁異常是正常的,而第一種缺頁異常是不正常的,內核要采取各種可行的手段將這種異常帶來的破壞減到最小。
缺頁異常的處理函數为do_page_fault(),該函數是和體系結構相關的一個函數,缺頁異常的來源可分为兩種,一種是內核空間(訪問了線性地址空間的第4個GB),一種是用戶空間(訪問了線性地址空間的0~3GB),以X86架構为例,先來看內核空間異常的處理。
dotraplinkage void __kprobes
do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
struct vm_area_struct *vma;
struct task_struct *tsk;
unsigned long address;
struct mm_struct *mm;
int write;
int fault; tsk = current; //獲取當前進程
mm = tsk->mm; //獲取當前進程的地址空間 /* Get the faulting address: */
address = read_cr2(); //讀取CR2寄存器獲取觸發異常的訪問地址 ...
... if (unlikely(fault_in_kernel_space(address))) { //判斷address是否處於內核線性地址空間
if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {//判斷是否處於內核態
if (vmalloc_fault(address) >= 0)//處理vmalloc異常
return; if (kmemcheck_fault(regs, address, error_code))
return;
} /* Can handle a stale RO->RW TLB: */
/*異常發生在內核地址空間但不屬於上面的情況或上面的方式無法修正,
則檢查相應的頁表項是否存在,權限是否足夠*/
if (spurious_fault(error_code, address))
return; /* kprobes don't want to hook the spurious faults: */
if (notify_page_fault(regs))
return;
/*
* Don't take the mm semaphore here. If we fixup a prefetch
* fault we could otherwise deadlock:
*/
bad_area_nosemaphore(regs, error_code, address); return;
}
...
...
}
該函數傳遞進來的兩個参數--
regs包含了各個寄存器的值
error_code是觸發異常的錯誤類型,它的含義如下
/*
* Page fault error code bits:
*
* bit 0 == 0: no page found 1: protection fault
* bit 1 == 0: read access 1: write access
* bit 2 == 0: kernel-mode access 1: user-mode access
* bit 3 == 1: use of reserved bit detected
* bit 4 == 1: fault was an instruction fetch
*/
enum x86_pf_error_code { PF_PROT = 1 << 0,
PF_WRITE = 1 << 1,
PF_USER = 1 << 2,
PF_RSVD = 1 << 3,
PF_INSTR = 1 << 4,
};
首先要檢查該異常的觸發地址是不是位於內核地址空間 也就是address>=TASK_SIZE_MAX,一般为3GB。然後要檢查觸發異常時是否處於內核態,滿足這兩個條件就嘗試通過vmalloc_fault()來解决這個異常。由於使用vmalloc申請內存時,內核只會更新主內核頁表,所以當前使用的進程頁表就有可能因为未與主內核頁表同步導致這次異常的觸發,因此該函數試圖將address對應的頁表項與主內核頁表進行同步
static noinline int vmalloc_fault(unsigned long address)
{
unsigned long pgd_paddr;
pmd_t *pmd_k;
pte_t *pte_k; /* 確定觸發異常的地址是否處於VMALLOC區域*/
if (!(address >= VMALLOC_START && address < VMALLOC_END))
return -1; /*
* Synchronize this task's top level page-table
* with the 'reference' page table.
*
* Do _not_ use "current" here. We might be inside
* an interrupt in the middle of a task switch..
*/
pgd_paddr = read_cr3();//獲取當前的PGD地址
pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);//將當前使用的頁表和內核頁表同步
if (!pmd_k)
return -1; /*到這裏已經獲取了內核頁表對應於address的pmd,並且將該值設置给了當前使用頁表的pmd,
最後一步就是判斷pmd對應的pte項是否存在*/
pte_k = pte_offset_kernel(pmd_k, address);//獲取pmd對應address的pte項
if (!pte_present(*pte_k))//判斷pte項是否存在,不存在則失敗
return -1; return 0;
}
同步處理:
static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
{
unsigned index = pgd_index(address);
pgd_t *pgd_k;
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k; pgd += index; //記錄當前頁表pgd對應address的偏移
pgd_k = init_mm.pgd + index;//記錄內核頁表對應address的偏移 if (!pgd_present(*pgd_k))//內核PGD頁表對應的項不存在,則無法進行下一步,返回NULL
return NULL; /*
* set_pgd(pgd, *pgd_k); here would be useless on PAE
* and redundant with the set_pmd() on non-PAE. As would
* set_pud.
*/ /*獲取當前頁表對應address的PUD地址和內核頁表對應address的地址,並判斷pud_k對應的項是否存在*/
pud = pud_offset(pgd, address);
pud_k = pud_offset(pgd_k, address);
if (!pud_present(*pud_k))
return NULL; /*對pmd進行和上面類似的操作*/
pmd = pmd_offset(pud, address);
pmd_k = pmd_offset(pud_k, address);
if (!pmd_present(*pmd_k))
return NULL; if (!pmd_present(*pmd))//當前使用頁表對應的pmd項不存在,則修正pmd項使其和內核頁表的pmd_k項相同
set_pmd(pmd, *pmd_k);
else
BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k)); return pmd_k;
}
如果do_page_fault()函數執行到了bad_area_nosemaphore(),那麼就表明這次異常是由於對非法的地址訪問造成的。在內核中產生這样的結果的情況一般有兩種:
1.內核通過用戶空間傳遞的系統調用参數,訪問了無效的地址
2.內核的程序設計缺陷
第一種情況內核尚且能通過異常修正機制來進行修复,而第二種情況就會導致OOPS錯誤了,內核將強制用SIGKILL結束當前進程。
內核態的bad_area_nosemaphore()的實際處理函數为bad_area_nosemaphore()-->__bad_area_nosemaphore()-->no_context()
static noinline void
no_context(struct pt_regs *regs, unsigned long error_code,
unsigned long address)
{
struct task_struct *tsk = current;
unsigned long *stackend;
unsigned long flags;
int sig; /* Are we prepared to handle this kernel fault? */
/*fixup_exception()用於搜索異常表,並試圖找到一個對應該異常的例程來進行修正,
這個例程在fixup_exception()返回後執行*/
if (fixup_exception(regs))
return; /*
* 32-bit:
*
* Valid to do another page fault here, because if this fault
* had been triggered by is_prefetch fixup_exception would have
* handled it.
*
* 64-bit:
*
* Hall of shame of CPU/BIOS bugs.
*/
if (is_prefetch(regs, error_code, address))
return; if (is_errata93(regs, address))
return; /*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice:
*/
/* 走到這裏就說明異常確實是由於內核的程序設計缺陷導致的了,內核將
產生一個oops,下面的工作就是打印CPU寄存器和內核態堆棧的信息到控制台並
終結當前的進程*/
flags = oops_begin(); show_fault_oops(regs, error_code, address); stackend = end_of_stack(tsk);
if (*stackend != STACK_END_MAGIC)
printk(KERN_ALERT "Thread overran stack, or stack corrupted\n"); tsk->thread.cr2 = address;
tsk->thread.trap_no = 14;
tsk->thread.error_code = error_code; sig = SIGKILL;
if (__die("Oops", regs, error_code))
sig = 0; /* Executive summary in case the body of the oops scrolled away */
printk(KERN_EMERG "CR2: %016lx\n", address); oops_end(flags, regs, sig);
}
linux缺頁異常處理--內核空間[v3.10]的更多相关文章
- 第一章 Linux內核簡介
1. Linux是類Unix系統,但他不是Unix. 儘管Linux借鑑了Unix的許多設計並且實現了Unix的API(由Posix標準和其他Single Unix Specification定義的) ...
- 【转】Linux內核驅動之GPIO子系統(一)GPIO的使用 _蝸牛
原文网址:http://tc.chinawin.net/it/os/article-2512b.html 一 概述 Linux內核中gpio是最簡單,最常用的資源(和interrupt ,dma,ti ...
- 整理幾種常見PCB表面處理的優缺點
這只是一篇整理文,而且我個人僅從事過後段的電路板組裝,而未從事過電路板製程,所以有些見解純粹只是個人看法,如果有些不一樣的聲音或錯誤也歡迎留言討論. 隨著時代的演進,科技的進步,環保的要求,電子業也隨 ...
- 在 Windows 上遇到非常多 TIME_WAIT 連線時應如何處理
我們公司所代管的網站裡,有幾個流量是非常大的,在尖峰的時刻同時上線人數可能高達數千到數萬人,而在這個時候如果使用 netstat 或 TCPView 查看所有 TCP 連線時就會看到非常多處於 ...
- C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem
原帖:https://tokyo.zxproxy.com/browse.php?u=uG7kXsFlW1ZmaxKEvCzu8HrCJ0bXIAddA1s5dtIUZ%2FYzM1u9JI7jjKLT ...
- java 異常抛出 throw 與 return
package 異常; public class TestException { public TestException() { } boolean test ...
- Linux下安裝Oracle database內核參數設置
參考:1529864.1 ************************************************** RAM ...
- T-SQL 簡易小數處理
今天因應同事提的一則需求,寫了一段 CASE WHEN 的整數與小數處理 過程中居然踩了個雷,特此記錄下來 首先,需求如下: 當內容為整數或零時則去掉尾端的小數否則就顯示原本的小數內容 若內容為 NU ...
- 關於 WebClient wc = new WebClient() 下載第三方數據不能進安安信任異常
報錯異常:The underlying connection was closed: Could not establish trust relationship for SSL/TLS secure ...
随机推荐
- 2019-1-5-Windows-的-Pen-协议
title author date CreateTime categories Windows 的 Pen 协议 lindexi 2019-01-05 11:14:49 +0800 2019-01-0 ...
- resolver - 解析器(resolver) 配置文件
总览 (SYNOPSIS) /etc/resolv.conf 描述 (DESCRIPTION) 解析器(resolver) 是 C 函数库 中 的 一组 例程, 用于 访问 Internet 域名系统 ...
- socket - Linux 套接字
总览 #include <sys/socket.h> mysocket = socket(int socket_family, int socket_type, int protocol) ...
- BZOJ1076/Luogu2473 奖励关(SCOI2008)状压DP+期望DP
题意:给n(n<=15)种宝物宝物有价值w且每个宝物有一个前置宝物(即你必须先吃过它的所有前置宝物至少一次才能吃该宝物),共有m轮游戏,每一轮会在n种宝物等概率选一个出来,因为宝物价值可正可负你 ...
- AGC003[BCDEF]题解
2018-12-28 有点累EF明天再写叭=v= 2018-12-29 update EF B - Simplified mahjong 可以注意到 一段连续的非0序列都可以凑出 就是显然%2=0的可 ...
- Hibernate入门核心配置文件和orm元数据配置文件详解
框架是什么? 框架是用来提高开发效率的 封装了一些功能,我们需要使用这些功能时,调用即可,不用手动实现 所以框架可以理解为一个半成品的项目,只要懂得如何使用这些功能即可 Hibernate是完全面向对 ...
- ubuntu安装deb包(dpkg)
安装 sudo dpkg -i DEB_PACKAGE 卸载 sudo dpkg -r PACKAGE_NAME 重新配置已安装的包 Reconfigure an existing package 例 ...
- 存储系统设计——NVMe SSD性能影响因素一探究竟
目录1 存储介质的变革 2 NVME SSD成为主流 2.1 NAND FLASH介质发展 2.2 软件层面看SSD——多队列技术 2.3 深入理解SSD硬件 3 影响NVME SSD的性能因素 3. ...
- SQL Server 2008性能故障排查(三)——IO
接着上一章:CPU瓶颈 I/O瓶颈(I/O Bottlenecks): SQLServer的性能严重依赖I/O子系统.除非你的数据库完全加载到物理内存中,否则SQLServer会不断地把数据库文件从缓 ...
- ES6转ES5环境搭配
1.初始化项目 在项目根目录创建 package.json 文件 npm init //或者 npm init -y 2.安装babel-cli脚手架 npm install babel-cli -- ...