【深入理解Linux内核架构】3.2 (N)UMA模型中的内存组织
内核对一致和非一致内存访问系统使用相同的数据结构。在UMA系统上,只使用一个NUMA结点来管理整个系统内存。而内存管理的其他部分则相信他们是在处理一个伪NUMA系统。
3.2.1 概述
内存划分为结点。每个结点关联系统中的一个处理器,在内核中,使用pg_data_t的实例。
各个结点又划分为内存域,是内存的进一步细分。分为:normal、DMA、highmem 最多3个内存域。
/* ./include/linux/memzon.h */
enum zone_type {
#ifdef CONFIG_ZONE_DMA
/*
* ZONE_DMA is used when there are devices that are not able
* to do DMA to all of addressable memory (ZONE_NORMAL). Then we
* carve out the portion of memory that is needed for these devices.
* The range is arch specific.
*
* Some examples
*
* Architecture Limit
* ---------------------------
* parisc, ia64, sparc <4G
* s390 <2G
* arm Various
* alpha Unlimited or 0-16MB.
*
* i386, x86_64 and multiple other arches
* <16M.
*/
ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
/*
* x86_64 needs two ZONE_DMAs because it supports devices that are
* only able to do DMA to the lower 16M but also 32 bit devices that
* can only do DMA areas below 4G.
*/
ZONE_DMA32,
#endif
/*
* Normal addressable memory is in ZONE_NORMAL. DMA operations can be
* performed on pages in ZONE_NORMAL if the DMA devices support
* transfers to all addressable memory.
*/
ZONE_NORMAL, /* 无法保证改地址范围对应实际的物理内存,例如:在AMD64位系统有2GB内存,那么所有内存都属于ZONE_DMA32范围,而ZONE_NORMAL则为空 */
#ifdef CONFIG_HIGHMEM
/*
* A memory area that is only addressable by the kernel through
* mapping portions into its own address space. This is for example
* used by i386 to allow the kernel to address the memory beyond
* 900MB. The kernel will set up special mappings (page
* table entries on i386) for each page that the kernel needs to
* access.
*/
ZONE_HIGHMEM,
#endif
ZONE_MOVABLE, // 伪内存区域,在防止物理内存碎片的机制中需要使用
__MAX_NR_ZONES // 结束标记,在迭代系统所有内存域时,会常用该常量
};
各个内存域都关联了一个数组,用来组织属于该内存域的物理页(内核中称之为页帧)。对于每个页帧,都分配一个struct page实例及所需的管理数据。
各个内存结点保存在一个单链表中,共内核遍历。
出于性能的考虑,在为进程分配内存时,内核总是试图在当前运行CPU相关联的NUMA结点上进行。但这并不总是可行的,例如,该结点的内存已经用尽。对此情况,每个结点都提供一个备用链表(借助于struct zonelist)。改列表包含了其他结点和相关的内存域,可用于代替当前结点分配内存。列表项的位置越靠后,就越不适合分配。
3.2.2 数据结构
1. 结点管理
pg_data_t用于表示结点的基本元素,定义如下:
/* ./include/linux/mmzone.h */ /*
* The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM
* (mostly NUMA machines?) to denote a higher-level memory zone than the
* zone denotes.
*
* On NUMA machines, each NUMA node would have a pg_data_t to describe
* it's memory layout.
*
* Memory statistics and page replacement data structures are maintained on a
* per-zone basis.
*/
struct bootmem_data;
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES]; // 属于该内存结点的各个内存域的数组
struct zonelist node_zonelists[MAX_ZONELISTS]; // 备用结点及其内存域的列表,以便在当前结点没有可用空间时,在备用结点分配内存
int nr_zones; // 该内存结点不同内存域的数目
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
struct page *node_mem_map; // 指向page实例的数组指针,用于描述结点的所有物理内存页
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
struct page_cgroup *node_page_cgroup;
#endif
#endif
#ifndef CONFIG_NO_BOOTMEM
struct bootmem_data *bdata; // 在系统启动期间,内存管理子系统初始化之前,系统所需内存分配,即自举内存分配器数据结构的实例
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/*
* Must be held any time you expect node_start_pfn, node_present_pages
* or node_spanned_pages stay constant. Holding this will also
* guarantee that any pfn_valid() stays that way.
*
* Nests above zone->lock and zone->size_seqlock.
*/
spinlock_t node_size_lock;
#endif
unsigned long node_start_pfn; // 第一个页帧的逻辑编号,系统中所有结点的页帧是依次编号的,每个页帧的号码是全局唯一的。在UMA系统中总是0,因为只有一个结点。
unsigned long node_present_pages; /* total number of physical pages */
unsigned long node_spanned_pages; /* total size of physical page range, including holes */
int node_id; // 全局结点ID,系统中的NUMA结点都是从0开始编号
wait_queue_head_t kswapd_wait; // 交换守护进程(swap daemon)的等待队列,在将页帧换出结点时会用到。
struct task_struct *kswapd; // 负责该结点的交换守护进程的task_struct
int kswapd_max_order; // 用于交换子系统的实现,用来定义需要释放的区域的长度
enum zone_type classzone_idx;
} pg_data_t;
- 结点的状态管理
如果系统中结点多于一个,内核会维护一个位图,泳衣提供各个结点的状态信息。状态是用位掩码指定的,可使用下列值:
/* include/nodemask.h */
/*
* Bitmasks that are kept for all the nodes.
*/
enum node_states {
N_POSSIBLE, /* The node could become online at some point */
N_ONLINE, /* The node is online */
N_NORMAL_MEMORY, /* The node has regular memory */
#ifdef CONFIG_HIGHMEM
N_HIGH_MEMORY, /* The node has regular or high memory */
#else
N_HIGH_MEMORY = N_NORMAL_MEMORY,
#endif
N_CPU, /* The node has one or more cpus */
NR_NODE_STATES
};
状态N_POSSIBLE、N_ONLINE和N_NORMAL_MEMORY用于CPU和内存的热拔插。如果该结点有普通或高端内存则使用N_HIGH_MEMORY和N_NORMAL_MEMORY,仅当结点没有高端内存才设置N_NORMAL_MEMORY。
/* 两个辅助函数用来设置或清除位域或特定结点中的一个bit */
void node_set_state(int node, enum node_states state);
void node_clear_state(int node, enum node_states state); /*
* 宏 for_each_node_state(__node, __state):用来迭代处于特定状态的所有结点
* 宏 for_each_online_node(node):迭代所有的活动结点
*/
如果内核编译为只支持单个结点(UMA系统),没有结点位图,上述操作该位图的函数则变为空操作。
2. 内存域
struct zone {
/* Fields commonly accessed by the page allocator(由页分配器访问的字段) */ /* zone watermarks, access with *_wmark_pages(zone) macros */
unsigned long watermark[NR_WMARK]; /*
* When free pages are below this point, additional steps are taken
* when reading the number of free pages to avoid per-cpu counter
* drift allowing watermarks to be breached
*/
unsigned long percpu_drift_mark; /*
* We don't know if the memory that we're going to allocate will be freeable
* or/and it will be released eventually, so to avoid totally wasting several
* GB of ram we must reserve some of the lower zone memory (otherwise we risk
* to run OOM on the lower zones despite there's tons of freeable ram
* on the higher zones). This array is recalculated at runtime if the
* sysctl_lowmem_reserve_ratio sysctl changes.
*/
unsigned long lowmem_reserve[MAX_NR_ZONES]; #ifdef CONFIG_NUMA
int node;
/*
* zone reclaim becomes active if more unmapped pages exist.
*/
unsigned long min_unmapped_pages;
unsigned long min_slab_pages;
#endif
struct per_cpu_pageset __percpu *pageset;
/*
* free areas of different sizes
*/
spinlock_t lock;
int all_unreclaimable; /* All pages pinned */
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
seqlock_t span_seqlock;
#endif
struct free_area free_area[MAX_ORDER]; #ifndef CONFIG_SPARSEMEM
/*
* Flags for a pageblock_nr_pages block. See pageblock-flags.h.
* In SPARSEMEM, this map is stored in struct mem_section
*/
unsigned long *pageblock_flags;
#endif /* CONFIG_SPARSEMEM */ #ifdef CONFIG_COMPACTION
/*
* On compaction failure, 1<<compact_defer_shift compactions
* are skipped before trying again. The number attempted since
* last failure is tracked with compact_considered.
*/
unsigned int compact_considered;
unsigned int compact_defer_shift;
#endif ZONE_PADDING(_pad1_) /* Fields commonly accessed by the page reclaim scanner */
spinlock_t lru_lock;
struct zone_lru {
struct list_head list;
} lru[NR_LRU_LISTS]; struct zone_reclaim_stat reclaim_stat; unsigned long pages_scanned; /* since last reclaim */
unsigned long flags; /* zone flags, see below */ /* Zone statistics */
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; /*
* The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on
* this zone's LRU. Maintained by the pageout code.
*/
unsigned int inactive_ratio; ZONE_PADDING(_pad2_)
/* Rarely used or read-mostly fields */ /*
* wait_table -- the array holding the hash table
* wait_table_hash_nr_entries -- the size of the hash table array
* wait_table_bits -- wait_table_size == (1 << wait_table_bits)
*
* The purpose of all these is to keep track of the people
* waiting for a page to become available and make them
* runnable again when possible. The trouble is that this
* consumes a lot of space, especially when so few things
* wait on pages at a given time. So instead of using
* per-page waitqueues, we use a waitqueue hash table.
*
* The bucket discipline is to sleep on the same queue when
* colliding and wake all in that wait queue when removing.
* When something wakes, it must check to be sure its page is
* truly available, a la thundering herd. The cost of a
* collision is great, but given the expected load of the
* table, they should be so rare as to be outweighed by the
* benefits from the saved space.
*
* __wait_on_page_locked() and unlock_page() in mm/filemap.c, are the
* primary users of these fields, and in mm/page_alloc.c
* free_area_init_core() performs the initialization of them.
*/
wait_queue_head_t * wait_table;
unsigned long wait_table_hash_nr_entries;
unsigned long wait_table_bits; /*
* Discontig memory support fields.
*/
struct pglist_data *zone_pgdat;
/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
unsigned long zone_start_pfn; /*
* zone_start_pfn, spanned_pages and present_pages are all
* protected by span_seqlock. It is a seqlock because it has
* to be read outside of zone->lock, and it is done in the main
* allocator path. But, it is written quite infrequently.
*
* The lock is declared along with zone->lock because it is
* frequently read in proximity to zone->lock. It's good to
* give them a chance of being in the same cacheline.
*/
unsigned long spanned_pages; /* total size, including holes */
unsigned long present_pages; /* amount of memory (excluding holes) */ /*
* rarely used fields:
*/
const char *name;
} ____cacheline_internodealigned_in_smp;
- 由ZONE_PADDING分割为几个部分。因为对zone结构的访问非常频繁,在SMP上,通常会有不同的CPU试图同时访问该结构成员。因此使用锁防止它们彼此干扰。在内核访问时,会获取两个自旋锁zone->lock和zone->lru_lock。如果数据保存在CPU高速缓存中,那么会处理的更加快速。高速缓存分为行,每一行负责不同的内存区(cache line)。内核使用ZONE_PADDING宏生成“填充”字段添加到结构中,以确保每个自旋锁都出于自身的缓存行中。而且使用了编译器关键字__cacheline_maxaligned_in_smp,泳衣实现最优的告诉缓存对其方式。
- unsigned long watermark[NR_WMARK];,其中NR_WATER为WMARK_MIN,WMARK_LOW,WMARK_HIGH。这是页换出时使用的水印。如果内存不足时,内核可以将页写到硬盘。这3个成员会影响守护进程的行为。
- 如果空闲页多于WATER_HIGH,内存域处于理想状态。
- 如果空闲页低于WATER_LOW,内核开始将页换出硬盘。
- 如果空闲页低于WATER_MIN,那么页回收工作压力就比较大,因为内存域中急需空闲页
- lowmem_reserve数组分别指定了各个内存域无论如何都不能失败的关键性内存分配。各个内存域的份额根据重要性决定。
- pageset是一个数组,用于实现每个CPU的冷/热页帧列表。内核使用这些列表来保存可用于满足实现的“新鲜页”。但冷热页帧的高速缓存状态不同:有些页帧也很可能仍然在高速缓存中,因此可以快速访问,故称之为热的;未缓存的页帧与此相对,故为冷的。
- free_area用于实现buddy系统。每个元素表示某种固定长度的一些连续内存区。对于包含在每个内存区中的空闲页的管理,free_area是一个起点。
- pages_scanned指定了上次换出一页以来,有多少页未能成功扫描。
- flags内存域当前状态。
typedef enum {
ZONE_RECLAIM_LOCKED, /* prevents concurrent reclaim 【防止并发回收】*/
ZONE_OOM_LOCKED, /* zone is in OOM killer zonelist */
ZONE_CONGESTED, /* zone has many dirty pages backed by a congested BDI*/
} zone_flags_t;
- vm_stat 维护了大量有关该内存域的统计信息。
- wait_table、wait_table_bits和wait_table_hash_nr_entries实现了一个等待队列,用于等待某一个页变为可用进程。进程排成一个队列,等待某些条件。在条件变为真时,内核会通知进程恢复工作。
- zone_start_pfn内存域第一个页帧的索引。
3. 内存域水印的计算
在计算各种水印之前,内核首先去顶需要为关键性分配保留的内存空间的最小值。该值随可用内存的大小而非线性增长,并保存在全局变量min_free_kbytes中。如下图:大图横轴采用对数坐标,小图横轴采用普通坐标,小图放大了总内存容量在0~4GB之间的变化曲线。总之:不能少于128KB,不能多于64MB【或许现在已经超了】。用户可以通过文件/proc/sys/vm/min_free_kbytes来读取和修改该设置。
/* ./mm/page_alloc.c */
/*
* Initialise min_free_kbytes.
*
* For small machines we want it small (128k min). For large machines
* we want it large (64MB max). But it is not linear, because network
* bandwidth does not increase linearly with machine size. We use
*
* min_free_kbytes = 4 * sqrt(lowmem_kbytes), for better accuracy:
* min_free_kbytes = sqrt(lowmem_kbytes * 16)
*
* which yields
*
* 16MB: 512k
* 32MB: 724k
* 64MB: 1024k
* 128MB: 1448k
* 256MB: 2048k
* 512MB: 2896k
* 1024MB: 4096k
* 2048MB: 5792k
* 4096MB: 8192k
* 8192MB: 11584k
* 16384MB: 16384k
*/
int __meminit init_per_zone_wmark_min(void)
{
unsigned long lowmem_kbytes; lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> ); min_free_kbytes = int_sqrt(lowmem_kbytes * );
if (min_free_kbytes < )
min_free_kbytes = ;
if (min_free_kbytes > )
min_free_kbytes = ;
setup_per_zone_wmarks();
refresh_zone_stat_thresholds();
setup_per_zone_lowmem_reserve();
setup_per_zone_inactive_ratio();
return ;
}
module_init(init_per_zone_wmark_min)
/**
* setup_per_zone_wmarks - called when min_free_kbytes changes
* or when memory is hot-{added|removed}
*
* Ensures that the watermark[min,low,high] values for each zone are set
* correctly with respect to min_free_kbytes.
*/
void setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - );
unsigned long lowmem_pages = ;
struct zone *zone;
unsigned long flags; /* Calculate total number of !ZONE_HIGHMEM pages */
for_each_zone(zone) {
if (!is_highmem(zone))
lowmem_pages += zone->present_pages;
} for_each_zone(zone) {
u64 tmp; spin_lock_irqsave(&zone->lock, flags);
tmp = (u64)pages_min * zone->present_pages;
do_div(tmp, lowmem_pages);
if (is_highmem(zone)) {
/*
* __GFP_HIGH and PF_MEMALLOC allocations usually don't
* need highmem pages, so cap pages_min to a small
* value here.
*
* The WMARK_HIGH-WMARK_LOW and (WMARK_LOW-WMARK_MIN)
* deltas controls asynch page reclaim, and so should
* not be capped for highmem.
*/
int min_pages; min_pages = zone->present_pages / ;
if (min_pages < SWAP_CLUSTER_MAX)
min_pages = SWAP_CLUSTER_MAX;
if (min_pages > )
min_pages = ;
zone->watermark[WMARK_MIN] = min_pages;
} else {
/*
* If it's a lowmem zone, reserve a number of pages
* proportionate to the zone's size.
*/
zone->watermark[WMARK_MIN] = tmp;
} zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> );
zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> );
setup_zone_migrate_reserve(zone);
spin_unlock_irqrestore(&zone->lock, flags);
} /* update totalreserve_pages */
calculate_totalreserve_pages();
}
5. 冷热页
热页:该页已经加载到CPU高速缓存中。冷页反之。
struct per_cpu_pageset {
struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
s8 expire;
#endif
#ifdef CONFIG_SMP
s8 stat_threshold;
s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
};
5. 页帧
页帧代表系统内存的最小单位,对内存中每个页都会创建struct page一个实例。所以需要该结构尽量小,不然假如一个页4K,但是描述它的结构却占了2K,那岂不是很浪费?!内存管理的许多部分都是用页,用于各种不同的用途。那么问题来了,page为某个功能提供的信息,但是其他部分可能完全无用。所以C语言的union适合该问题。
例如:一个物理内存页能够通过多个地方的不同页表映射到虚拟地址空间,内核想要跟踪有多少个地方映射了该页。为此,struct page中有一个计数器用于计算映射的数目。如果一页用于slab分配器,那么可以确保只有内核会使用该页,而不会有其他地方使用,因此映射技术信息就是多余的。因此内核可以重新解释该字段,用来表示该页被细分为多少个小的内存对象使用。
struct page {
......
union {
atomic_t _mapcount; /* Count of ptes mapped in mms,
* to show when page is mapped
* & limit reverse map searches.
*/
struct { /* SLUB */
u16 inuse; /* slub分配器:对象的数目 */
u16 objects;
};
};
......
};
/* include/linux/mm_types.h */
/*
* Each physical page in the system has a struct page associated with
* it to keep track of whatever it is we are using the page for at the
* moment. Note that we have no way to track which tasks are using
* a page, though if it is a pagecache page, rmap structures can tell us
* who is mapping it.
*/
struct page {
unsigned long flags; /* Atomic flags, some possibly updated asynchronously */
atomic_t _count; /* Usage count, see below. */
union {
atomic_t _mapcount; /* Count of ptes mapped in mms,
* to show when page is mapped
* & limit reverse map searches.
*/
struct { /* SLUB */
u16 inuse;
u16 objects;
};
};
union {
struct {
unsigned long private; /* Mapping-private opaque data:
* usually used for buffer_heads
* if PagePrivate set; used for
* swp_entry_t if PageSwapCache;
* indicates order in the buddy
* system if PG_buddy is set.
*/
struct address_space *mapping; /* If low bit clear, points to
* inode address_space, or NULL.
* If page mapped as anonymous
* memory, low bit is set, and
* it points to anon_vma object:
* see PAGE_MAPPING_ANON below.
*/
};
#if USE_SPLIT_PTLOCKS
spinlock_t ptl;
#endif
struct kmem_cache *slab; /* SLUB: Pointer to slab */
struct page *first_page; /* Compound tail pages */
};
union {
pgoff_t index; /* Our offset within mapping. */
void *freelist; /* SLUB: freelist req. slab lock */
};
struct list_head lru; /* Pageout list, eg. active_list
* protected by zone->lru_lock !
*/
/*
* On machines where all RAM is mapped into kernel address space,
* we can simply calculate the virtual address. On machines with
* highmem some memory is mapped into kernel virtual memory
* dynamically, so we need a place to store that address.
* Note that this field could be 16 bits on x86 ... ;)
*
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
unsigned long debug_flags; /* Use atomic bitops on this */
#endif #ifdef CONFIG_KMEMCHECK
/*
* kmemcheck wants to track the status of each byte in a page; this
* is a pointer to such a status block. NULL if not tracked.
*/
void *shadow;
#endif
};
该结构与体系结构无关,不依赖于CPU的类型。
- flags:与体系结构无关的标志。用于描述页的属性。
- _count:引用计数。表示内核中引用该页的次数。在其值为0时,内核知道page实例当前不适用,因此可以删除。
- _mapcount:页表中有多少项指向该页。
- lru:一个链表头。用于在各种连表上维护该页,以便将页按不同的类别分类,最重要的类别是活动页和不活动页。
- 内核可以将毗邻的页合并为较大的复合页(compound page)。分组中的第一个页称为首页(head page),而所有其余各页叫做尾页(tail page)。所有尾页对应的page实例中,都将first_page设置为指向首页。
- mapping指定了页帧所在地址空间。index是页帧在映射内部的偏移量。地址空间是一个非常一般的概念,例如:可以用在向内存读取文件时,地址空间可以将文件的内容与装在数据的内存关联起来。通过一个小技巧,mapping不仅能够保存一个指针,还能包含一些额外的信息,用于判断页是否属于未关联到地址空间的某个匿名内存区。如果将mapping设置为1,则该指针并不指向address_space的实例,而是指向另一个数据结构(anon_vma),该结构对实现匿名页的逆向映射很重要。对该指针的双重使用是可能的,因为address_space实例总是对齐到sizeof(long).因此在Linux支持的所有计算机上,指向该实例的指针最低位总是0.
该指针如果指向address_space实例,则可以直接使用。如果使用了技巧将最低位设置为1,内核可使用以下操作恢复来恢复指针:
anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON);
- private指向”私有数据“。虚拟内存管理会忽略该数据。根据页的用途,可以用不同的方式使用该指针。大多数情况下它用于将页与数据缓冲区关联起来。
- virtual:用于高端内存区中的页。virtual用于存储该页的虚拟地址。
WANT_PAGE_VIRTUAL:只用几个体系结构定义该宏:摩托罗拉m68k、FRV和Extensa。
而其他体系结构都采用了一种不同的方案来寻址虚拟内存页。其核心是用来查找所有的高端内存页帧的散列表。
【深入理解Linux内核架构】3.2 (N)UMA模型中的内存组织的更多相关文章
- 【深入理解Linux内核架构】第3章:内存管理
3.1 概述 内存管理涵盖了许多领域: 内存中物理内存页的管理: 分配大块内存的伙伴系统: 分配小块内存的slab.slub.slob分配器: 分配非连续内存块的vmalloc机制: 进程的地址空间. ...
- 【深入理解Linux内核架构】6.6 资源分配
一段摘自<Linux设备驱动程序>的话: 每种外设都通过读写寄存器进行控制.大部分外设都有多个寄存器,不管是内存地址空间还是I/O地址空间,这些寄存器的访问地址都是连续的. 在硬件层,内存 ...
- 【深入理解Linux内核架构】3.3 页表
页表:用于建立用户进程空间的虚拟地址空间和系统物理内存(内存.页帧)之间的关联. 向每个进程提供一致的虚拟地址空间. 将虚拟内存页映射到物理内存,因而支持共享内存的实现. 可以在不增加物理内存的情况下 ...
- 《深入理解linux内核架构》第二章 进程管理和调度
2.1进程优先级 进程优先级 硬实时进程 软实时进程 抢占式多任务处理 2.2进程生命周期 用户太切换到核心态的办法 系统调用 中断 抢占调度模型优先级普通进程<系统调用<中断 普通进程可 ...
- Linux内核入门到放弃-内存管理-《深入Linux内核架构》笔记
概述 内存管理的实现涵盖了许多领域: 内存中的物理内存页管理 分配大块内存的伙伴系统 分配较小内存块的slab.slub和slob分配器 分配非连续内存块的vmalloc机制 进程的地址空间 在IA- ...
- 读书笔记之Linux系统编程与深入理解Linux内核
前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...
- 搭建《深入Linux内核架构》的Linux环境
作者 彭东林 pengdonglin137@163.com 软件 Host: Ubuntu14.04 64 Qemu 2.8.0 Linux 2.6.24 busybox 1.24.2 gcc 4.4 ...
- 【读书笔记::深入理解linux内核】内存寻址【转】
转自:http://www.cnblogs.com/likeyiyy/p/3837272.html 我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0 ...
- 【读书笔记::深入理解linux内核】内存寻址
我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系. 这句话瞬间让我惊呆了,根据我的CPU的知识,开 ...
随机推荐
- js对象的数据属性和访问器属性
js面向对象 ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征.ECMA-262定义这些特性是为了实现javascript引擎用的,因此 ...
- 分享一个Flink checkpoint失败的问题和解决办法
本文来自: PerfMa技术社区 PerfMa(笨马网络)官网 接触Flink一段时间了,遇到了一些问题,其中有一个checkpoint失败导致作业重启的问题,遇到了很多次,重启之后一般也能恢复正常, ...
- Android The layout "activity_main" in layout has no declaration in the base layout folder
报错: The layout "activity_main" in layout has no declaration in the base layout folder; thi ...
- 什么?Java9这些史诗级更新你都不知道?Java9特性一文打尽!
「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...
- 简单的股票信息查询系统 1 程序启动后,给用户提供查询接口,允许用户重复查股票行情信息(用到循环) 2 允许用户通过模糊查询股票名,比如输入“啤酒”, 就把所有股票名称中包含“啤酒”的信息打印出来 3 允许按股票价格、涨跌幅、换手率这几列来筛选信息, 比如输入“价格>50”则把价格大于50的股票都打印,输入“市盈率<50“,则把市盈率小于50的股票都打印,不用判断等于。
'''需求:1 程序启动后,给用户提供查询接口,允许用户重复查股票行情信息(用到循环)2 允许用户通过模糊查询股票名,比如输入“啤酒”, 就把所有股票名称中包含“啤酒”的信息打印出来3 允许按股票价格 ...
- 第7篇scrum冲刺(5.27)
一.站立会议 1.照片 2.工作安排 成员 昨天已完成的工作 今天的工作安排 困难 陈芝敏 学习云开发,云函数调用以及数据的前后端传递 今天实现云词库搭建,随机获取并显示,对云开发有更深的认识 ...
- Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 2.通过 ConnectablePublisher 控制何时发布. 内容概览 前言 使用 ma ...
- 谷歌蜂鸟算法对网站seo优化有何影响
http://www.wocaoseo.com/thread-89-1-1.html 谷歌在过去三个月里,非常低调的推出了蜂鸟算法,据谷歌技术员表示,此种方法一出,将影响90%网站的排名, ...
- Javascript逻辑运算认识
1 - 运算符(操作符) 1.1 运算符的分类 运算符(operator)也被称为操作符,是用于实现赋值.比较和执行算数运算等功能的符号. JavaScript中常用的运算符有: 算数运算符 递增和递 ...
- 23种设计模式 - 状态变化(Memento备忘录 - State)
其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 状态变化 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的 ...