免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3246 | 回复: 3
打印 上一主题 下一主题

[操作系统] 查一个内核问题而衍生出更多的关于内核的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-04-21 14:35 |只看该作者 |倒序浏览
本帖最后由 chenrvmldd 于 2011-04-21 14:36 编辑

前段时间查一个内核问题大概查了一个星期左右,也在论坛上发了很多帖子求救但是无果,异常痛苦!谨以此文记录查问题的全过程,同时将自己解决问题的思路记录下来,供大家参考!
废话少说,上问题:我linux的版本是2.4.25的,系统跑起来之后:
  1. / # free
  2.                     total         used         free       shared      buffers
  3. Mem:             13956        12316         1640            0            0
  4. Swap:            0            0            0
  5. Total:        13956        12316         1640
复制代码
通过free命令发现剩余的内存只有:1640k,明显内存不足。刚开始怀疑有可能是缓存内存占用太多:
再次通过:
  1. # cat /proc/meminfo
  2.            total:           used:    free:  shared: buffers:  cached:
  3. Mem:  14290944 12713984  1576960        0        0  1941504
  4. Swap:        0        0        0
  5. MemTotal:        13956 kB
  6. MemFree:          1540 kB
  7. MemShared:           0 kB
  8. Buffers:             0 kB
  9. Cached:           1896 kB
  10. SwapCached:          0 kB
  11. Active:            428 kB
  12. Inactive:         1716 kB
  13. HighTotal:           0 kB
  14. HighFree:            0 kB
  15. LowTotal:        13956 kB
  16. LowFree:          1540 kB
  17. SwapTotal:           0 kB
  18. SwapFree:            0 kB
复制代码
发现并不是由于缓存引起的cache和buffer的值加起来都很小,那么是由于什么问题引起的了?
后来又想到一个命令:/ # ps
  1.   PID  Uid     VmSize Stat Command
  2.     1 root        304 S   init      
  3.     2 root            SW  [keventd]
  4.     3 root            SWN [ksoftirqd_CPU0]
  5.     4 root            SW  [kswapd]
  6.     5 root            SW  [bdflush]
  7.     6 root            SW  [kupdated]
  8.     7 root            SW  [mtdblockd]
  9.     8 root            SWN [jffs2_gcd_mtd2]
  10.    16 root        160 S   telnetd
  11.    24 root            SWN [jffs2_gcd_mtd3]
  12.    31 root        236 S   vsftpd
  13.    32 root        456 S   /bin/ash
  14.    36 root        304 R   ps
  15. / #
复制代码
统计了一下发现其实应用程序只占用了少量的内存,没有统计出来的都是内核占用的,那么内核占用的为什么统计不出来了?
因为内核共用的是一个mm_struct,这个mm_struct的初始化函数如下:
  1. #define INIT_MM(name) \
  2. {                                                         \
  3.         mm_rb:                RB_ROOT,                        \
  4.         pgd:                swapper_pg_dir,                 \
  5.         mm_users:        ATOMIC_INIT(2),                 \
  6.         mm_count:        ATOMIC_INIT(1),                 \
  7.         mmap_sem:        __RWSEM_INITIALIZER(name.mmap_sem), \
  8.         page_table_lock: SPIN_LOCK_UNLOCKED,                 \
  9.         mmlist:                LIST_HEAD_INIT(name.mmlist),        \
  10. }
复制代码
在这个里面根本就没有发现对mm_struct结构体中的:struct vm_area_struct * mmap;                /* list of VMAs */这个成员进行初始化
从初始化函数中就可以验证了UTSK书上讲的:struct vm_area_struct * mmap这个结构是面向用户进程的
所以在ps命令中没有关于内核线程占用的虚拟内存的大小。
看来ps命令也没有效果,那么我开始思考:
第一:可以肯定的是内存肯定是被内核占用了!
第二:内核有可能是通过静态的方式占用太多的内存,也就是说定义了太多的静态缓冲区,凭我的直接这种情况应该可能性很小
第三:内核是在初始化的时候通过某种动态的方式不断的申请内存(注意:在我的系统中系统刚起来就占用那么多内存了,我并没有执行任何的应用程序),这个查起来就比较困难了

论坛徽章:
0
2 [报告]
发表于 2011-04-21 14:53 |只看该作者
既然得出这三个结论,那么就从第二个结论开始,因为第二个问题验证起来比较方便,看过内核初始化代码的兄弟肯定知道,在初始化的时候有一段如下的代码:
start_kernel->setup_arch函数下面的:
  1.         init_mm.start_code = (unsigned long) &_text;   //内核代码段的起始地址
  2.         init_mm.end_code = (unsigned long) &_etext;  //内核代码段的末端地址
  3.         init_mm.end_data = (unsigned long) &_edata;  //内核数据段的起始地址
  4.         init_mm.brk = (unsigned long) &_end                 //内核数据段的末端地址
  5. ;
复制代码
既然知道了这四个参数,那么,下一步想知道内核静态空间到底占用多大就容易了,有两种方法:
第一种:直接在原有的内核代码上修改内核代码
第二种:自己写一个可动态加载的内核模块

我选择了第二种,其实不管哪一种,都比较容易了,我的代码如下:
  1. #ifndef __KERNEL__
  2. #define __KERNEL__
  3. #endif
  4. #ifndef __MODULE__
  5. #define __MODULE__
  6. #endif




  7. #include <linux/config.h>
  8. #include <linux/module.h>
  9. #include <linux/sched.h>
  10. #include <linux/init.h>
  11. #include <linux/bootmem.h>
  12. #include <linux/mm.h>
  13. #include <linux/kernel.h>
  14. #include <asm/uaccess.h>
  15. #include <asm/pgtable.h>


  16. int kernel_size_init(void)
  17. {
  18.     unsigned long kernel_code_size=0;
  19.     unsigned long kernel_data_size=0;
  20.     printk("to test kernel size!\n");
  21.     printk("start_code is:[%lu]\n",init_mm.start_code);
  22.     printk("end_code is:[%lu]\n",init_mm.end_code);
  23.     kernel_code_size=init_mm.end_code-init_mm.start_code;   //算出内核代码段的大小
  24.     printk("kernel code size is:[%lu]\n",kernel_code_size);
  25.     printk("start data is:[%lu]\n",init_mm.end_data);
  26.     printk("end data is :[%lu]\n",init_mm.brk);
  27.     kernel_data_size=init_mm.brk-init_mm.end_data;                //算出内核数据段的大小
  28.     printk("kernel data size is:[%lu]\n",kernel_data_size);
  29.     return 0;   
  30. }


  31. void kernel_size_exit(void)
  32. {
  33.     printk("Bye");
  34.     return;
  35. }
  36. makefile如下:
  37. [code]#Make file for a basic kernel module

  38. KERNSRC =/opt/eldk/ppc_82xx/usr/src/linuxppc_2_4_devel
  39. CC = ppc_82xx-gcc
  40. MODCFLAGS := -DCONFIG_KERNELD -DMODULE -D__KERNEL__ -DLinux -O2 -Wall -I$(KERNSRC)/include -I$(KERNSRC)/arch/ppc

  41. kernel_size.o:        kernel_size.c $(KERNSRC)/include/linux/version.h
  42.         $(CC) $(MODCFLAGS) -c kernel_size.c
  43.        
复制代码
module_init(kernel_size_init);
module_exit(kernel_size_exit);
MODULE_LICENSE("GPL");


[/code]

编译成功之后将,kernel_size这个内核模块加载到内核中去经过测试发现:
(1212k kernel code, 508k data)
从这个参数可以看出内核静态占用的数据段和代码段才1M左右,所以可以肯定的,导致内存被大量占用,一定是内核初始化的时候,动态分配了
很多内存,那么这个问题怎么去查了?

论坛徽章:
0
3 [报告]
发表于 2011-04-21 15:41 |只看该作者
我记得深入理解Linux那本书上曾经介绍过内核的内存的分配比较特殊,主要是通过slab的方式来管理的,那么我想有没有可能是由于内核动态申请了大量的数据结构导致的了?
那么怎么知道内核数据结构占用内存的大小了,可以通过下面的一个命令来查看:
/ # cat /proc/slabinfo

关于slabinfo中参数的含义我就不讲了这个google很容易的
  1. slabinfo - version: 1.1
  2. kmem_cache            65     72    108    2    2    1
  3. tcp_tw_bucket          0      0     96    0    0    1
  4. tcp_bind_bucket        2    113     32    1    1    1
  5. tcp_open_request       0      0     64    0    0    1
  6. inet_peer_cache        0      0     64    0    0    1
  7. ip_fib_hash            5    113     32    1    1    1
  8. ip_dst_cache           9     24    160    1    1    1
  9. arp_cache              0      0     96    0    0    1
  10. blkdev_requests      512    520     96   13   13    1
  11. jffs2_inode_cache    458    580     24    4    4    1
  12. jffs2_node_frag      591   1890     28    5   15    1
  13. jffs2_raw_node_ref   2928   3030     16   15   15    1
  14. jffs2_tmp_dnode        0   1518     12    0    6    1
  15. jffs2_raw_inode        0     56     68    0    1    1
  16. jffs2_raw_dirent       0      0     40    0    0    1
  17. jffs2_full_dnode     623   2020     16    4   10    1
  18. nfs_write_data         0      0    384    0    0    1
  19. nfs_read_data          0      0    352    0    0    1
  20. nfs_page               0      0     96    0    0    1
  21. dnotify_cache          0      0     20    0    0    1
  22. file_lock_cache        0      0     96    0    0    1
  23. fasync_cache           0      0     16    0    0    1
  24. uid_cache              0      0     32    0    0    1
  25. skbuff_head_cache   2048   2064    160   86   86    1
  26. sock                   6     10    768    2    2    1
  27. sigqueue               0     29    132    0    1    1
  28. kiobuf                 0      0     64    0    0    1
  29. cdev_cache             4    113     32    1    1    1
  30. bdev_cache             2     59     64    1    1    1
  31. mnt_cache              9     59     64    1    1    1
  32. inode_cache           65     72    480    9    9    1
  33. dentry_cache          84     90    128    3    3    1
  34. filp                  13     30    128    1    1    1
  35. names_cache            0      2   4096    0    2    1
  36. buffer_head            0      0     96    0    0    1
  37. mm_struct              5     30    128    1    1    1
  38. vm_area_struct        21     40     96    1    1    1
  39. fs_cache               4    113     32    1    1    1
  40. files_cache            5      9    416    1    1    1
  41. signal_act             8      9   1312    3    3    1
  42. size-131072(DMA)       0      0 131072    0    0   32
  43. size-131072            0      1 131072    0    1   32
  44. size-65536(DMA)        0      0  65536    0    0   16
  45. size-65536             3      3  65536    3    3   16
  46. size-32768(DMA)        0      0  32768    0    0    8
  47. size-32768             0      0  32768    0    0    8
  48. size-16384(DMA)        0      0  16384    0    0    4
  49. size-16384             0      0  16384    0    0    4
  50. size-8192(DMA)         0      0   8192    0    0    2
  51. size-8192              2      2   8192    2    2    2
  52. size-4096(DMA)         0      0   4096    0    0    1
  53. size-4096           2055   2055   4096 2055 2055    1
  54. size-2048(DMA)         0      0   2048    0    0    1
  55. size-2048              7      8   2048    4    4    1
  56. size-1024(DMA)         0      0   1024    0    0    1
  57. size-1024             12     16   1024    3    4    1
  58. size-512(DMA)          0      0    512    0    0    1
  59. size-512               9     16    512    2    2    1
  60. size-256(DMA)          0      0    256    0    0    1
  61. size-256               5     15    256    1    1    1
  62. size-128(DMA)          0      0    128    0    0    1
  63. size-128             847    870    128   29   29    1
  64. size-64(DMA)           0      0     64    0    0    1
  65. size-64              159    177     64    3    3    1
  66. size-32(DMA)           0      0     32    0    0    1
  67. size-32             2532   2599     32   23   23    1
复制代码
size-4096这一项引起了我的注意:因为一项占用太多的内存了:总共2055个对象,每个对象占用4096个字节,也就是4K的大小,
大概算一下这一项就占用了8-9M左右的内存。这里建议大家看一下slab.c文件,这个文件对于我们理解slab有非常大的帮助

哈哈,这个时候哥们很激动啊,总算找到原因了,可是突然问题好像又来了,那么这一项到底是谁申请的?为什么会申请这么大了?这个问题貌似更猛!
继续思考:
我们知道对于slab来讲申请内存的话有好几个接口:常用的两个:kmalloc和kmem_cache_alloc,第二个接口一般都是面向具体数据结构的也就是
cat /proc/slabinfo前面列出的那些数据结构,那么size-4096这一项应该是通过kmalloc来分配的,接下来是不是可以通过kmalloc来看了,我大概搜索了一下
在2.4.25的内核中使用kmalloc调用的地方至少有3000个左右,这样的话,我们要一个一个的去看,工作量太大了,而且很不科学,在由于我的linux内核很多功能都是自己公司
添加上去的,那么我想会不会是由于添加的部分导致内核占用内存过多的原因了,后来,我搜索了一下公司增加的若干部分代码有没有kmalloc的调用,经过半天的努力终于
找到了如下的一段代码:
  1. for(i=0;i<sysctl_hot_list_len;i++)
  2.     {
  3.             skb_static_mem[i].data= kmalloc(4096,GFP_ATOMIC & ~__GFP_DMA);  
  4.             *(int*)(skb_static_mem[i].data+4092)=i;
  5.            
  6.             skb_static_mem[i].used=0;
  7.         if((sysctl_hot_list_len-1)!=i)
  8.                 skb_static_mem[i].next=i+1;
  9.     }
复制代码
其中sysctl_host_list_len的定义如下:
int sysctl_hot_list_len = 128*8*2;
从上面的这段代码不难看出:kmalloc被调用了128*8*2=2048 这个值和2055很接近罗。。。。而每次kmalloc都会分配4096个字节,即4K
到这里问题的最终原因总算找到了,好了下面要验证自己的想法了!修改内核参数:
将int sysctl_hot_list_len = 128*8*2;
改为:sysctl_hot_list_len= 128
重新编译内核,将内核文件下载到powepc板子上:

论坛徽章:
0
4 [报告]
发表于 2011-04-21 15:46 |只看该作者
系统起来后:
  1. / # free
  2.                     total         used         free       shared      buffers
  3.   Mem:        13964         4248         9716            0            0
  4. Swap:            0            0            0
  5. Total:        13964         4248         9716
复制代码
发现没有:free的值没有变大了,大概多了8M左右,再看看slabinfo的值:
  1. / # cat /proc/slabinfo
  2. slabinfo - version: 1.1
  3. kmem_cache            65     72    108    2    2    1
  4. tcp_tw_bucket          0      0     96    0    0    1
  5. tcp_bind_bucket        2    113     32    1    1    1
  6. tcp_open_request       0      0     64    0    0    1
  7. inet_peer_cache        0      0     64    0    0    1
  8. ip_fib_hash            5    113     32    1    1    1
  9. ip_dst_cache           4     24    160    1    1    1
  10. arp_cache              0      0     96    0    0    1
  11. blkdev_requests      512    520     96   13   13    1
  12. jffs2_inode_cache    458    580     24    4    4    1
  13. jffs2_node_frag      591   1890     28    5   15    1
  14. jffs2_raw_node_ref   2928   3030     16   15   15    1
  15. jffs2_tmp_dnode        0   1518     12    0    6    1
  16. jffs2_raw_inode        0     56     68    0    1    1
  17. jffs2_raw_dirent       0      0     40    0    0    1
  18. jffs2_full_dnode     622   2020     16    5   10    1
  19. nfs_write_data         0      0    384    0    0    1
  20. nfs_read_data          0      0    352    0    0    1
  21. nfs_page               0      0     96    0    0    1
  22. dnotify_cache          0      0     20    0    0    1
  23. file_lock_cache        0      0     96    0    0    1
  24. fasync_cache           0      0     16    0    0    1
  25. uid_cache              0      0     32    0    0    1
  26. skbuff_head_cache    128    144    160    6    6    1
  27. sock                   6     10    768    2    2    1
  28. sigqueue               0     29    132    0    1    1
  29. kiobuf                 0      0     64    0    0    1
  30. cdev_cache             4    113     32    1    1    1
  31. bdev_cache             2     59     64    1    1    1
  32. mnt_cache              9     59     64    1    1    1
  33. inode_cache           64     64    480    8    8    1
  34. dentry_cache          82     90    128    3    3    1
  35. filp                  12     30    128    1    1    1
  36. names_cache            0      2   4096    0    2    1
  37. buffer_head            0      0     96    0    0    1
  38. mm_struct              5     30    128    1    1    1
  39. vm_area_struct        21     40     96    1    1    1
  40. fs_cache               4    113     32    1    1    1
  41. files_cache            5      9    416    1    1    1
  42. signal_act             8      9   1312    3    3    1
  43. size-131072(DMA)       0      0 131072    0    0   32
  44. size-131072            0      1 131072    0    1   32
  45. size-65536(DMA)        0      0  65536    0    0   16
  46. size-65536             3      3  65536    3    3   16
  47. size-32768(DMA)        0      0  32768    0    0    8
  48. size-32768             0      0  32768    0    0    8
  49. size-16384(DMA)        0      0  16384    0    0    4
  50. size-16384             0      0  16384    0    0    4
  51. size-8192(DMA)         0      0   8192    0    0    2
  52. size-8192              2      2   8192    2    2    2
  53. size-4096(DMA)         0      0   4096    0    0    1
  54. size-4096            135    135   4096  135  135    1
  55. size-2048(DMA)         0      0   2048    0    0    1
  56. size-2048              7      8   2048    4    4    1
  57. size-1024(DMA)         0      0   1024    0    0    1
  58. size-1024             12     16   1024    3    4    1
  59. size-512(DMA)          0      0    512    0    0    1
  60. size-512               9     16    512    2    2    1
  61. size-256(DMA)          0      0    256    0    0    1
  62. size-256               5     15    256    1    1    1
  63. size-128(DMA)          0      0    128    0    0    1
  64. size-128             847    870    128   29   29    1
  65. size-64(DMA)           0      0     64    0    0    1
  66. size-64              159    177     64    3    3    1
  67. size-32(DMA)           0      0     32    0    0    1
  68. size-32              611    678     32    6    6    1
复制代码
注意到没有size-4096这一项的值变为135了。原来是2055,sysctl_hot_list_len = 128*8*2=2048 现在改为128,
2048-128=1920我们少分配了这么多次   2055-1920=135个,正好验证了我的想法
总结一下:
查这个问题总共查了一个多星期,过程总是痛苦的,看了很多资料也阅读了很多代码,以前还觉得网络上的资源很丰富,可是一旦遇到问题的时候发现其实有用的资料少的可怜
通过这个问题主要学到的知识:
第一:slab的具体含义,slab的管理模式
第二:系统对于内存的管理模式
第三:查找内核问题的思路
第四:内核模块的编写
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP