Linux系统的内存管理机制学习

Linux内核:内存管理——伙伴系统 还是和之前分析过程一样,我们首先概述一下这个系统中涉及到的主要数据结构,如下图所示,(1)在没有内存域中(ZONE),有一个per CPU变量pageset,这个结构管理这个域的单页分配工作,内核中分配单个页可能是最频繁的一项操作,如此频繁的操作如果直接从伙伴系统中分配单个页面,可能会引起伙伴系统频繁的拆分和合并大页,对系统性能造成比较大的冲击。第一剑式,随意而为。比如下图中,每一个格子代表一个page,蓝色表示被分配了,很明显,系统只分配了很小的一部分内存,但是如果我想再分配连续的8个页面已经不可能了。 阅读详情
Linux系统的内存管理机制学习
2010年01月14日 星期四 20:31

一直对Linux的内存管理机制模模糊糊的,这两天有时间,就把Intel的手册和 《Understanding the Linux Kernel》还有《Understanding the Linux® Virtual Memory Manager》和内存管理相关的都仔细看了一遍。以前老是对分页的地方不是很清楚,这次看到手册上页表里面的物理地址是怎么存放的,就有豁然开朗的感觉,看其他部分代码也就很容易理解了。



上面所有地址转换机制由cpu硬件实现,但软件必须设好好相应的分段用的寄存器和分页用的页表。

逻辑地址通常也成为 “长指针(far pointer)”,有16位的“segment selector” 和32位的offset 组成。offset也就是我们C语言编程时,32位的指针类型的值。“segment selector”则是保存在cs ds等段寄存器中的,一般不是有程序自己修改,而是系统自己修改或者编译器指定。经过两次转换之后才得到物理地址。x86的pc机上物理地址是等于总线地址的。32位机器上cpu有32条地址总线管脚和外面的内存等设备相连的。

为什么要搞“分段”,然后是“分页”这么两套机制呢?一个好处是让所有的程序都能获得一个统一的地址空间,不同的进程可以共同拥有一个同样的地址空间比如0x12345678对应的一块内存,但两个进程经过不同的映射方式,把这个同样的地址映射到不同的物理内存上去。还有就是所有的进程都可以0~3G的进程地址空间,即使你的物理内存只有比如说512M或者1G的时候。系统甚至可以把你没有用到的内存暂时保存到硬盘上去。在32为机器上逻辑地址是可以达到2的32次方4G那么多的,但Linux系统人为的划分一下逻辑地址,让1~3G的线性地址作为应用程序的可访问地址,3~4G的地址作为内核空间的可访问地址了。内核为了维护这个逻辑地址到物理地址的映射是要做很多工作的,这就是标题所说的“Linux系统的内存管理机制”,呵呵


上面就是“分段”的原理图,处理器从“段寄存器”的16位数据中得到一个“段选择符 (Segment Selector )”,然后根据高13位得到一个index,第3位确定是去Global Descriptor Table (GDT ) 或者Local Descriptor Table(LDT)的数组表中得到“段描述符Segment Descriptors ”。其中GDT 和LDT的线性地址(linear address)的起始地址和数组抵消分别保存在cpu的GDTR寄存器(Global Descriptor Table Register (GDTR))和Local Descriptor Table Register (LDTR)中。

不过Linux应该是有限度的使用了分段功能的,应该有点类似上图,所以Linux系统上面只是用分段功能来区分ring 0和ring3的校验,Linux上面的逻辑地址和线性地址应该是相等的。 仅有的_ _USER_CS, _ _USER_DS, _ _KERNEL_CS, _ _KERNEL_DS 几个段选择符起始地址也都是从0开始的。详见《Understanding the Linux Kernel》一书。




上面是分页的图示,首先cpu的cr3寄存器保存第一级页表的物理地址,然后每个页表项(在32位机器上就是一个32位整数)除了包含页的标志位外,有20位是用于指定下一级页表的物理地址的。因为一个页框大小就是4086(2的12次方)字节,所以页表的地址用12+20位是刚好可以表示32位地址空间的。对于页表来说,20位的物理地址,线性地址中的10位偏移值表明一个页表最多可以表示1024项,页表项大小为4个字节,刚好填满20位地址空间。
Both the Directory and the Table fields are 10 bits long, so Page Directories and Page Tables can include up to 1,024 entries. It follows that a Page Directory can address up to 1024 x 1024 x 4096=232 memory cells, as you'd expect in 32-bit addresses.

我以前就因为不理解每个页表项里面指定了下一级页表的物理地址,所以总是看的不明白书上说什么。

cr3寄存器的值是由内核指定的,所有的内核进程应该使用同一个叫做“主内核页表”的,不过其他内核固定映射的也用特别的一个。不同的用户进程应该是用不同的一级页表的,所以cr3寄存器就要在切换进程的时候修改吧,这个值保存在进程对应的memory descriptor 结mm_struct 的

pgd_t *

pgd

里面。

而第二级以后的页表都是到必须使用的时候才分配的。

 

9.2.1. Memory Descriptor of Kernel Threads

Kernel threads run only in Kernel Mode, so they never access linear addresses below TASK_SIZE (same as PAGE_OFFSET, usually 0xc0000000). Contrary to regular processes, kernel threads do not use memory regions, therefore most of the fields of a memory descriptor are meaningless for them.

Because the Page Table entries that refer to the linear address above TASK_SIZE should always be identical, it does not really matter what set of Page Tables a kernel thread uses. To avoid useless TLB and cache flushes, a kernel thread uses the set of Page Tables of the last previously running regular process. To that end, two kinds of memory descriptor pointers are included in every process descriptor: mm and active_mm.

The mm field in the process descriptor points to the memory descriptor owned by the process, while the active_mm field points to the memory descriptor used by the process when it is in execution. For regular processes, the two fields store the same pointer. Kernel threads, however, do not own any memory descriptor, thus their mm field is always NULL. When a kernel thread is selected for execution, its active_mm field is initialized to the value of the active_mm of the previously running process (see the section "The schedule( ) Function" in Chapter 7).

There is, however, a small complication. Whenever a process in Kernel Mode modifies a Page Table entry for a "high" linear address (above TASK_SIZE), it should also update the corresponding entry in the sets of Page Tables of all processes in the system. In fact, once set by a process in Kernel Mode, the mapping should be effective for all other processes in Kernel Mode as well. Touching the sets of Page Tables of all processes is a costly operation; therefore, Linux adopts a deferred approach.

We already mentioned this deferred approach in the section "Noncontiguous Memory Area Management" in Chapter 8: every time a high linear address has to be remapped (typically by vmalloc( ) or vfree( )), the kernel updates a canonical set of Page Tables rooted at the swapper_pg_dir master kernel Page Global Directory (see the section "Kernel Page Tables" in Chapter 2). This Page Global Directory is pointed to by the pgd field of a master memory descriptor , which is stored in the init_mm variable.[*]

We mentioned in the section "Kernel Threads" in Chapter 3 that the swapper process uses init_mm during the initialization phase. However, swapper never uses this memory descriptor once the initialization phase completes.

Later, in the section "Handling Noncontiguous Memory Area Accesses," we'll describe how the Page Fault handler takes care of spreading the information stored in the canonical Page Tables when effectively needed.

 

从上面的解释可以知道内核空间使用的是最近运行的普通进程的页表集,因为所有进程的3~4G这个内核空间页表项总是一样的。

内核里 想去访问用户进程地址空间(1~3G)时,就可以直接去访问的,就能使用普通的memcpy函数因为他们是属于同一个页表的,但如果你传的是一个无效的地 址,那么就会导致内核挂掉。而使用系统推荐使用的copy_from_user 和copy_to_user函数来操作的话,他这两个函数除了实现memcpy的功能外,还检查你传过来的进程空间地址是不是正确的。如果你读到一个无效 的地址,cpu产生缺页异常后就调用到do_page_fault函数,这个函数将处理“非法地址/页不在当前内存”等情况,而且如果他通过cpu的 eip寄存器检查到这个错误是 copy_from_user 产生的,还会调用 copy_from_user提供的一段fixup代码来修复错误让copy_from_user返回一个错误的返回值。copy_from_user比 memcpy更好的就是他可以让你非法地址也能从异常中恢复回来,让内核继续跑下去。而memcpy碰到非法地址,到了do_page_fault函数之 后找不到fixup代码,就会让内核崩溃了。很多时候我们并不能保证进程空间地址是不是合法的,这就是你应该去使用copy_from_user 系列函数的时候了。

几本书上都对这个导致的页异常错误的动态地址修复代码方法做了解释, 《Understanding.the.Linux.Kernel.3rd.Ed》的第十章,“系统调用”讲到访问进程空间的参数时说到这个, 《Understanding the Linux? Virtual Memory Manager》的4.5,4.7小节也说到这个技术。

10.4.2. Accessing the Process Address Space

System call service routines often need to read or write data contained in the process's address space. Linux includes a set of macros that make this access easier. We'll describe two of them, called get_user( ) and put_user( ). The first can be used to read 1, 2, or 4 consecutive bytes from an address, while the second can be used to write data of those sizes into an address.

Each function accepts two arguments, a value x to transfer and a variable ptr. The second variable also determines how many bytes to transfer. Thus, in get_user(x,ptr), the size of the variable pointed to by ptr causes the function to expand into a _ _get_user_1( ), _ _get_user_2( ), or _ _get_user_4( ) assembly language function. Let's consider one of them, _ _get_user_2( ):

_ _get_user_2:
addl $1, %eax
jc bad_get_user
movl $0xffffe000, %edx /* or 0xfffff000 for 4-KB stacks */
andl %esp, %edx
cmpl 24(%edx), %eax
jae bad_get_user
2: movzwl
-1(%eax), %edx
xorl %eax, %eax
ret
bad_get_user:
xorl %edx, %edx
movl $-EFAULT, %eax
ret

The eax register contains the address ptr of the first byte to be read. The first six instructions essentially perform the same checks as the access_ok( ) macro: they ensure that the 2 bytes to be read have addresses less than 4 GB as well as less than the addr_limit.seg field of the current process. (This field is stored at offset 24 in the thread_info structure of current, which appears in the first operand of the cmpl instruction.)

If the addresses are valid, the function executes the movzwl instruction to store the data to be read in the two least significant bytes of edx register while setting the high-order bytes of edx to 0; then it sets a 0 return code in eax and terminates. If the addresses are not valid, the function clears edx, sets the -EFAULT value into eax, and terminates.

The put_user(x,ptr) macro is similar to the one discussed before, except it writes the value x into the process address space starting from address ptr. Depending on the size of x, it invokes either the _ _put_user_asm( ) macro (size of 1, 2, or 4 bytes) or the _ _put_user_u64( ) macro (size of 8 bytes). Both macros return the value 0 in the eax register if they succeed in writing the value, and -EFAULT otherwise.

Several other functions and macros are available to access the process address space in Kernel Mode; they are listed in Table 10-1. Notice that many of them also have a variant prefixed by two underscores (_ _). The ones without initial underscores take extra time to check the validity of the linear address interval requested, while the ones with the underscores bypass that check. Whenever the kernel must repeatedly access the same memory area in the process address space, it is more efficient to check the address once at the start and then access the process area without making any further checks.

Table 10-1. Functions and macros that access the process address space

Function

Action

get_user _ _get_user

Reads an integer value from user space (1, 2, or 4 bytes)

put_user _ _put_user

Writes an integer value to user space (1, 2, or 4 bytes)

copy_from_user _ _copy_from_user

Copies a block of arbitrary size from user space

copy_to_user _ _copy_to_user

Copies a block of arbitrary size to user space

strncpy_from_user _ _strncpy_from_user

Copies a null-terminated string from user space

strlen_user strnlen_user

Returns the length of a null-terminated string in user space

clear_user _ _clear_user

Fills a memory area in user space with zeros


10.4.3. Dynamic Address Checking: The Fix-up Code

As seen previously, access_ok( ) makes a coarse check on the validity of linear addresses passed as parameters of a system call. This check only ensures that the User Mode process is not attempting to fiddle with the kernel address space; however, the linear addresses passed as parameters still might not belong to the process address space. In this case, a Page Fault exception will occur when the kernel tries to use any of such bad addresses.

Before describing how the kernel detects this type of error, let's specify the three cases in which Page Fault exceptions may occur in Kernel Mode. These cases must be distinguished by the Page Fault handler, because the actions to be taken are quite different.

  1. The kernel attempts to address a page belonging to the process address space, but either the corresponding page frame does not exist or the kernel tries to write a read-only page. In these cases, the handler must allocate and initialize a new page frame (see the sections "Demand Paging" and "Copy On Write" in Chapter 9).

  2. The kernel addresses a page belonging to its address space, but the corresponding Page Table entry has not yet been initialized (see the section "Handling Noncontiguous Memory Area Accesses" in Chapter 9). In this case, the kernel must properly set up some entries in the Page Tables of the current process.

  3. Some kernel functions include a programming bug that causes the exception to be raised when that program is executed; alternatively, the exception might be caused by a transient hardware error. When this occurs, the handler must perform a kernel oops (see the section "Handling a Faulty Address Inside the Address Space" in Chapter 9).

  4. The case introduced in this chapter: a system call service routine attempts to read or write into a memory area whose address has been passed as a system call parameter, but that address does not belong to the process address space.

The Page Fault handler can easily recognize the first case by determining whether the faulty linear address is included in one of the memory regions owned by the process. It is also able to detect the second case by checking whether the corresponding master kernel Page Table entry includes a proper non-null entry that maps the address. Let's now explain how the handler distinguishes the remaining two cases.

10.4.4. The Exception Tables

The key to determining the source of a Page Fault lies in the narrow range of calls that the kernel uses to access the process address space. Only the small group of functions and macros described in the previous section are used to access this address space; thus, if the exception is caused by an invalid parameter, the instruction that caused it must be included in one of the functions or else be generated by expanding one of the macros. The number of the instructions that address user space is fairly small.

Therefore, it does not take much effort to put the address of each kernel instruction that accesses the process address space into a structure called the exception table. If we succeed in doing this, the rest is easy. When a Page Fault exception occurs in Kernel Mode, the do_ page_fault( ) handler examines the exception table: if it includes the address of the instruction that triggered the exception, the error is caused by a bad system call parameter; otherwise, it is caused by a more serious bug.

Linux defines several exception tables . The main exception table is automatically generated by the C compiler when building the kernel program image. It is stored in the _ _ex_table section of the kernel code segment, and its starting and ending addresses are identified by two symbols produced by the C compiler: _ _start_ _ _ex_table and _ _stop_ _ _ex_table.

Moreover, each dynamically loaded module of the kernel (see Appendix B) includes its own local exception table. This table is automatically generated by the C compiler when building the module image, and it is loaded into memory when the module is inserted in the running kernel.

Each entry of an exception table is an exception_table_entry structure that has two fields:


insn

The linear address of an instruction that accesses the process address space


fixup

The address of the assembly language code to be invoked when a Page Fault exception triggered by the instruction located at insn occurs

The fixup code consists of a few assembly language instructions that solve the problem triggered by the exception. As we will see later in this section, the fix usually consists of inserting a sequence of instructions that forces the service routine to return an error code to the User Mode process. These instructions, which are usually defined in the same macro or function that accesses the process address space, are placed by the C compiler into a separate section of the kernel code segment called .fixup.

The search_exception_tables( ) function is used to search for a specified address in all exception tables: if the address is included in a table, the function returns a pointer to the corresponding exception_table_entry structure; otherwise, it returns NULL. Thus the Page Fault handler do_page_fault( ) executes the following statements:

if ((fixup = search_exception_tables(regs->eip))) {
regs->eip = fixup->fixup;
return 1;
}

The regs->eip field contains the value of the eip register saved on the Kernel Mode stack when the exception occurred. If the value in the register (the instruction pointer) is in an exception table, do_page_fault( ) replaces the saved value with the address found in the entry returned by search_exception_tables( ). Then the Page Fault handler terminates and the interrupted program resumes with execution of the fixup code .

10.4.5. Generating the Exception Tables and the Fixup Code

The GNU Assembler .section directive allows programmers to specify which section of the executable file contains the code that follows. As we will see in Chapter 20, an executable file includes a code segment, which in turn may be subdivided into sections. Thus, the following assembly language instructions add an entry into an exception table; the "a" attribute specifies that the section must be loaded into memory together with the rest of the kernel image:

.section _ _ex_table, "a"
.long faulty_instruction_address, fixup_code_address
.previous

The .previous directive forces the assembler to insert the code that follows into the section that was active when the last .section directive was encountered.

Let's consider again the _ _get_user_1( ), _ _get_user_2( ), and _ _get_user_4( ) functions mentioned before. The instructions that access the process address space are those labeled as 1, 2, and 3:

_ _get_user_1:
[...]
1: movzbl (%eax), %edx
[...]
_ _get_user_2:
[...]
2: movzwl -1(%eax), %edx
[...]
_ _get_user_4:
[...]
3: movl -3(%eax), %edx
[...]
bad_get_user:
xorl %edx, %edx
movl $-EFAULT, %eax
ret
.section _ _ex_table,"a"
.long 1b, bad_get_user
.long 2b, bad_get_user
.long 3b, bad_get_user
.previous

Each exception table entry consists of two labels. The first one is a numeric label with a b suffix to indicate that the label is "backward;" in other words, it appears in a previous line of the program. The fixup code is common to the three functions and is labeled as bad_get_user. If a Page Fault exception is generated by the instructions at label 1, 2, or 3, the fixup code is executed. It simply returns an -EFAULT error code to the process that issued the system call.

Other kernel functions that act in the User Mode address space use the fixup code technique. Consider, for instance, the strlen_user(string) macro. This macro returns either the length of a null-terminated string passed as a parameter in a system call or the value 0 on error. The macro essentially yields the following assembly language instructions:

movl $0, %eax
movl $0x7fffffff, %ecx
movl %ecx, %ebx
movl string, %edi
0: repne; scasb

subl %ecx, %ebx
movl %ebx, %eax
1:
.section .fixup,"ax"
2: xorl %eax, %eax
jmp 1b
.previous
.section _ _ex_table,"a"
.long 0b, 2b
.previous

The ecx and ebx registers are initialized with the 0x7fffffff value, which represents the maximum allowed length for the string in the User Mode address space. The repne;scasb assembly language instructions iteratively scan the string pointed to by the edi register, looking for the value 0 (the end of string /0 character) in eax. Because scasb decreases the ecx register at each iteration, the eax register ultimately stores the total number of bytes scanned in the string (that is, the length of the string).

The fixup code of the macro is inserted into the .fixup section. The "ax" attributes specify that the section must be loaded into memory and that it contains executable code. If a Page Fault exception is generated by the instructions at label 0, the fixup code is executed; it simply loads the value 0 in eaxthus forcing the macro to return a 0 error code instead of the string lengthand then jumps to the 1 label, which corresponds to the instruction following the macro.

The second .section directive adds an entry containing the address of the repne; scasb instruction and the address of the corresponding fixup code in the _ _ex_table section.

 

 

=====================================================================

 

 

4.5 Exception Handling

A very important part of VM is how kernel address space exceptions, which are not bugs, are caught.[1] This section does not cover the exceptions that are raised with errors such as divide by zero. I am only concerned with the exception raised as the result of a page fault. There are two situations where a bad reference may occur. The first is where a process sends an invalid pointer to the kernel by a system call, which the kernel must be able to safely trap because the only check made initially is that the address is below PAGE_OFFSET. The second is where the kernel uses copy_from_user() or copy_to_user() to read or write data from userspace.

Many thanks go to Ingo Oeser for clearing up the details of how this is implemented.

At compile time, the linker creates an exception table in the __ex_table section of the kernel code segment, which starts at __start___ex_table and ends at __stop___ex_table. Each entry is of type exception_table_entry, which is a pair consisting of an execution point and a fixup routine. When an exception occurs that the page fault handler cannot manage, it calls search_exception_table() to see if a fixup routine has been provided for an error at the faulting instruction. If module support is compiled, each module's exception table will also be searched.

If the address of the current exception is found in the table, the corresponding location of the fixup code is returned and executed. We will see in Section 4.7 how this is used to trap bad reads and writes to userspace.




4.7 Copying to/from Userspace

It is not safe to access memory in the process address space directly because there is no way to quickly check if the page addressed is resident or not. Linux relies on the MMU to raise exceptions when the address is invalid and have the Page Fault Exception handler catch the exception and fix it up. In the x86 case, an assembler is provided by the __copy_user() to trap exceptions where the address is totally useless. The location of the fixup code is found when the function search_exception_table() is called. Linux provides an ample API (mainly macros) for copying data to and from the user address space safely as shown in Table 4.6.


Table 4.6. Accessing Process Address Space API

unsigned long copy_from_user(void *to, const void *from, unsigned long n)
Copies n bytes from the user address(from) to the kernel address space(to).

unsigned long copy_to_user(void *to, const void *from, unsigned long n)
Copies n bytes from the kernel address(from) to the user address space(to).

void copy_user_page(void *to, void *from, unsigned long address)
Copies data to an anonymous or COW page in userspace. Ports are responsible for avoiding D-cache aliases. It can do this by using a kernel virtual address that would use the same cache lines as the virtual address.

void clear_user_page(void *page, unsigned long address)
Similar to copy_user_page(), except it is for zeroing a page.

void get_user(void *to, void *from)
Copies an integer value from userspace (from) to kernel space (to).

void put_user (void *from, void *to)
Copies an integer value from kernel space (from) to userspace (to).

long strncpy_from_user(char *dst, const char *src, long count)
Copies a null terminated string of at most count bytes long from userspace (src) to kernel space (dst).

long strlen_user(const char *s, long n)
Returns the length, upper bound by n, of the userspace string including the terminating NULL.

int access_ok(int type, unsigned long addr, unsigned long size)
Returns nonzero if the userspace block of memory is valid and zero otherwise.




All the macros map on to assembler functions, which all follow similar patterns of implementation. For illustration purposes, we'll just trace how copy_from_user() is implemented on the x86.

If the size of the copy is known at compile time, copy_from_user() calls __constant_copy_from_user(), or __generic_copy_from_user() is used. If the size is known, there are different assembler optimizations to copy data in 1, 2 or 4 byte strides. Otherwise, the distinction between the two copy functions is not important.

The generic copy function eventually calls the function __copy_user_zeroing() in <asm-i386/uaccess.h>, which has three important parts. The first part is the assembler for the actual copying of size number of bytes from userspace. If any page is not resident, a page fault will occur, and, if the address is valid, it will get swapped in as normal. The second part is fixup code, and the third part is the __ex_table mapping the instructions from the first part to the fixup code in the second part.

These pairings, as described in Section 4.5, copy the location of the copy instructions and the location of the fixup code to the kernel exception handle table by the linker. If an invalid address is read, the function do_page_fault() will fall through, call search_exception_table(), find the Enhanced Instruction Pointer (EIP) where the faulty read took place and jump to the fixup code, which copies zeros into the remaining kernel space, fixes up registers and returns. In this manner, the kernel can safely access userspace with no expensive checks and let the MMU hardware handle the exceptions.

All the other functions that access userspace follow a similar pattern.

 

 

 

 

 

理解上面这些,再去看书的“页框管理”“内核空间的页描述符数组”“进程地址空间地址管理”“slab分配器”“kmalloc的连续页内存分配,vmalloc的非连续页内存”分配等都比较拗容易理解了。

深入理解操作系统中的并发与内存管理机制 本文将深入探讨操作系统中的并发机制和内存管理策略。从经典的死锁策略和哲学家问题的解决方案,到UNIX和LINUX内核的并发控制,再到SOLARIS的线程同步原语,以及WINDOWS和ANDROID的并发机制。同时,我们会探索内存管理的需求和分区,分页与分段技术,以及虚拟内存的实现和管理。此外,文章还将涉及调度算法,包括传统的UNIX调度以及多处理器、多核和实时调度策略。 阅读详情

相关推荐

Linux操作系统原理思维导图大全

进程是一个程序在其执行过程中的一个实例,是操作系统进行资源分配和调度的基本单位。每一个进程都有其独特的进程标识符(PID),资源集合和程序执行上下文。进程的组成包括:程序代码(代码段)程序计数器(PCB)处理器状态(寄存器集)内存分配(堆、栈)系统资源(如文件描述符和信号)进程之间可以共享代码段,但各自拥有独立的数据段和堆栈段。当一个程序被执行时,操作系统会为其分配一个进程控制块(PCB),用于存储进程信息和状态。return 0;上述代码通过。

weixin_30356433的博客 1099

Illegal string offset ' ' 解决方法

Illegal string offset ' ' 解决方法 今天在thinkphp5的框架里遇到了这个错误,具体的报错如下图 翻译后是“非法字符串偏移 ‘pay_price’”,意思是$v数组里面有空数组,空数组里没有pay_price这个字段。所以我们要用issert来验证,正确写法如下: foreach ($list as $k=&amp;gt;$v) $price[]=isset($v['pay...

凌云鹤YK 9万+

linux内核调试分析指南

linux内核调试分析指南 linux内核调试分析指南--上篇 本文档已经转到下面的网址,位于zh-kernel.org的文档停止更新,请访问新网址 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 建立调试环境 发行版的选择和安装 安装交叉编译工具 bin工具集的使用 qemu的使用 skyeye的使用 UML的使用 vmware的使用 initrd.img的原理与制作 x86虚拟调试环境的建立 arm虚拟调试环境的建立 arm开发板调试环境的建立 gdb基础 基本命令 gdb之gui gdb技巧 gdb宏 汇编基础--X86篇 用户手册 AT&T汇编格式 内联汇编 汇编与C函数的相互调用 调用链形成和参数传递 C难点的汇编解释 优化级别的影响 汇编基础--ARM篇 用户手册 调用链形成和参数传递 源码浏览工具 调用图生成工具 find + grep wine + SI global Source-Navigator vim + cscope/ctags kscope lxr SI等与gdb的特点 调用链、调用树和调用图 理想调用链 函数指针调用 调用链的层次 非理想调用链 调用树与调用图 穿越盲区 穿越gdb的盲区 穿越交叉索引工具的盲区 工程方法 bug 与 OOPS linux内核调试分析指南--下篇 ***第二部分:内核分析*** 内核组织层次和复杂度 内核层次 内核复杂度 复杂度隔离 gdb在内核分析中的用途 数据验证 界面剥离 参数记忆 路径快照 长程跟踪 整理思路 内核编码的艺术 信息聚集 数据聚集 关系聚集 操作聚集 松散聚集 顺序聚集 链表聚集 哈希聚集 树形聚集 分层聚集 分块聚集 对象聚集 设施客户 设备驱动模型分析 linux设备子系统的组成 设备驱动模型 usb子系统分析 如何阅读分析大型子系统 btrfs文件系统分析 区间树核心代码分析 B树核心代码分析 调试相关子系统 kgdb源码分析 sysrq oprofile kprobes 驱动分析 载入模块符号 ***第三部分:其他工具*** kexec strace ltrace SystemTap MEMWATCH YAMD Magic SysRq 附录:社区交流相关 补丁提交相关文档 补丁制作与提交示范 多补丁发送工具 git使用 Git公共库创建及使用 附录:内核参考书籍文章 内核git库 书籍 子系统官方网站 必看网站 参考文章 私人备忘

C++异常处理

C++异常处理(try catch throw)——袁志文 程序运行时常会碰到一些异常情况,例如: 做除法的时候除数为 0; 用户输入年龄时输入了一个负数; 用 new 运算符动态分配空间时,空间不够导致无法分配; 访问数组元素时,下标越界;打开文件读取时,文件不存在。 这些异常情况,如果不能发现并加以处理,很可能会导致程序崩溃。 一发现异常情况就立即处理未必妥当,因为在一个函数执行过程中发生的异常,在有的情况下由该函数的调用者决定如何处理更加合适。尤其像库函数这类提供给程序员调用,用以完成与具体应用无

qq_42589025的博客 2103

海思MIPI屏调试实战:从参数计算到点亮屏幕的完整指南

本文提供了一份海思平台MIPI屏幕调试的完整实战指南。文章以Hi3516DV300调试800x1280分辨率屏幕为例,详细讲解了从参数计算、配置文件修改到最终点亮屏幕的全流程,并分享了常见问题排查与进阶优化技巧,旨在帮助嵌入式开发者高效解决MIPI屏调试难题。

weixin_29190573的博客 736

电子工程师日记:用STM32F4的DCMI接口驱动OV2640时,那些数据同步的坑我都踩过了

本文分享了使用STM32F4的DCMI接口驱动OV2640摄像头时,在数据同步方面遇到的典型问题与解决方案。作者通过示波器分析PCLK相位、纠正DCMI_CR寄存器中同步信号极性的配置误区,并排查DMA传输瓶颈与FIFO溢出,最终实现了稳定的图像采集。文章为嵌入式工程师提供了宝贵的硬件调试经验与配置清单。

weixin_29189377的博客 156

/usr/bin/ld误操作,导致链接失效或者是丢失

直接在终端操作sudo apt-get install --reinstall binutils

weixin_42058099的博客 1352

形式化方法(Formal Methods)和人工智能(Artificial Intelligence)

一、形式化方法(Formal Methods) ​ 形式化方法最初应用在硬件上,验证硬件的正确性,后面逐渐应用到软件编程中,能够在软件中找到软件的错误,验证软件的安全可靠。形式化方法更加接近离散数学,所以其应用难度非常大,同时学习成本也较高。再加上形式化方法的大多数理论还停留在上个世纪,由于程序越来越复杂,导致形式化方法进行验证需要大量人工和时间,以至于现在绝大部分软件开发人员完全没有考虑过这个方法,甚至没有听说过这个名词。 ​ 形式化方法最新的应用成果是在工业上,比如一些极其重要且对安全性要求极高的领域(

qq_37030400的博客 4004

Illegal string offset 报错解决

Illegal string offset 'pareaId' 由于本地的数组赋值以字符串的形式初始值,在本地环境下并没有报错,但是在线上错先数据类型的报错, 请检测接受的变量是数组还是字符串,如果用数组的,请在赋值数组的时候以$where = [],然后以$where['pareaId'] = '值'; 解决:检测接受的变量值是否有误,因为在linux环境下严格要求 ...

weixin_40428902的博客 2768

PLL锁相环基础知识(一)

本文介绍了锁相环(PLL)的核心组件及其工作原理,重点阐述了压控振荡器(VCO)的结构与调谐机制。VCO作为PLL的关键部分,通过变容二极管实现频率调谐,但易受温度、电压等因素影响稳定性。文章详细讲解了VCO的谐振电路原理、硅基集成方案,以及通过分频器和调制器实现的频率控制技术。特别分析了整数型和分数型N分频器的差异,指出高阶德尔塔-西格玛调制器可有效降低分数杂散。文章还探讨了预分频器的应用及其对频率分辨率的影响,揭示了PLL通过反馈环路实现频率锁定的基本原理。

weixin_49761275的博客 3030

GD-Link离线烧录全攻略:从配置到一键下载的保姆级教程

本文详细介绍了GD-Link离线烧录的全流程,从硬件准备到软件配置,再到一键下载的实战技巧。通过保姆级教程,帮助工程师快速掌握国产MCU的离线烧录技术,适用于产线批量烧录和现场设备维护等场景,显著提升工作效率。

ww90123的博客 649

Linux资源限制

Linux 资源限制通过限制特定用户或程序的资源访问,也可以实现性能调优的目的。对于多用户的系统不限制资源本身可能就是一种不公平。1.通过 ulimit 是限制系统资源的一种途径,ulimit 支持 hard 和 soft 限制。Soft limits can be adjusted by the affected user.Hard limits are set by a system admi...

李俊的博客 6148

(6)APB总线协议 —— 逻辑梳理(0.1认知)

这篇大家就差不多看看就行

CAUC_learner的博客 2702

轻松学习Linux之认识内存管理机制

weixin_34293902的博客 78

【毕设实战】基于YOLOv8的HRSC2016船舶智能检测系统开发全指南(含可视化UI界面,打造海事领域高分毕设)

亲爱的同学,通过这篇教程,你已经掌握了“基于YOLOv8的HRSC2016微小船只检测系统”从数据集处理、模型训练到Tkinter界面开发的全流程。这个课题既贴合“海事智能化”的产业战略,又涵盖了“目标检测、深度学习、界面开发”等多个毕设评分点,非常适合作为计算机或海事工程专业的毕业设计。在实践中,你可能会遇到“海面背景复杂导致船只漏检”“微小船只识别精度不足”“UI与模型推理同步卡顿”等挑战——别担心,这些都是技术成长的必经之路。

know_F0727的博客 130

MIPI C-PHY介绍(Specification for C-PHY V1.2)

本文档描述了一个名为C-PHY的高速串行接口,它在带宽有限(bandwidth limited channels,看的有人说C的含义就是channel limit)的通道上提供高吞吐量性能,用于连接到外设,包括显示器和摄像头。首先0x4fff在0x4000~0x4fff区域,则f=(0,0,0,0,0,0,1),顺序是S6~S1;最后,端到端的传输过程是什么样呢,如下图所示,16bit先转换成7个符号(每个符号3位,共21位),然后并行数据转换成串行数据,输送到符号编码器和3个线的驱动器,接收端流程相反。

qflook的博客 5875

Linux内核内存管理系列学习路线规划

Linux内核内存管理系列学习路线规划

深耕嵌入式领用多年, 致力于分享嵌入式领域技术! 797

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Linux内核编程学习资料

Linux内核编程学习资料 去发现同类优质开源项目:https://gitcode.com/ 此仓库提供了一份关于Linux内核编程的详细资料,以PDF格式呈现。该资源适用于对Linux内核开发有兴趣的程序员和开发者,帮助读者深入了解Linux内核的架构和编程技巧。 资源描述 标题:Linux内核编程 格式:PDF 通过这份资料,您将能够: 学习Linux内核的基础知识 掌握内核编程的基本技...

gitblog_06760的博客 336

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Linux操作系统分析学习总结

本次学习主要围绕Linux操作系统内核的关键子系统进行。内核启动流程进程调度机制系统调用实现内存管理机制中断处理流程通过查阅源码、调试运行、分析设计思路,对操作系统“如何管理资源”和“如何调度执行”有了更清晰的认识。相比以往对系统的抽象理解,现在能够追踪实际执行路径并指出控制点。深入分析Linux内核源码,可以精确理解系统如何管理CPU、内存和IO设备。阅读《庖丁解牛Linux操作系统分析》一书时,我不仅学习了基本原理,还通过简洁代码重新构建了对调度、中断、内存的认识。

m0_64418752的博客 1475

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Linux内核编程学习资料:深入掌握内核开发的艺术

Linux内核编程学习资料:深入掌握内核开发的艺术 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在开源世界的广阔领域中,Linux内核编程学习资料无疑是一份宝贵的财富。这份资料以PDF格式呈现,专为对Linux内核开发有兴趣的程序员和开发者设计,旨在帮助读者系统地学习Linux内核的架构、原理及编程技巧。 项目技术分析 核心内容 Linux内核编程学习资料的核心内容...

gitblog_06717的博客 349

内存管理

《深入理解LINUX内存管理》学习笔记       引子 为什么要写这个笔记: 1,这本书的中文版翻译了太垃圾,没法阅读。阅读英文原版,可以很好的理解作者的思路。作此笔记备忘 2,一直以来学习LINUX kernel的知识缺乏系统化,借对这本书的学习,系统化的学习一下LINUX ke

yishuige的专栏 878

【学习地图】Linux学习类 · 文章索引

Linux 内核、驱动与系统编程的学习笔记。

runafterhit的博客 213

Linux操作系统分析学习心得

最后,资源复制子系统根据传入的 clone_flags 参数决定共享或复制资源,包括 copy_files(文件描述符表)、copy_fs(文件系统上下文)、copy_sighand/copy_signal(信号处理机制),其中 copy_mm 函数负责实现关键的写时复制机制来处理内存空间。1.进程的运行信息,如进程的当前状态(state),进程的各种时间片消耗记录(utime、stime等),进程的信号(signal)和优先级(priority)等。接下来以进程的创建为例,分享在课程中学到的内容。

JustFine23的博客 910

Linux学习总结

虽然工作中一直在写用户层代码,但工作中也凭兴趣看下kernel代码,提高自己,毕竟懂得越多,对工作和业务了解、架构设计就越有帮助,而且很多东西看到最后都是借助kernel实现,所以掌握kernel也是非常必要。从业以来也陆陆续续看过,解决一些疑问,但没有记录,也没想的特别明白,还有一些是没有认真想过的东西,现在有时间认真思考下,本文相当于十万个为什么,记录我对linux内核和驱动的一些学习。 内核概述 linux内核分为进程管理系统 、内存管理系统 、 i/o管理系统 和文件管理系统四.........

zhangxinjieli3的专栏 1259
上一篇: linux iostat
下一篇: ELF文件格式一例分析:
tinnycloud
博客等级 码龄21年 9粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值