首页
chatGPT
关于
友链
其它
统计
更多
壁纸
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,538 阅读
2
修改Linux Kernel defconfig的标准方法
6,453 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
6,057 阅读
4
使用git生成patch和应用patch
3,572 阅读
5
c语言的__attribute__
3,184 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
Rust
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
adtxl
累计撰写
380
篇文章
累计收到
16
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
页面
chatGPT
关于
友链
其它
统计
壁纸
留言
搜索到
126
篇与
的结果
2022-07-20
关于watermark的计算
关于watermark的概念请参考zoned page frame allocator--3(对watermark的理解)本文主要根据一个实例来加深理解1. zoneinfo的信息通过/proc/zoneinfo节点可以查看zone的相关信息,下面只截取了一部分,该开发平台只有1个node,也只有一直zone类型,即zone DMA32;Node 0, zone DMA32 ...... pages free 56388 min 1139 low 7295 high 7621 spanned 521952 present 345696 managed 326626 protection: (0, 0, 0) nr_free_pages 56388 ...... 这里的min、low、high的值是怎么计算出来的的呢?主要是依据min_free_kbytes的值,下面先分析下这个值是怎么计算出来的。2. min_free_kbytes值的计算根据文档Documentation/sysctl/vm.txt,============================================================== min_free_kbytes: This is used to force the Linux VM to keep a minimum number of kilobytes free. The VM uses this number to compute a watermark[WMARK_MIN] value for each lowmem zone in the system. Each lowmem zone gets a number of reserved free pages based proportionally on its size. Some minimal amount of memory is needed to satisfy PF_MEMALLOC allocations; if you set this to lower than 1024KB, your system will become subtly broken, and prone to deadlock under high loads. Setting this too high will OOM your machine instantly. =============================================================从上面的话得出了以下几点:min_free_kbyes代表的是系统保留空闲内存的最低限watermark[WMARK_MIN]的值是通过min_free_kbytes计算出来的系统中min_free_kbytes的值可以通过下面的节点查看。可以看到,这个4556/4=1139,就是上面min的值(因为我们这里只有一个zone DMA32)。console:/ # cat /proc/sys/vm/min_free_kbytes 4556下面的函数对每个zone做计算,将每个zone中超过high水位的值放到sum中。超过高水位的页数计算方法是:managed_pages减去watermark[WMARK_HIGH], 这样就可以获取到系统中各个zone超过高水位页的总和其中,high_wmark_pages(zone)获取的是watermark[WMARK_HIGH]的值,这里有个让我疑惑的地方是watermark[WMARK_HIGH]的值是根据watermark[WMARK_MIN]确定的,我们这里是为了计算watermark[WMARK_MIN],怎么会需要watermark[WMARK_HIGH]呢,在这里加个打印,发现在我的平台一开始获取的high值就是0,所以这里返回的就是managed_pages值,即326626/** * nr_free_zone_pages - count number of pages beyond high watermark * @offset: The zone index of the highest zone * * nr_free_zone_pages() counts the number of counts pages which are beyond the * high watermark within all zones at or below a given zone index. For each * zone, the number of pages is calculated as: * * nr_free_zone_pages = managed_pages - high_pages */ static unsigned long nr_free_zone_pages(int offset) { struct zoneref *z; struct zone *zone; /* Just pick one node, since fallback list is circular */ unsigned long sum = 0; struct zonelist *zonelist = node_zonelist(numa_node_id(), GFP_KERNEL); for_each_zone_zonelist(zone, z, zonelist, offset) { unsigned long size = zone->managed_pages; unsigned long high = high_wmark_pages(zone); if (size > high) sum += size - high; } return sum; } /** * nr_free_buffer_pages - count number of pages beyond high watermark * * nr_free_buffer_pages() counts the number of pages which are beyond the high * watermark within ZONE_DMA and ZONE_NORMAL. */ unsigned long nr_free_buffer_pages(void) { return nr_free_zone_pages(gfp_zone(GFP_USER)); } watermark的初始化是由下面的函数来完成的,将上面获取的值,即326626代入,可以算得在我们得平台上,min_free_kbytes得值为4572.与上面得4556大小基本相等。/* * 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; int new_min_free_kbytes; lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10); new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16); if (new_min_free_kbytes > user_min_free_kbytes) { min_free_kbytes = new_min_free_kbytes; if (min_free_kbytes < 128) min_free_kbytes = 128; if (min_free_kbytes > 65536) min_free_kbytes = 65536; } else { pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferred\n", new_min_free_kbytes, user_min_free_kbytes); } // 建立各个zone得水位值 setup_per_zone_wmarks(); refresh_zone_stat_thresholds(); setup_per_zone_lowmem_reserve(); #ifdef CONFIG_NUMA setup_min_unmapped_ratio(); setup_min_slab_ratio(); #endif khugepaged_min_free_kbytes_update(); return 0; }3. 建立各个zone得水位值具体每个zone得水位值是由下面得函数确认的,static void __setup_per_zone_wmarks(void) { // pages_min = 4556/4=1139 unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); // extra_free_kbytes机制应该是Android得patch,看了下5.18得kernel,还不包含这个patch,本平台这个值大小为24300 // page_low=24300/4=6075 unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; // 计算除了ZONE_HIGHMEM之外所有的managed_pages之和,即lowmem_pages=326626 /* Calculate total number of !ZONE_HIGHMEM pages */ for_each_zone(zone) { if (!is_highmem(zone)) lowmem_pages += zone->managed_pages; } for_each_zone(zone) { u64 min, low; spin_lock_irqsave(&zone->lock, flags); // min = 1139*326626=372027014 min = (u64)pages_min * zone->managed_pages; // 取商,min = 1139, do_div(min, lowmem_pages); low = (u64)pages_low * zone->managed_pages; // vm_total_pages的值就是nr_free_pagecache_pages()的返回值,打log发现这个值是340294 // 也不是managed_pages值 // 所以下面计算后low的值在我们的平台还是pages_low=6075*326626/340294=5830 do_div(low, vm_total_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 control asynch page reclaim, and so should * not be capped for highmem. */ unsigned long min_pages; min_pages = zone->managed_pages / 1024; min_pages = clamp(min_pages, SWAP_CLUSTER_MAX, 128UL); 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]=1139 zone->watermark[WMARK_MIN] = min; } /* * Set the kswapd watermarks distance according to the * scale factor in proportion to available memory, but * ensure a minimum size on small systems. */ min = max_t(284,326),即min=326. min = max_t(u64, min >> 2, mult_frac(zone->managed_pages, watermark_scale_factor, 10000)); // Android打的patch,low的值在这里起了作用 // zone->watermark[WMARK_LOW]=1139+5830+326=7286 zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + low + min; // watermark[WMARK_HIGH]即比zone->watermark[WMARK_LOW]多了个min的值 zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + low + min * 2; spin_unlock_irqrestore(&zone->lock, flags); } /* update totalreserve_pages */ calculate_totalreserve_pages(); } /** * 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) { static DEFINE_SPINLOCK(lock); spin_lock(&lock); __setup_per_zone_wmarks(); spin_unlock(&lock); } 本平台得extra_free_kbytes值为24300console:/ # cat /proc/sys/vm/extra_free_kbytes 24300 本平台watermark_scale_factor的值为10console:/ # cat /proc/sys/vm/watermark_scale_factor 10Android为什么要增加extra_free_kbytes的值呢,主要目的是为了拉开watermark[WMARK_LOW]和watermark[WMARK_MIN]的距离。下面是我从本地的ubuntu开发机截取的zoneinfo信息,可以看到,min和low的差值并不大,其中low是min的1.25倍,high是min的1.5倍。Node 0, zone DMA ...... pages free 3977 min 16 low 20 high 24 spanned 4095 present 3998 managed 3977 protection: (0, 3116, 15719, 15719, 15719) ...... Node 0, zone DMA32 pages free 417377 min 3346 low 4182 high 5018 spanned 1044480 present 825004 managed 808543 protection: (0, 0, 12602, 12602, 12602) ......根据上图可知我们分配页的第一次尝试是从LOW水位开始分配的,当所剩余的空闲页小于LOW水位的时候,就会唤醒kswapd内核线程进行内存回收如果回收内存效果很显著,当空闲页大于HIGH水位的时候,则会停止kswapd内核线程回收如果回收内存效果不明显,当空闲内存直接小于MIN水位的时候,则会进行直接的内存回收(Direct reclaim),这样空闲内存就会逐渐增大当回收效果依然不明显的时候,则会启动OOM杀死进程kswapd周期回收机制kswapd是linux中用于页面回收的内核线程。当空闲内存的值低于low时,内存面临着一定的压力,在这次分配结束后kswapd就会被唤醒,这时内核线程kswapd开始进行页面回收,当kswapd回收页面发现此时内存终于到达了high水位,那么系统认为内存已经不再紧张了,所以将会停止进一步的操作。要注意,在这种情况下,内存分配虽然会触发内存回收,但不存在被内存回收所阻塞的问题,两者的执行关系是异步的。这里所说的“空闲内存”其实是一个zone总的空闲内存减去其lowmem_reserve的值。对于kswapd来说,要回收多少内存才算完成任务呢?只要把空闲内存的大小恢复到high对应的watermark值就可以了,当然,这取决于当前空闲内存和high值之间的差距,差距越大,需要回收的内存也就越多。low可以被认为是一个警戒水位线,而high则是一个安全的水位线。内存紧缺直接回收机制如果内存达到或者低于min时,这时说明现在内存严重不足,会触发内核直接回收操作(direct reclaim),这是一种默认的操作,此时分配器将同步等待内存回收完成,再进行内存分配。还有一种特殊情况,如果内存分配的请求是带了PF_MEMALLOC标志位的,并且现在空闲内存的大小可以满足本次内存分配的需求,那么也将是先分配,再回收。使用PF_MEMALLOC(PF表示per-process flag)相当于是忽略了watermark,因此它对应的内存分配的标志是ALLOC_NO_WATERMARk。能够获取"min"值以下的内存,也意味着该process有动用几乎所有内存的权力,因此它也对应GFP的标志__GFP_MEMALLOC。if (gfp_mask & __GFP_MEMALLOC) return ALLOC_NO_WATERMARKS; if (alloc_flags & ALLOC_NO_WATERMARKS) set_page_pfmemalloc(page); 可在内存严重短缺的时候,拥有不等待回收而强行分配内存的权力的进程其实可以想到kswapd,因为kswapd本身就是负责回收内存的,它只需要占用一小部分内存支撑其正常运行,就可以去回收更多的内存。虽然kswapd是在low到min的这段区间被唤醒加入调度队列的,但当它真正执行的时候,空闲内存的值可能已经掉到min以下了。可见,min值存在得一个意义是保证像kswapd这样得特殊任务能够在需要得时候立刻获取所需得内存。比如下面ubunut的水位,其中min=16, low=20, high=24比如当前空闲内存是在LOW水位以及MIN以上,这时候后台会启动kswapd内核线程进行进程内存回收,假设这时候突然有个很大的进程需要很大的内存请求,这样一来kswapd回收速度赶不上分配速度,内存一下掉到了MIN水位,这样直接就触发了直接内存回收,直接回收很影响系统性能的。这样看来linux原生的代码涉及MIN-LOW之间的间隙太小,很容易导致进入直接回收的情况的。所以在android的版本上增加了一个变量extra_free_kbytes.
2022年07月20日
1,484 阅读
0 评论
0 点赞
2022-07-06
内核调试秘籍
转载整理自《奔跑吧 Linux内核》,张天飞著
2022年07月06日
1,288 阅读
0 评论
0 点赞
2022-06-24
Linux函数 warn_alloc():page allocation failure问题分析
转载整理自https://www.cnblogs.com/arnoldlu/p/10691034.html代码部分使用的kernel版本为4.19.176
2022年06月24日
1,174 阅读
0 评论
0 点赞
2022-06-07
genalloc — 通用内存分配器
版权声明:本文为CSDN博主「g_salamander」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/g_salamander/article/details/8562424
2022年06月07日
833 阅读
0 评论
0 点赞
2022-06-06
使用gdb调试kernel
暂无简介
2022年06月06日
559 阅读
0 评论
0 点赞
2022-04-28
Linux ftrace的使用(2)(TODO)
整理自《奔跑吧 Linux内核,张天飞著》1. 动态ftrace在配置内核时打开了CONFIG_DYNAMIC_FTRACE选项,就可以支持动态ftrace功能。set_ftrace_filter和set_ftrace_notrace这两个文件可以配对使用,其中,前者设置要跟踪的函数,后者指定不要跟踪的函数。在实际的调试过程中,我们通常会被ftrace提供的大量信息淹没,因此动态过滤的方法非常有用。avaliable_filter_functions文件可以列出当前系统支持的所有函数,例如现在我只想关注hrtimer_nanosleep()和hrtimer_interrupt()这两个函数。# cd /sys/kernel/debug/tracing # echo hrtimer_nanosleep hrtimer_interrupt > set_ftrace_filter # echo function > current_tracer # echo 1 > tracing_on # usleep 1 # echo 0 > tracing_on # cat trace抓取的数据如下:# tracer: function # # entries-in-buffer/entries-written: 57/57 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | rcu_preempt-10 [002] d.h2 11073.199551: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [002] d.h2 11073.215430: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [002] d.h3 11073.235407: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [002] d.h2 11074.191632: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [000] d.h2 11074.603390: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [001] d.h1 11074.719377: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [001] d.h1 11074.735375: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [001] d.h1 11074.791404: hrtimer_interrupt <-arch_timer_handler_virt rcu_preempt-10 [001] d.h3 11075.828143: hrtimer_interrupt <-arch_timer_handler_virt ......此外,过滤器还支持如下通配符。<match>*:匹配所有match开头的函数*<match>:匹配所有match结尾的函数*<match>*:匹配所有包含match的函数如果跟踪所有"hrtimer"开头的函数,可以"echo \`hrtimer\_*\` > set_ftrace_filter"。还有两个非常有用的操作符,">"表示会覆盖过滤器里的内容;">>"表示新加的函数会增加到过滤器中,但不会覆盖。# echo do_nanosleep > set_ftrace_filter // 往过滤器里写入do_nanosleep # cat set_ftrace_filter do_nanosleep # echo 'hrtimer_*' >> set_ftrace_filter // 追加写入 # cat set_ftrace_filter hrtimer_init_sleeper hrtimer_active hrtimer_reprogram hrtimer_force_reprogram hrtimer_update_softirq_timer hrtimer_run_softirq hrtimer_forward hrtimer_init hrtimer_wakeup hrtimer_start_range_ns hrtimer_try_to_cancel hrtimer_cancel hrtimer_get_next_event hrtimer_next_event_without hrtimer_interrupt hrtimer_run_queues hrtimer_nanosleep do_nanosleep hrtimer_nanosleep_restart # echo '*preempt*' '*lock*' > set_ftrace_notrace // 表示不跟踪包含preempt和lock的函数 # echo > set_ftrace_filter // 向过滤器中输入空字符表示清空过滤器 cat set_ftrace_filter #### all functions enabled ####2. 事件跟踪ftrace里的跟踪机制主要有两种,分别是函数和tracepoint。前者属于"傻瓜式"操作,后者tracepoint可以理解为一个Linux内核中的的占位符函数,内核子系统的开发者通常喜欢利用它来调试。tracepoint可以输出开发者想要的参数、局部变量等信息。tracepoint的位置比较固定,一般都是内核开发者添加上去的,可以把它理解为传统C语言中#if DEBUG部分。如果在运行中没有开启DEBUG,那么是不占用任何系统开销的。在阅读内核代码时经常会遇到以trace_开头的函数,例如CFS调度器里的update_curr()函数。 */ static void update_curr(struct cfs_rq *cfs_rq) { ...... curr->vruntime += calc_delta_fair(delta_exec, curr); update_min_vruntime(cfs_rq); if (entity_is_task(curr)) { struct task_struct *curtask = task_of(curr); trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime); cgroup_account_cputime(curtask, delta_exec); account_group_exec_runtime(curtask, delta_exec); } ...... }update_curr()函数使用了一个sched_stat_runtime的tracepoint,我们可以在avaliable_events文件中查到,把想要跟踪的事件添加到set_event文件中即可,该文件同样支持通配符。# cd /sys/kernel/debug/tracing # cat available_events | grep sched_stat_runtime sched:sched_stat_runtime # echo sched:sched_stat_runtime > set_event # echo 1 > tracing_on # cat trace # echo sched:* > set_event // 支持通配符,跟踪所有sched开头的事件 # echo *:* > set_event // 跟踪系统所有事件输出如下:# tracer: nop # # entries-in-buffer/entries-written: 456/51078 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | mali-cmar-backe-5672 [002] d..2 5880.987504: sched_stat_runtime: comm=mali-cmar-backe pid=5672 runtime=117420 [ns] vruntime=216701544114 [ns] Colors-5658 [002] d..2 5880.987548: sched_stat_runtime: comm=Colors pid=5658 runtime=50800 [ns] vruntime=216706582321 [ns] mali-cmar-backe-5672 [002] d..2 5880.987621: sched_stat_runtime: comm=mali-cmar-backe pid=5672 runtime=73240 [ns] vruntime=216701590175 [ns] ......另外事件跟踪还支持另外一个强大的功能,可以设定跟踪条件,做到更精细化的设置。每个tracepoint都定义一个format格式,其中定义了该tracepoint支持的域。# cd /sys/kernel/debug/tracing/events/sched/sched_stat_runtime # cat format输出如下:name: sched_stat_runtime ID: 246 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:char comm[16]; offset:8; size:16; signed:0; field:pid_t pid; offset:24; size:4; signed:1; field:u64 runtime; offset:32; size:8; signed:0; field:u64 vruntime; offset:40; size:8; signed:0; print fmt: "comm=%s pid=%d runtime=%Lu [ns] vruntime=%Lu [ns]", REC->comm, REC->pid, (unsigned long long)REC->runtime, (unsigned long long)REC->vruntime例如,sched_stat_runtime这个tracepoint支持8个域,前4个是通用域,后4个是该tracepoint支持的域,comm是一个字符串域,其他都是数字域。支持类似C语言表达式对事件进行过滤,对于数字域支持==, !=, <, <=, >, >=, &操作符,对于字符串域支持==, !=, ~操作符。例如只想跟踪进程名字开头为sh的所有进程的sched_stat_runtime事件。# cd /sys/kernel/debug/tracing/events/sched/sched_stat_runtime # echo 'comm ~ "sh*"' > filter // 跟踪所有进程名字开头为sh的 echo 'pid==5658' > filter // 跟踪进程PID为5658的输出如下:# # entries-in-buffer/entries-written: 474/51988245 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | Colors-5658 [003] d..2 18238.020119: sched_stat_runtime: comm=Colors pid=5658 runtime=1010360 [ns] vruntime=1531587993187 [ns] Colors-5658 [003] dn.2 18238.023648: sched_stat_runtime: comm=Colors pid=5658 runtime=425020 [ns] vruntime=1531588418207 [ns] Colors-5658 [003] dn.2 18238.023837: sched_stat_runtime: comm=Colors pid=5658 runtime=52780 [ns] vruntime=1531588470987 [ns] Colors-5658 [003] dn.2 18238.024666: sched_stat_runtime: comm=Colors pid=5658 runtime=637600 [ns] vruntime=1531589108587 [ns] 3. 添加tracepoint内核各个子系统目前已经有大量的tracepoint,如果觉得这些tracepoint还不满足需求,可以自己手动添加一个,这在实际工作中也是很常用的技巧。还是以CFS
2022年04月28日
1,183 阅读
0 评论
0 点赞
2022-04-28
Linux ftrace的使用(1)
整理自《奔跑吧 Linux内核,张天飞著》
2022年04月28日
1,299 阅读
0 评论
0 点赞
2022-04-20
linux 内核中的ioremap函数
版权声明:本文为CSDN博主「小灏灏同学」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/student456852/article/details/116868447
2022年04月20日
2,044 阅读
0 评论
0 点赞
2022-04-18
分配内存常用api
暂无简介
2022年04月18日
601 阅读
0 评论
0 点赞
2022-04-18
内存管理中常用api
整理自《奔跑吧Linux内核,张天飞著》
2022年04月18日
817 阅读
0 评论
0 点赞
1
...
6
7
8
...
13