linux那些事之page fault(AMD64架构)(user space)(2)
do_user_addr_fault
用户空间地址处理是page fault主要处理流程,x86 64位系统主要是do_user_addr_fault()函数 该处理部分是x86架构特有部分 即与架构有关处理,整个函数处理逻辑过程可以总结为如下图所示:
do_user_addr_fault源码
/* Handle faults in the user portion of the address space */
static inline
void do_user_addr_fault(struct pt_regs *regs,unsigned long hw_error_code,unsigned long address)
{struct vm_area_struct *vma;struct task_struct *tsk;struct mm_struct *mm;vm_fault_t fault, major = 0;unsigned int flags = FAULT_FLAG_DEFAULT;tsk = current;mm = tsk->mm;//获取当前进程mm,发生的addr 属于当前进程/* kprobes don't want to hook the spurious faults: */if (unlikely(kprobe_page_fault(regs, X86_TRAP_PF))) //kprobe是否支持处理return;/** Reserved bits are never expected to be set on* entries in the user portion of the page tables.*/if (unlikely(hw_error_code & X86_PF_RSVD))//entry中保留位被置1,发生x86_PF_RSVD erropgtable_bad(regs, hw_error_code, address);/** If SMAP is on, check for invalid kernel (supervisor) access to user* pages in the user address space. The odd case here is WRUSS,* which, according to the preliminary documentation, does not respect* SMAP and will have the USER bit set so, in all cases, SMAP* enforcement appears to be consistent with the USER bit.*///smap特性,查看error code是否属于userif (unlikely(cpu_feature_enabled(X86_FEATURE_SMAP) &&!(hw_error_code & X86_PF_USER) &&!(regs->flags & X86_EFLAGS_AC))){bad_area_nosemaphore(regs, hw_error_code, address);return;}/** If we're in an interrupt, have no user context or are running* in a region with pagefaults disabled then we must not take the fault*/if (unlikely(faulthandler_disabled() || !mm)) {bad_area_nosemaphore(regs, hw_error_code, address);return;}/** It's safe to allow irq's after cr2 has been saved and the* vmalloc fault has been handled.** User-mode registers count as a user access even for any* potential system fault or CPU buglet:*///设置flagsif (user_mode(regs)) {local_irq_enable();flags |= FAULT_FLAG_USER;} else {if (regs->flags & X86_EFLAGS_IF)local_irq_enable();}perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);if (hw_error_code & X86_PF_WRITE)flags |= FAULT_FLAG_WRITE;if (hw_error_code & X86_PF_INSTR)flags |= FAULT_FLAG_INSTRUCTION;#ifdef CONFIG_X86_64/** Faults in the vsyscall page might need emulation. The* vsyscall page is at a high address (>PAGE_OFFSET), but is* considered to be part of the user address space.** The vsyscall page does not have a "real" VMA, so do this* emulation before we go searching for VMAs.** PKRU never rejects instruction fetches, so we don't need* to consider the PF_PK bit.*///vsyscall范围的page fault单独处理if (is_vsyscall_vaddr(address)) {if (emulate_vsyscall(hw_error_code, regs, address))return;}
#endif/** Kernel-mode access to the user address space should only occur* on well-defined single instructions listed in the exception* tables. But, an erroneous kernel fault occurring outside one of* those areas which also holds mmap_lock might deadlock attempting* to validate the fault against the address space.** Only do the expensive exception table search when we might be at* risk of a deadlock. This happens if we* 1. Failed to acquire mmap_lock, and* 2. The access did not originate in userspace.*/if (unlikely(!mmap_read_trylock(mm))) {if (!user_mode(regs) && !search_exception_tables(regs->ip)) {/** Fault from code in kernel from* which we do not expect faults.*/bad_area_nosemaphore(regs, hw_error_code, address);return;}
retry:mmap_read_lock(mm);} else {/** The above down_read_trylock() might have succeeded in* which case we'll have missed the might_sleep() from* down_read():*/might_sleep();}// vma查找结果处理vma = find_vma(mm, address);if (unlikely(!vma)) {bad_area(regs, hw_error_code, address);return;}if (likely(vma->vm_start <= address))goto good_area;if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {bad_area(regs, hw_error_code, address);return;}if (unlikely(expand_stack(vma, address))) {bad_area(regs, hw_error_code, address);return;}/** Ok, we have a good vm_area for this memory access, so* we can handle it..*/
good_area:if (unlikely(access_error(hw_error_code, vma))) {bad_area_access_error(regs, hw_error_code, address, vma);return;}/** If for any reason at all we couldn't handle the fault,* make sure we exit gracefully rather than endlessly redo* the fault. Since we never set FAULT_FLAG_RETRY_NOWAIT, if* we get VM_FAULT_RETRY back, the mmap_lock has been unlocked.** Note that handle_userfault() may also release and reacquire mmap_lock* (and not return with VM_FAULT_RETRY), when returning to userland to* repeat the page fault later with a VM_FAULT_NOPAGE retval* (potentially after handling any pending signal during the return to* userland). The return to userland is identified whenever* FAULT_FLAG_USER|FAULT_FLAG_KILLABLE are both set in flags.*///核心处理函数,所有处理器通用部分fault = handle_mm_fault(vma, address, flags);major |= fault & VM_FAULT_MAJOR;/* Quick path to respond to signals */if (fault_signal_pending(fault, regs)) {if (!user_mode(regs))no_context(regs, hw_error_code, address, SIGBUS,BUS_ADRERR);return;}/** If we need to retry the mmap_lock has already been released,* and if there is a fatal signal pending there is no guarantee* that we made any progress. Handle this case first.*/if (unlikely((fault & VM_FAULT_RETRY) &&(flags & FAULT_FLAG_ALLOW_RETRY))) {flags |= FAULT_FLAG_TRIED;goto retry;}mmap_read_unlock(mm);if (unlikely(fault & VM_FAULT_ERROR)) {mm_fault_error(regs, hw_error_code, address, fault);return;}/** Major/minor page fault accounting. If any of the events* returned VM_FAULT_MAJOR, we account it as a major fault.*///发生page fault 属于major或者minor 记录if (major) {tsk->maj_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address);} else {tsk->min_flt++;perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);}check_v8086_mode(regs, address, tsk);
}
结合上述流程图和源码主要流程:
- 获取当前进程mm,所发生的page fault异常地址就是属于该进程空间范围内。
- kprobes是否支持,如果支持,则该中断交友kprobe处理page fault(kprobe主要是用于监控内核运行的一个子系统)。
- 对硬件返回的page fault 具体error code进行检查处理:
- X86_PF_RSVD 类型错误不支持直接返回,由《linux那些事之page fault(AMD64架构)(1)》,可知该类型错误位为entry中的保留位被置成1,说明entry未初始化或者被某些修改造成无效。
- X86_PF_USER 错误处理,如果SMAP特性开启,如果error code不是user mode而是supervisor 造成的,那么直接返回错误 不处理。
- faulthandler_disabledL:根据注释解释,如果处于一个中断中且没有用户上下文,那么将没法处理page fault,直接返回,主要是此时不属于任何进程,无法知道该地址属于哪个进程。
- 根据实际情况设置flags标志位:
- 用户模式 flags标记为添加FAULT_FLAG_USER
- erro code 含有X86_PF_WRITE,则说明发生的page fault是写内存操作造成的,flags标记为设置为FAULT_FLAG_WRITE
- error code含有X86_PF_INSTR,则说明发的page fault是对指令读取造成的,添加FAULT_FLAG_INSTRUCTION
- 对发生page fault地址是否属于vsycall范围内,如果是则需要单独处理emulate_vsyscall,因为vsycall地址并没有创建一个真正的vma进行管理,通常vsycall地址位于高地址。
- 对mm 尝试加锁,后续需要mm中查找对应的vma。
- find_vma处理,查找发生page fault的地址是否被申请过处理部分。
- 如果没有查找到vma,进入bad_are处理并返回。
- 查找到离addr最近的vma的start 地址大于address,则调用expand_stack进行vma扩充,如果扩充失败进入bad_are 处理并直接返回。
- 查找到vma 且vma的start地址大于addreess ,进入good_are处理。
- 将error code中的权限与查找的vma权限做对比检查,如果vma权限允许继续操作,则进入下一步。
- handle_mm_fault 是page fault处理的核心函数,与架构无关通用处理部分。
- fault_signal_pending:根据handle_mm_fault 处理完成之后返回的faut类型决定是否向进程发生signal 停止当前进程。
- 处理完成之后判断 page fault类型属于major还是minor,并分别做统计,分别对应task_struct中maj_flt和min_flt 用于统计各种page fault次数。
//include/linux/sched.h
struct task_struct {... ...
/* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */
unsigned long min_flt;
unsigned long maj_flt;
... ...
};
《understanding linux virtual memory》能够更好说明详细说明上述过程,图如下:
上述流程虽然是基于2.6版本,但是可以看出整个流程到5.8版本中改变并不是很大,基本和5.8.10版本处理一致,说明处理的健壮性。
flags
将应用的erro code转换成flags,方便后续处理是do_user_addr_fault()函数的主要工作内容之一,所支持的flag列表如下:
flags | value | notes |
FAULT_FLAG_WRITE | 0x01 | page fault是由写内存触发 |
FAULT_FLAG_MKWRITE | 0x02 | addree对应PTE已经存在,且对该地址 进行写操作触发的page fault |
FAULT_FLAG_ALLOW_RETRY | 0x04 | 如果page fault发生堵塞,允许重新尝试触发该地址的page fault |
FAULT_FLAG_RETRY_NOWAIT | 0x08 | 当重新尝试page fault时,不等待或 是否mmap_lock |
FAULT_FLAG_KILLABLE | 0x10 | 发生page fault task 处于SIGKILL 区域 |
FAULT_FLAG_TRIED | 0x20 | 该地址page fault已经尝试过一次 |
FAULT_FLAG_USER | 0x40 | 该page faut时发生在用户空间 |
FAULT_FLAG_REMOTE | 0x80 | 该page fault对应地址不是当前进程空间 |
FAULT_FLAG_INSTRUCTION | 0x100 | 在取指令时发生page fault |
FAULT_FLAG_INTERRUPTIBLE | 0x200 | 该page fault可以被非致命signal打断 |
特别需要说明的时FAULT_FLAG_ALLOW_RETRY 和FAULT_FLAG_TRIED标记为可以用来说明是否允许再次对page fault进行尝试
- FAULT_FLAG_ALLOW_RETRY设置和FAULT_FLAG_TRIED不设置 说明允许page fault进行再次尝试,且这是第一次尝试
- FAULT_FLAG_ALLOW_RETRY和FAULT_FLAG_TRIED都设置 说明page fault已经尝试过一次,允许再次尝试
- FAULT_FLAG_ALLOW_RETRY和FAULT_FLAG_TRIED都不设置不允许对page fault进行尝试
- FAULT_FLAG_ALLOW_RETRY不设置和FAULT_FLAG_TRIED设置为非法状态,用于都不使用
major/minor page fault
major/minor是两种比较常见的page fault类型.
major page fault
major page fault又称hard page fault, 一般只要访问的虚拟内存地址 转换称物理地址之后,所访问的内存不在物理内存中,而是需要通过I/O设备或者磁盘中读取出来加载到内存中,由于有I/O访问操作因此非常耗时,是影响程序性能的重要因素,一般出现major page fault主要有以下几点:
- 由于虚拟内存允许申请比物理内存要大的空间,因此为了保存虚拟内存都能够映射到物理内存,需要将长期不用或者在一段时间不用的物理内存给置换swap out到磁盘等存储设备中,当再次访问该内存时,需要将数据从磁盘中swap int到内存中,因此是非常耗时。
- 一个应用程序经常需要使用到各种共享库,操作系统内核为了节省内存,在程序启动中并不会一次性把所有共享库的代码段加载到内存中,而是加载一部分,当要使用的代码指令还没有加载时,就会触发major page fault从磁盘中将代码指令加载到内存中。
- 使用特殊驱动开发的mmap,有些设备有自己的内存 比如GPU等异构系统,分配异构设备的内存,实际上也是major,理由是需要通过IO访问
minor page fault
minor page fault 又称为soft page fault,意思当要访问的虚拟地址发生page fault时,要访问的物理内存在内存中,而还没有为该虚拟地址建立物理内存映射关系,因此该类型page fault只需要建立映射 刷新MMU即可,产生minor page faul主要如下:
- 使用glibc的malloc和mmap匿名映射申请的虚拟空间,一般产生的page fault都是minor。
- 多个进程共享相同的数据,由于第一个进程已经将进程加载到内存中,因此后续进程只需要对该物理内存建立对应进程内的虚拟地址映射即可。
- 共享库,在多个进程使用共享库时,如果第一个进程已经将该共享库加载,后续进程使用该共享库时就不需要再次加载 发生的时minor page fault。
查看page fault方法
目前主要有两种方法可以查看一个程序产生的page fault,以便用来进行调优
ps命令
ps命令可以用来查看一个应用程序运行过程中产生的page fault次数,命令行格式为:
ps -o min_flt,maj_flt -C <program_name>
ps -o min_flt,maj_flt -p <pid>
例如:
以上是查看进程3717在运行过程中以及产生的page fault次数,其中minor page fault次数为1609,major page fault次数为0次
/usr/bin/time
/usr/bin/time 命令可以统计一个程序运行完之后 整个过程产生的minor 和major page fault次数:
/usr/bin/time -v <program name>
例如使用该命令查看ls 命令产生的page fault次数:
可以看到除了page fault之外 还有有其他详细信息统计,是程序调优过程中经常使用到的一个工具。
getrusage系统调用
getrusage系统调用是用于获取进程的详细信息,其中就包括page fault信息,本质上ps是通过该系统调用获取到的:
#include <sys/types.h>#include <sys/time.h>#include <sys/resource.h>#define RUSAGE_SELF 0#define RUSAGE_CHILDREN -1int getrusage(int who, struct rusage *usage); 当调用成功后,返回0,否则-1;
参数说明:
- who 支持的选项又RUSAGE_SELF,RUSAGE_CHILDREN等
- RSUAGE_SELF: 获取当前进程统计信息。
- RUSAGE_CHILDREN:获取调用进程的所有孙子进程的资源使用统计信息。如假设三个进程之间的关系为父进程、子进程和孙子进程,那么当子进程在wait()孙子进程时,孙子进程的资源使用值就会被加到子进程的RUSAFE_CHILDREN值上,当父进程执行一个wait()子进程的操作时,子进程和孙子进程的资源使用信息就会被加到父进程的RUSAGE_CHILDREN值上。而如果子进程没有wait()孙子进程,孙子进程的资源使用就不会被记录到父进程的RUSAGE_CHILDREN值中。ru_maxrss字段返回调用进程的所有子孙进程中最大驻留集大小(不是所有子孙进程之和)。
- RUSAGE_THRREAD:获取当前线程统计信息。
- struct rusage *usage: 返回的统计信息,其数据结构如下:
struct rusage {struct timeval ru_utime; /* user CPU time used */struct timeval ru_stime; /* system CPU time used */long ru_maxrss; /* maximum resident set size */long ru_ixrss; /* integral shared memory size */long ru_idrss; /* integral unshared data size */long ru_isrss; /* integral unshared stack size */long ru_minflt; /* page reclaims (soft page faults) */long ru_majflt; /* page faults (hard page faults) */long ru_nswap; /* swaps */long ru_inblock; /* block input operations */long ru_oublock; /* block output operations */long ru_msgsnd; /* IPC messages sent */long ru_msgrcv; /* IPC messages received */long ru_nsignals; /* signals received */long ru_nvcsw; /* voluntary context switches */long ru_nivcsw; /* involuntary context switches */
};
- ru_minflt和ru_majflt用来说明 发生的minor和major page fault次数
- 其他参数意义不再详细描述
wait3/wait4系统调用
wait3/wait4也可以获取到进程的资源使用情况,不过与getrusage函数不同的是,返回的ruagse是进程终止之后总的资源事情情况,并不是运行过程某一个时刻的资源使用情况:
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>pid_t wait3(int *wstatus, int options,struct rusage *rusage);pid_t wait4(pid_t pid, int *wstatus, int options,struct rusage *rusage);
两个API区别 wait3等待的是任意子进程,而wait4可以等待指定的某一个或者多个进程。
vm_fault_t
vm_fault_t为handle_mm_fault处理page fault之后返回的错误码,处理失败时候返回为非0,代表不能成功处理page fault的各种情况,所支持的fault值有:
name | value | notes |
VM_FAULT_OOM | 0x000001 | 内存不足,缺页异常处理无法申请物理内存,需要触发OOM |
VM_FAULT_SIGBUS | 0x000002 | 遇到page fault无法处理的错误,需要发生信号给内核终止该进程 |
VM_FAULT_MAJOR | 0x000004 | 该异常是major类型page fault |
VM_FAULT_WRITE | 0x000008 | 由于写内存触发的page fault |
VM_FAULT_HWPOISON | 0x000010 | HWPOISON特性,表明该页被中毒即出现硬件错误 |
VM_FAULT_HWPOISON_LARGE | 0x000020 | huge page 被标记为中毒,内存硬件出现错误 |
VM_FAULT_SIGSEGV | 0x000040 | 遇到无法处理的错误,内核发生SIGSEGV信息终止当前进程 |
VM_FAULT_NOPAGE | 0x000100 | 缺页异常处理函数安装新的PTE,不需要返回一个新page |
VM_FAULT_LOCKED | 0x000200 | 缺页异常处理函数持有内存锁 |
VM_FAULT_RETRY | 0x000400 | 缺页异常处理被堵塞,需要再次处理 |
VM_FAULT_FALLBACK | 0x000800 | huge page缺页处理失败,切换称小页即正常页进行处理 |
VM_FAULT_DONE_COW | 0x001000 | 处理写时复制情况 |
VM_FAULT_NEEDDSYNC | 0x002000 | 缺页处理函数没有修改页表,但是需要fsync同步 |
VM_FAULT_HINDEX_MASK | 0x0f0000 | HINDEX mask |
上述宏定义位于linux-5.8.10\include\linux\mm_types.h文件中:
/*** typedef vm_fault_t - Return type for page fault handlers.** Page fault handlers return a bitmask of %VM_FAULT values.*/
typedef __bitwise unsigned int vm_fault_t;/*** enum vm_fault_reason - Page fault handlers return a bitmask of* these values to tell the core VM what happened when handling the* fault. Used to decide whether a process gets delivered SIGBUS or* just gets major/minor fault counters bumped up.** @VM_FAULT_OOM: Out Of Memory* @VM_FAULT_SIGBUS: Bad access* @VM_FAULT_MAJOR: Page read from storage* @VM_FAULT_WRITE: Special case for get_user_pages* @VM_FAULT_HWPOISON: Hit poisoned small page* @VM_FAULT_HWPOISON_LARGE: Hit poisoned large page. Index encoded* in upper bits* @VM_FAULT_SIGSEGV: segmentation fault* @VM_FAULT_NOPAGE: ->fault installed the pte, not return page* @VM_FAULT_LOCKED: ->fault locked the returned page* @VM_FAULT_RETRY: ->fault blocked, must retry* @VM_FAULT_FALLBACK: huge page fault failed, fall back to small* @VM_FAULT_DONE_COW: ->fault has fully handled COW* @VM_FAULT_NEEDDSYNC: ->fault did not modify page tables and needs* fsync() to complete (for synchronous page faults* in DAX)* @VM_FAULT_HINDEX_MASK: mask HINDEX value**/
enum vm_fault_reason {VM_FAULT_OOM = (__force vm_fault_t)0x000001,VM_FAULT_SIGBUS = (__force vm_fault_t)0x000002,VM_FAULT_MAJOR = (__force vm_fault_t)0x000004,VM_FAULT_WRITE = (__force vm_fault_t)0x000008,VM_FAULT_HWPOISON = (__force vm_fault_t)0x000010,VM_FAULT_HWPOISON_LARGE = (__force vm_fault_t)0x000020,VM_FAULT_SIGSEGV = (__force vm_fault_t)0x000040,VM_FAULT_NOPAGE = (__force vm_fault_t)0x000100,VM_FAULT_LOCKED = (__force vm_fault_t)0x000200,VM_FAULT_RETRY = (__force vm_fault_t)0x000400,VM_FAULT_FALLBACK = (__force vm_fault_t)0x000800,VM_FAULT_DONE_COW = (__force vm_fault_t)0x001000,VM_FAULT_NEEDDSYNC = (__force vm_fault_t)0x002000,VM_FAULT_HINDEX_MASK = (__force vm_fault_t)0x0f0000,
};/* Encode hstate index for a hwpoisoned large page */
#define VM_FAULT_SET_HINDEX(x) ((__force vm_fault_t)((x) << 16))
#define VM_FAULT_GET_HINDEX(x) (((__force unsigned int)(x) >> 16) & 0xf)#define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | \VM_FAULT_SIGSEGV | VM_FAULT_HWPOISON | \VM_FAULT_HWPOISON_LARGE | VM_FAULT_FALLBACK)#define VM_FAULT_RESULT_TRACE \{ VM_FAULT_OOM, "OOM" }, \{ VM_FAULT_SIGBUS, "SIGBUS" }, \{ VM_FAULT_MAJOR, "MAJOR" }, \{ VM_FAULT_WRITE, "WRITE" }, \{ VM_FAULT_HWPOISON, "HWPOISON" }, \{ VM_FAULT_HWPOISON_LARGE, "HWPOISON_LARGE" }, \{ VM_FAULT_SIGSEGV, "SIGSEGV" }, \{ VM_FAULT_NOPAGE, "NOPAGE" }, \{ VM_FAULT_LOCKED, "LOCKED" }, \{ VM_FAULT_RETRY, "RETRY" }, \{ VM_FAULT_FALLBACK, "FALLBACK" }, \{ VM_FAULT_DONE_COW, "DONE_COW" }, \{ VM_FAULT_NEEDDSYNC, "NEEDDSYNC" }
了解各个情况处理,后续page fault详细处理handle_mm_fault函数非常重要。
参考资料
Understanding page faults and memory swap-in/outs: when should you worry? | Scout APM Blog
虚拟内存 和 page fault 的解释 - 知乎
《understanding linux virtual memory》
HWPOISON [LWN.net]
https://marc.info/?l=git-commits-head&m=128867595005864&w=2
linux那些事之page fault(AMD64架构)(user space)(2)相关推荐
- linux那些事之page fault(AMD64架构)(1)
应用程序或者内核都是运行在虚拟内存空间之中,kernel 启动完成之后如果一个虚拟地址要访问物理内存需要通过CPU MMU硬件进行地址转换,整个虚拟地址访问物理内存逻辑过程如下: kernel 启动完 ...
- Linux内存page,【原创】(十四)Linux内存管理之page fault处理
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- linux那些事之 page translation(硬件篇)
Page Translation 以<AMD64 Architecture Programmer's manual volums>从硬件角度说明一个虚拟地址如何转成对应物理页.AM64 地 ...
- linux那些事之 page table基本操作
通过遍历page table查找一个虚拟地址已经做了对应的物理内存映射是一个很常用的操作,因此整个walk遍历过程耗时要尽量小. show_pte() 以show_pte()函数为例说明如何根据一个虚 ...
- linux那些事之page table
内核中page table决定了一个虚拟地址如何查找到对应的物理地址关键,维护着整个整个虚拟地址与物理地址对应信息,是实现整个内存虚拟化的基础.在虚拟地址到物理地址的转换过程中为了减少整个物理空间的占 ...
- linux那些事之page cache
page cache page cache又称高速缓存,主要是针对文件文件系统,为了减少不必要的磁盘IO操作(读/写)造成卡顿问题,内核将磁盘文件中的内容缓存到内存中,并选择适当时机对磁盘进行读写操作 ...
- linux那些事之TLB(Translation-Lookaside Buffer)无效操作
TLB 为了加速虚拟地址转换物理地址过程,CPU内部一般都集成TLB硬件单元,通过缓存存取虚拟地址与物理地址映射关系,避免再通过MMU 通过多级查表引入多次内存开销,直接将映射关系存储到硬件单元中,本 ...
- linux那些事之中断与异常(AMD64架构)_2
内核中断初始化过程 <中断与异常(AMD64架构)_1>,主要从硬件角度分析amd64 x86 cpu 中的一些基本概念,以及如何配置x86 cpu的中断向量表等.由于中断需要在内核启动较 ...
- linux那些事之中断与异常(AMD64架构)_1
中断与异常 中断及异常是学习操作系统必备知识,通常CPU时将其设计作为打破当前执行程序流程的一种手段,同时也是cpu与外部硬件交互的一种手段. 一般而言中断可以分为可以屏蔽(maskable)和不可屏 ...
最新文章
- P2647 最大收益
- Swift之深入解析基于闭包的类型擦除
- Linux对包管理阐述
- Linux+php+memcache+APC加速PHP网站
- 真格量化——做空波动率卖期权策略
- 这两天在学习微信小程序,不可避免的又要用到一些图标,通过问朋友解决了这个事情 想分享给你们一下 希望对你们有用...
- Java中成员变量、局部变量和静态变量的区别
- TensorRT 进阶用法
- 门诊管理系统开发能提高医生的诊疗水平和质量吗
- linux shell脚本查找局域网内所有已连接的设备ip
- 2.3.1 TextView(文本框)详解
- Android EditText 只能输入数字
- bugku ctf 备份是个好习惯 (听说备份是个好习惯)
- 理解PeopleSoft HRMS人力资源管理系统
- 【C语言学习】————操作符、关键字
- 如何用C#+WinRAR 实现压缩
- 【学习笔记】图像超分辨
- python魔方程序算法_python魔方程序算法_python算法(一)
- Linux(五)Linux远程连接管理工具xftp,xshell使用
- Mac OS 10.12 - 如何能够像在Windows一样切换中英文输入法和大小写键?
热门文章
- Jeewx-Boot 1.0.3 版本发布,基于SpringBoot的免费微信管家平台
- Windows平台下安装证书文件cer的步骤
- Chrome Workspace开发者调试工具
- eclipse中为了format的代码更加好看,少换行,可以设置java、xml、jsp的代码line width。
- 最大似然估计【MLE】与最大后验概率【MAP】
- 20180927-1
- python学习之老男孩python全栈第九期_day017作业
- 新建maven的pom.xml第一行出错的解决思路
- 【CSS+HTML】关于字体的说明
- 如何使用HTML5创建在线精美简历