1 Linux内存管理的主要特点


无论物理内存多大,Linux都将其充分利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。而Windows是只在需要内存时,才为应用程序分配内存,并不能充分利用大容量的内存空间。Linux的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为cache、buffers,以此提高数据访问性能。页面高速缓存(page cache)是Linux内核实现的一种主要磁盘缓存。它主要用来减少对磁盘的I/O操作。具体地讲,是通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。


2 物理内存、虚拟内存


物理内存就是系统硬件提供的内存大小,是真正的内存,相对于物理内存,在Linux下还有一个虚拟内存的概念,虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间(Swap Space)。

Linux的内存管理采取的是分页存取机制,为了保证物理内存能得到充分的利用,内核会在适当的时候将物理内存中不经常使用的数据块自动交换到虚拟内存中,而将经常使用的信息保留到物理内存。


Linux内存运行机制:
2.1 Linux系统会不时的进行页面交换操作,以保持尽可能多的空闲物理内存

2.2 Linux进行页面交换是有条件的,不是所有页面在不用时都交换到虚拟内存,Linux内核根据”最近最经常使用“算法,仅仅将一些不经常使用的页面文件交换到虚拟内存

2.3 交换空间的页面在使用时会首先被交换到物理内存,如果此时没有足够的物理内存来容纳这些页面,它们又会被马上交换出去


3 Linux内存监控


free -m:

[root@rango backup_centos]# free -m

            total       used      free     shared    buffers     cached

Mem:          3805       3683       121          0        120        690

-/+ buffers/cache:       2872       932

Swap:         2041        134      1907

注解:

total:    物理内存的总大小used:已经使用的物理内存大小free:空闲的物理内存大小

shared:    多个进程共享的内存大小buffers/cached:磁盘缓存的大小

Mem:    代表物理内存使用情况(-/+buffers/cached):代表磁盘缓存使用状态

Swap:    表示交换空间内存使用状态

(-buffers/cache) used内存数:2872M(指的第一部分Mem行中的used- buffers - cached)
(+buffers/cache) free内存数:  932M (指的第一部分Mem行中的free+ buffers + cached)

从内核的角度可使用的内存:121M

从应用程序可使用的内存:free+buffers/cache=121+120+690=931M,为第二行的free值。对于应用程序来说,buffers/cached占有的内存是可用的,因为buffers/cached是为了提高文件读取的性能,当应用程序需要用到内存的时候,buffers/cached会很快地被回收,以供应用程序使用。


Linux缓存机制:buffers与cached都是内存操作,用来保存系统曾经打开过的文件以及文件属性信息,这样当操作系统需要读取某些文件时,会首先在buffers与cached内存区查找,如果找到,直接读出传送给应用程序,如果没有找到需要数据,才从磁盘读取。

buffers是用来缓冲块设备做的,它只记录文件系统的元数据(metadata)以及tracking in-flightpages,而cached是用来给文件做缓冲。更通俗一点说:buffers主要用来存放目录里面有什么内容,文件的属性以及权限等等。而cached直接用来记忆我们打开过的文件和程序。


4  Linux内存释放过程


4.1 free -m查看内存使用情况


4.2 sync:使用sync命令以确保文件系统的完整性,sync命令运行sync 子例程,将所有未写的系统缓冲区写到磁盘中,包含已修改的i-node、已延迟的块I/O和读写映射文件。为确保可靠起见,应执行两遍sync命令,这是因为sync命令完成时,并不保证信息实际写到了磁盘上。


4.3 修改/proc/sys/vm/drop_caches:

echo 3 > /proc/sys/vm/drop_caches

说明:

1)/proc是一个虚拟文件系统,我们可以通过对它的读写操作作为与kernel实体间进行通信的一种手段。也就是说可以通过修改/proc中的文件,来对当前kernel的行为做出调整。也就是说我们可以通过调整/proc/sys/vm/drop_caches来释放内存。

2)drop_caches:

Writing to this file causes the kernel to drop cleancaches,dentries and inodes from memory, causing that memory tobecomefree.

To free pagecache, use echo 1 > /proc/sys/vm/drop_caches;

to free dentries and inodes, use echo 2 >/proc/sys/vm/drop_caches;

to free pagecache, dentries and inodes, use echo 3>/proc/sys/vm/drop_caches.

Because this is a non-destructive operation and dirty objects arenot freeable, the user should run syncfirst.

5 Linux内存映射


当可执行文件准备运行时,可执行文件的内容仅仅映射到了对应进程虚拟地址空间中,而并没有调入物理内存。当程序开始运行并使用到这部分时,Linux才通过缺页中断把它们从磁盘上调入内存。这种将文件连接到进程虚拟地址空间的过程称为内存映射

一般情况下,用户空间是不可能也不应该直接访问设备的,但是,设备驱动程序中可实现mmap()函数,这个函数可使得用户空间能直接访问设备的物理地址。实际上,mmap()实现了这样的一个映射过程,它将用户空间的一段内存与设备内存关联,当用户访问用户空间的这段地址范围时,实际上会转化为对设备的访问。
当用户调用mmap()的时候,内核会进行如下的处理:

① 在进程的虚拟空间查找一块VMA。

② 将这块VMA进行映射。

③ 如果设备驱动程序或者文件系统的file_operations定义了mmap()操作,则调用它。

④ 将这个VMA插入进程的VMA链表中。

vma包含了用于访问设备的虚拟地址的信息,因此大量的工作由内核完成。为了执行mmap,驱动程序只需要为该地址返回建立合适的页表,并将vma->vm_ops替换为一系列的新操作就可以了。



6 Linux共享内存


6.1 共享内存


共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。


6.2 共享内存的释放


在使用共享内存的程序异常退出时,由于没有释放掉共享内存,在调试时会出现错误。

释放共享内存的方法:

1)如果总是通过Crtl+C来结束的话,可以做一个信号处理器,当接收到这个信号的时候,先释放共享内存,然后退出程序。

2)通过linux命令ipcrm shm shmid来释放,在使用该命令之前可以通过ipcs -m命令来查看共享内存。

查看共享内存:ipcs -m          -m只显示共享内存信息,-q只显示消息队列,-s只显示信号量

[root@rango backup_centos]# ipcs -m


------ Shared Memory Segments --------

key        shmid      owner      perms     bytes      nattch     status      

0x00000000 98304      rango      600       393216     2          dest        

0x00000000 131073     rango      600       393216     2          dest        

0x00000000 163842     rango      600       393216     2          dest        

0x00000000 196611     rango      600       393216     2          dest        

0x00000000 229380     rango      600       393216     2          dest        

0x00000000 262149     rango      600       393216     2          dest        

0x00000000 294918     rango      600       393216     2          dest        

0x00000000 327687     rango      600       393216     2          dest        

0x00000000 360456     rango      600       393216     2          dest

第一列就是共享内存的key;        第二列是共享内存的编号shmid;

第三列就是创建的用户owner;    第四列就是权限perms;

第五列为创建的大小bytes;        第六列为连接到共享内存的进程数nattach;

第七列是共享内存的状态status。其中显示“dest”表示共享内存段已经被删除,但是还有用户在使用它,当该段内存的mode字段设置为SHM_DEST时就会显示“dest”。当用户调用shmctl的IPC_RMID时,内存先查看多少个进程与这个内存关联着,如果关联数为0,就会销毁这段共享内存,否者设置这段内存的mod的mode位为SHM_DEST,如果所有进程都不用则删除这段共享内存。


释放共享内存:ipcrm   #释放单个共享内存

ipcs -m | awk ‘$2 ~/[0-9]+/ {print$2}’ | while read s; do sudo ipcrm –m $s; done  #释放所有共享内存

ps:释放共享内存的python脚本

rmsharemem.py

#!/usr/bin/env python

# Description: Remove the share memory

# Author: RangoChen

# Date: 2013.12.19

# -*- coding: utf-8 -*-

# Remove the share memory

import os

import sys

import getopt



def usage():

   print "usage: python rmsharemem.py -h -o -ssize "

   print " -h show help information"

   print " -o the owner create share memory needto delete"

   print " -s the share memory size"

   print " the shmid list need to delete"



def getsharemem():

   sharemap = {}

   fp = os.popen('ipcs -m')

   lines = fp.readlines()

   for l in lines:

       if not l.startswith('0x'):

           continue

   s = l.split()

   if sharemap.has_key(s[2]):

       sharemap[s[2]].append(s)

   else:

        sharemap[s[2]] = [s]

   #print 'Share memory map:\n', sharemap

return sharemap



if __name__ == "__main__":

   opts, args = getopt.getopt(sys.argv[1:], "o:hs:")

    # opts is the parameter with options

   # args is the parameter no ptions

    owner = None

    size = 0

   for o, p in opts:

       if o == '-h':

       usage()

       sys.exit(0)

   elif o == '-o':

       owner = p

   elif o == '-s':

       size = p



if not owner:

   val = raw_input("Are you sure to remove all sharememory?(yes/no)")

   if (val == "yes"):

       usage()

       sys.exit(0)



count = 0

total = 0

if len(args) > 0:

    for shmid in args:

        cmd = 'ipcrm -m %s' % shmid

       print 'execute command: %s' % cmd

        ret = os.system(cmd)

        total += 1

       if ret == 0:

            count += 1

            print 'remove %s shared memory success' % shmid

       else:

           print 'remove %s shared memory failed' % shmid

   else:

       shmmap = getsharemem()

       for o, l in shmmap.items():

           if owner and o == owner:

               continue

       for p in l:

            total += 1

            if size and size == p[4]:

                continue

           cmd = 'ipcrm -m %s' % p[1]

           print 'execute command: %s' % cmd

           ret = os.system(cmd)

           if ret == 0:

                count += 1

               print 'remove %s shared memory success' % p[1]

           else:

               print 'remove %s shared memory failed' % p[1]

print 'total share memory number = %s' % total

print 'remove success number = %s' % count

sys.exit(0)



6.3 共享内存大小的修改


查看共享内存的大小:cat    /proc/sys/kernel/shmmax

临时修改:echo 268435456 > /proc/sys/kernel/shmmax#把共享内存大小设置为256MB;

永久修改:vim /etc/rc.d/rc.local:  echo 268435456 > /proc/sys/kernel/shmmax

即可每次启动时把共享内存修改为256MB.


7 总结


此文介绍了Linux内存管理机制,包括内存映射、共享内存的实现机制等。

                                                                                           ——Rango Chen