测试单进程单线程极限处理能力

测试单进程单线程极限处理能力

[toc]

测试过程

章节标题按照:环境、网络(本机、内网)、包大小(5字节、512字节、65k)结果

MBP本机5字节33万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:335070
cnt:331970
cnt:325655
cnt:336310

MBP本机512字节32万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:323410
cnt:325464
cnt:322750
cnt:325346

MBP本机60k字节9万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:100932
cnt:100261
cnt:103060
cnt:99918
cnt:101380
cnt:101387
cnt:96641
cnt:102714
cnt:101106
cnt:99290
cnt:102612
cnt:101937

腾讯云本机5字节21万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:211167
cnt:209314
cnt:208062
cnt:213580
cnt:212643
cnt:207598

腾讯云本机512字节20万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:200112
cnt:206787
cnt:205799
cnt:206545
cnt:201219
cnt:203444

腾讯云本机65k字节4万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:42880
cnt:43289
cnt:43468
cnt:43475
cnt:43383
cnt:43909

腾讯云机房内网5字节12万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:117529
cnt:122266
cnt:122153

腾讯云机房内网1400字节(MTU 1500)8万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:83065
cnt:83596
cnt:82676
cnt:82902

测试代码

server

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>

#define BUFLEN 65515
#define NPACK 10
#define PORT 9930

void diep(const char *s)
{
    perror(s);
    exit(1);
}

int main(void)
{
    struct sockaddr_in si_me, si_other;
    int s, i, slen=sizeof(si_other);
    char buf[BUFLEN];

    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");

    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(PORT);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (const sockaddr*)&si_me, sizeof(si_me))==-1)
        diep("bind");

    unsigned packet_cnt=0;
    struct timeval tv_begin={0};
    struct timeval tv_end={0};

    gettimeofday(&tv_begin, NULL);
    while(1)
    {
        if (recvfrom(s, buf, BUFLEN, 0, (sockaddr*)&si_other, (socklen_t*)&slen)==-1)
            diep("recvfrom()");
        packet_cnt++;
#if 0
        if(packet_cnt%50000==0)
        {
            struct timeval tv={0};
            gettimeofday(&tv, NULL);

            printf("cnt:%d, curtstamp:%d.%d\n",packet_cnt,tv.tv_sec,tv.tv_usec);
        }
#else
        gettimeofday(&tv_end, NULL);
        long ex=(tv_end.tv_sec-tv_begin.tv_sec)*1000000+(tv_end.tv_usec-tv_begin.tv_usec);
        if(ex>1000000)
        {
            printf("cnt:%d\n",packet_cnt);
            tv_begin=tv_end;
            packet_cnt=0;
        }
#endif
    }

    //for (i=0; i<NPACK; i++) {
    //    if (recvfrom(s, buf, BUFLEN, 0, (sockaddr*)&si_other, (socklen_t*)&slen)==-1)
    //        diep("recvfrom()");
    //    printf("Received packet from %s:%d\nData: %s\n\n", 
    //            inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf);
    //}

    close(s);
    return 0;
}

client

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFLEN 65000
#define NPACK 100*10000
#define PORT 9930
//#define SRV_IP "10.104.231.33"
#define SRV_IP "127.0.0.1"

void diep(const char *s)
{
    perror(s);
    exit(1);
}

int main(int argc,char* argv[])
{
    struct sockaddr_in si_other;
    int s, i, slen=sizeof(si_other);
    char buf[BUFLEN];

    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");

    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(PORT);
    if (inet_aton(SRV_IP, &si_other.sin_addr)==0) {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }

    int cnt=NPACK;
    if(argc!=1)
    {
        cnt=atoi(argv[1]);
    }

    for (i=0; i<cnt; i++) {
        if (sendto(s, buf, BUFLEN, 0, (const sockaddr*)&si_other, slen)==-1)
            diep("sendto()");
    }

    close(s);
    return 0;
}

makefile

all:
    g++ -o udpserver server.cpp
    g++ -o udpclient client.cpp

clean:
    rm -f *.o
    rm -f udpclient
    rm -f udpserver

back_insert_iterator源码分析

back_insert_iterator源码分析

gcc 4.1.2 版本的 back_insert_iterator


  // 24.4.2.2.1 back_insert_iterator
  /**
   *  @brief  Turns assignment into insertion.
   *
   *  These are output iterators, constructed from a container-of-T.
   *  Assigning a T to the iterator appends it to the container using
   *  push_back.
   *
   *  Tip:  Using the back_inserter function to create these iterators can
   *  save typing.
  */
  template<typename _Container>
    class back_insert_iterator
    : public iterator<output_iterator_tag, void, void, void, void>
    {
    protected:
      _Container* container;

    public:
      /// A nested typedef for the type of whatever container you used.
      typedef _Container          container_type;

      // 非常巧妙的使用 explicit 限制了初始化必须传入类型,这个以后再编写类似
      // 的专用类的时候,可以参考这样的实现
      // 构造函数传入必须是一个容器
      /// The only way to create this %iterator is with a container.
      explicit
      back_insert_iterator(_Container& __x) : container(&__x) { }

      /**
       *  @param  value  An instance of whatever type
       *                 container_type::const_reference is; presumably a
       *                 reference-to-const T for container<T>.
       *  @return  This %iterator, for chained operations.
       *
       *  This kind of %iterator doesn't really have a "position" in the
       *  container (you can think of the position as being permanently at
       *  the end, if you like).  Assigning a value to the %iterator will
       *  always append the value to the end of the container.
      */
      //将赋值运算符适配成 push_back, 返回值为 back_insert_iterator 迭代器适配器
      //传入容器必须要还有 push_back接口(常用容器都是有这个接口)
      back_insert_iterator&
      operator=(typename _Container::const_reference __value)
      {
    container->push_back(__value);
    return *this;
      }

      /// Simply returns *this.
      //空操作,保证迭代器接口的完备
      back_insert_iterator&
      operator*()
      { return *this; }

      /// Simply returns *this.  (This %iterator does not "move".)
      //空操作,保证迭代器接口的完备
      back_insert_iterator&
      operator++()
      { return *this; }

      /// Simply returns *this.  (This %iterator does not "move".)
      //空操作,保证迭代器接口的完备
      back_insert_iterator
      operator++(int)
      { return *this; }
    };

C++17

C++17采用了新的设计(尚无源码,看不出实现的区别)

应用

应用时候一般是使用 back_inserter
back_inserter 的实现源码如下:

  /**
   *  @param  x  A container of arbitrary type.
   *  @return  An instance of back_insert_iterator working on @p x.
   *
   *  This wrapper function helps in creating back_insert_iterator instances.
   *  Typing the name of the %iterator requires knowing the precise full
   *  type of the container, which can be tedious and impedes generic
   *  programming.  Using this function lets you take advantage of automatic
   *  template parameter deduction, making the compiler match the correct
   *  types for you.
  */
  //创建一个临时的 back_insert_iterator 对象,执行之后就返回.
  template<typename _Container>
    inline back_insert_iterator<_Container>
    back_inserter(_Container& __x)
    { return back_insert_iterator<_Container>(__x); }

使用ProtoBuffer 自带压缩功能

使用ProtoBuffer 自带压缩功能

起因和本文想要说明的内容

消息中心数据存储时候,是使用key-value的形式来存储邮件,但是protobuf在默认的序列化/反序列化的时候是不进行压缩的。
如果想使用 ProtoBuffer的压缩功能,还需要多做几步,构造一个InputStream/OutputStream传入传出内容。ProtoBufffer可选的 zlib压缩 gzip 压缩,压缩效率极高。对于当前项目来说,压缩之后的数据大小为原来大小的: 15~40%(消息体越大,压缩率越大)(目前正常的邮件信息都超过1k),实用性很强。
网上关于这个部分内容较少,有不少关于这方面的提问,但是回答都语焉不详,官方并没有提供这方面的例子,对于初学者来说想要用好并不容易。
另外,protobuf的这一个InputStream/OutputStream的设计也有非常好的参考价值,大家在写自己的IO函数实现的时候也可以参考这个设计。
因此,写一个走读笔记,供参考。

目标:

  1. 如何使用Protobuffer的压缩功能;
  2. ProtoBuffer io 模块设计方法理解。

Protobuf序列化函数列表

要使用ProtoBuf的压缩功能,则主要用到的bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const; 函数。
需要实现一个派生之后的ZeroCopyOutputStream函数体。

// Write a protocol buffer of this message to the given output.  Returns
// false on a write error.  If the message is missing required fields,
// this may GOOGLE_CHECK-fail.
bool SerializeToCodedStream(io::CodedOutputStream* output) const;
// Like SerializeToCodedStream(), but allows missing required fields.
bool SerializePartialToCodedStream(io::CodedOutputStream* output) const;
// Write the message to the given zero-copy output stream.  All required
// fields must be set.
bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Like SerializeToZeroCopyStream(), but allows missing required fields.
bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Serialize the message and store it in the given string.  All required
// fields must be set.
bool SerializeToString(string* output) const;
// Like SerializeToString(), but allows missing required fields.
bool SerializePartialToString(string* output) const;
// Serialize the message and store it in the given byte array.  All required
// fields must be set.
bool SerializeToArray(void* data, int size) const;
// Like SerializeToArray(), but allows missing required fields.
bool SerializePartialToArray(void* data, int size) const;

// Make a string encoding the message. Is equivalent to calling
// SerializeToString() on a string and using that.  Returns the empty
// string if SerializeToString() would have returned an error.
// Note: If you intend to generate many such strings, you may
// reduce heap fragmentation by instead re-using the same string
// object with calls to SerializeToString().
string SerializeAsString() const;
// Like SerializeAsString(), but allows missing required fields.
string SerializePartialAsString() const;

// Like SerializeToString(), but appends to the data to the string's existing
// contents.  All required fields must be set.
bool AppendToString(string* output) const;
// Like AppendToString(), but allows missing required fields.
bool AppendPartialToString(string* output) const;

如何使用ProtoBuffer压缩功能

  1. 构造一个ZeroCopyOutputStream的派生函数,负责提供压缩解压的内存空间;
  2. 正确调用GzipInputStream,获取结果。

定义ZeroCopyOutputStream派生函数

class CPBufferInputStream: public google::protobuf::io::ZeroCopyInputStream
{
    public:
        CPBufferInputStream() {}
        virtual ~CPBufferInputStream();
        virtual bool Next(const void** data, int* size)
        {
            *data=m_szBuffer+m_idx;
            *size=m_size-m_idx;
            m_idx+=m_size;
            return true;
        }
        virtual void BackUp(int count)
        {
            m_idx-=count;
        }
        virtual bool Skip(int count)
        {
            if(m_idx+count==m_size)
            {
                m_idx+=count;
                return false;
            }
            else if(m_idx+count>m_size)
            {
                return false;
            }

            m_idx+=count;
            return true;

        }
        virtual int64 ByteCount()
        {
            return m_idx;
        }

    private:
        ::google::protobuf::int64 m_idx;
        ::google::protobuf::int64 m_size;
        char m_szBuffer[PROTOBUFFER_MAX_BUFFER_SIZE];
};

class CPBufferOutputStream: public google::protobuf::io::ZeroCopyOutputStream
{
    public:
        CPBufferOutputStream(){}
        ~CPBufferOutputStream(){}

        void Reset()
        {
            m_idx=0;
        }

        std::string ToString()
        {
            return std::string(m_szBuffer,m_idx);
        }

        virtual bool Next(void** data, int* size)
        {
            *data=m_szBuffer+m_idx;
            *size=sizeof(m_szBuffer)-m_idx;
            m_idx+=*size;
            return true;
        }
        virtual void BackUp(int count)
        {
            m_idx-=count;
        }

        virtual ::google::protobuf::int64 ByteCount() const
        {
            return m_idx;
        }

    private:
        ::google::protobuf::int64 m_idx;
        char m_szBuffer[PROTOBUFFER_MAX_BUFFER_SIZE];
};

使用并获取结果

CPBufferInputStream          m_pbinputstream;
CPBufferOutputStream         m_pboutputstream;

//将m_msg PB结构序列化、压缩并将结果传回 str_output 
bool Serialize(std::string* str_output) const
{
    CPBufferOutputStream   stream_output;
    ::google::protobuf::io::GzipOutputStream m_zip(stream_output);

    if(!m_msg.SerializeToZeroCopyStream(&m_zip))
    {
        return false;
    }

    if(!m_zip.Close())
    {
        return false;
    }

    *str_output=stream_output->ToString();
    return true;
}

//将 str_serial 反序列化成PB结构
bool Parse(const std::string& str_serial)
{
    CPBufferInputStream stream_input;
    stream_input->Reset((char*)str_serial.c_str(),(unsigned)str_serial.length());
    ::google::protobuf::io::GzipInputStream m_zip(&stream_input);
    if(!m_msg.ParseFromZeroCopyStream(&m_zip))
    {
        return false;
    }

    return true;
}

ProtoBuffer io 模块设计方法

  1. 在接口设计上,序列化函数需要用到的缓冲区可以是内部缓冲区和外部缓冲区,并没有做严格的限定。
  2. 当需要使用外部缓冲区的时候,PB定义了一个 ZeroCopyOutputStream 这样的一个输入流(这个和STL的IOStream有区别,请注意),指定了只需要Stream提供3个接口:Next(获取buffer地址),BackUp(返还buufer),ByteCount(查看一共使用了多少大小的buffer),接口小而完备。
  3. 压缩之后的序列化,主要是通过实现 ZeroCopyOutputStream 来进行,PB中已经实现了一个通过 GzipInputStream/GzipOutputStream 来进行实现,但是,GZipxxStream 这个接口仍然需要缓冲区,所以此时GzipxxxStream是作为一个适配器(或说是代理)来进行工作,使用时候需要提供一个 ZeroCopyOutputStream 的派生类来传入缓冲区。
  4. 简而言之,PB 在第一次序列化之后(未压缩数据),缓冲区放在 GzipxxxStream 的实现中(可以通过 GZipxxStream::Option()自己指定不指定则会new)。 而序列化完成之后,压缩时候使用的缓冲区,则有 ZeroCopyOutputStream 传入缓冲区来提供。

ascii 图表(测试显示效果)

ascii 图表

测试 ascii 图形在blog中的显示效果的文章
window chrome 下显示 ascii效果最好, mac 差一些。

英文框型

         +---+         
         |V7 |         
         +---+         
           |           
           v           
       +-------+       
       |  V1   |       
       +-------+       
         |  ||         
     -----  |--------  
     |      ---     |  
     v        |     |  
  +-----+     |     |  
  | V2  |     |     |  
  +-----+     |     |  
    | |       |     |  
  --- ---     |     |  
  |     |     |     |  
  v     v     v     v  
+---+ +---+ +---+ +---+
|V5 | |V6 | |V4 | |V3 |
+---+ +---+ +---+ +---+

中文框型

                         +--------+
                         |  顶框  |
                         +--+-----+
               +------------+----------------+
               |                             |
          +----+----+                    +---+---+
          |  左框1  |                    | 右框1 |
          +---------+                    +-------+

C语言

#include "stdio.h"
int main(int argc,char* argv[])
{
    printf("hello world");
    return 0;
}

C语言之二

#include <iostream>

int main(int argc, char *argv[]) {

  /* An annoying "Hello World" example */
  for (auto i = 0; i < 0xFFFF; i++)
    cout << "Hello, World!" << endl;

  char c = '\n';
  unordered_map <string, vector<string> > m;
  m["key"] = "\\\\"; // this is an error

  return -2e3 + 12l;
}

ascii表格

+-----------------------------------------------------------------------------+
|                   |                   |                   |                 |
|    title 1        |     title 2       |     title 3       |     title 4     |
|                   |                   |                   |                 |
+-------------------+-------------------+-------------------+-----------------+
|                   |                   |    bla bla        |                 |
| bla bla           |                   |    bla bla bla    |                 |
| bla bla bla       |    bla bla        |    bla bla bla ...| bla bla         |
| bla bla bla ...   |    bla bla bla    |                   | bla bla bla     |
|                   |    bla bla bla ...|                   | bla bla bla ... |
|                   |                   |                   |                 |
|                   |                   |                   |                 |
+-----------------------------------------------------------------------------+

ascii椭圆形


         ***********                  ***********
     ****           ****          ****           ****
  ***                   ***    ***                   ***
 *                         *  *                         *
*         ellipse           **        ellipse            *
*         ellipse...        **        ellipse...         *
*         ellipse..         **        ellipse..          *
 *                         *  *                         *
  ***                   ***    ***                   ***
     ****           ****          ****           ****
         ***********                  ***********

ascii 箭头线


                            -----                                             -----                       
                      -----/     \----                                   ----/     \----                  
                -----/                \----                         ----/               \----             
          -----/                           \----               ----/                         \----        
    -----/                                      \----     ----/                                   \----   
---/                                                 \---/                                             \->

小箭头

                  ^
                  |
           <------+------> 
                  |
                  v

大箭头

                  . 
                 /_\
                  |
                  |
         <|-------+--------|>
                  |
                 _|_
                 \ /
                  ' 

自由画(鼠标画人头)

           ************ 
          **********************
       ******  *************** ****  
     **********           *** ******* 
   *****                    ***    ** 
     **                       **      
     *                          *  
     *      *          *        ** 
     *                           * 
     *                           * 
     **                         ** 
      *                  *     **  
      **         *    ***     **  
       **        ******     ***  
         *                ***   
          ***            **   
            **         ***  
              ****   **                                                                       
                  ****  

自由画(复制人头)

                                                          ************              
                                                        **********************     
                                                     ******  *************** ****  
                                                   **********           *** *******
                                                 *****                    ***    **
                                                   **                       **     
                                                   *                          *    
                                                   *      *          *        **   
            ************                           *                           *   
           **********************                  *                           *   
        ******  *************** ****               **                         **   
      **********           *** *******              *                  *     **    
    *****                    ***    **              **         *    ***     **     
      **                       **                    **        ******     ***      
      *                          *                     *                ***        
      *      *          *        **                     ***            **          
      *                           *                       **         ***           
      *                           *                         ****   **              
      **                         **                             ****               
       *                  *     **                                                 
       **         *    ***            ************                                 
        **        ******     *       ********************                          
          *                ***    ******  *************** ****                     
           ***            **    **********           *** *******                   
             **         ***   *****                    ***    **                   
               ****   **        **                       **                        
                   ****         *                          *                       
                                *      *          *        **                      
                                *                           *                      
                                *                           *                      
                                **                         **                      
                                 *                  *     **                       
                                 **         *    ***     **                        
                                  **        ******     ***                         
                                    *                ***                           
                                     ***            **                             
                                       **         ***                              
                                         ****   **                                 
                                             ****                                  

graphviz

digraph example3 {
Server1 -> Server2
Server2 -> Server3
Server3 -> Server1

Server1 [shape=box, label="Server1\nWeb Server", fillcolor="#ABACBA", style=filled]
Server2 [shape=triangle, label="Server2\nApp Server", fillcolor="#DDBCBC", style=filled]
Server3 [shape=circle, label="Server3\nDatabase Server", fillcolor="#FFAA22",style=filled]
}

文件系统缓存调优检查表(性能之巅-内存部分笔记)

目标

为你的操作系统开发出以下的步骤:

  1. 一个文件系统调优检查表。这应该列出文件系统缓存,以及如何检查他们的大小、使用情况和命中率;
  2. 一个文件系统负载特征归纳表。包括如何得到各项详细信息,并优先使用系统现有的文件系统观察工具。

文件系统调优检查表

文件系统的缓存 表 3.2 磁盘I/O缓存层级示例

缓存 实例
应用程序缓存 apache/gameserver
服务器缓存 memcached/redis
数据库缓存 mysql 缓存
目录缓存 DNLC
文件元数据缓存 inode缓存
操作系统缓存区高速缓存 segvn
文件系统主缓存 ZFS ARC
文件系统次缓存 ZFS L2ARC
设备缓存 ZFS vdev
块设备 缓存区高速缓存
磁盘控制器缓存 RAID卡缓存
存储阵列缓存 -
磁盘内置缓存 -

Linux-文件系统缓存

cache && buffer

free, ps,top,vmstat 命令查看,主要是通过 /proc/meminfo 算出来的,其中 used 字段,是通过 used=total-free 得到,是个间接值。

[LawrenceChi@LOCAL-96-156 ~]$ free ;top -b -n 1 | grep -e "Mem:" -e "Swap:"; vmstat 1 5;vmstat -a 1 5;
             total       used       free     shared    buffers     cached
Mem:       8062400    6961432    1100968          0     390780    3838792
-/+ buffers/cache:    2731860    5330540
Swap:      2097144      55724    2041420
Mem:   8062400k total,  6961944k used,  1100456k free,   390780k buffers
Swap:  2097144k total,    55724k used,  2041420k free,  3838796k cached
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0  55724 1100952 390780 3838796    0    0     0    12    0    4  4  1 95  0  0
 0  0  55724 1100960 390784 3838796    0    0     0    88  490  556  0  0 99  0  0
 0  0  55724 1100960 390784 3838796    0    0     0     0  537  570  0  0 99  0  0
 0  0  55724 1100836 390784 3838928    0    0     0     0 1298  833  2  1 97  0  0
 0  0  55724 1100868 390784 3838960    0    0     0     0  535  570  1  1 99  0  0
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st
 1  0  55724 1100868 2023192 4141020    0    0     0    12    0    4  4  1 95  0  0
 0  0  55724 1101496 2023228 4140004    0    0     0    16  861  654  1  1 98  0  0
 0  0  55724 1101496 2023300 4140048    0    0     0   180 1085  767  1  1 98  0  0
 0  0  55724 1101496 2023336 4140056    0    0     0     0  611  558  1  1 99  0  0
 0  0  55724 1101380 2023444 4140076    0    0     0     0 1215  781  2  1 97  0  0
[LawrenceChi@LOCAL-96-156 ~]$

cache 是文件系统读缓存。
buffer是文件系统写缓存,通过查看 /proc/meminfo 中的Dirty,查看有多少“脏页”。

文件是否已经在缓存中及处理

文件系统读缓存,可以通过vmtouch工具来检查文件是否已经在缓存中。

vmtouch 判断文件是否在缓存中原理

  1. fopen 需要检测的文件,mmap到内存;
  2. 通过 mincore 系统调用,判断文件页是否已经在内存中;
  3. ummap,输出统计结果。
  4. 可以按照文件、文件夹进行统计;

vmtouoch 将文件加载到缓存中的原理

  1. fopen 需要检测的文件,mmap到内存;
  2. 每一个页(page)访问一次,触发文件缓存机制;
  3. 退出;

vmtouch 将文件从缓存中释放掉的原理

  1. fopen 文件, mmap到内存;
  2. posix_fadvise 系统调用 进行内存释放。 关于 posix_fadvise不应定真正释放内存,这里有一篇文章说明文件脏数据的对内存释放的影响;
  3. ummap, flcose。

缓存占用空间

cat /proc/meminfo 中 Buffers、Cached可以看到文件系统缓存占用情况:

[LawrenceChi@LOCAL-96-157 gameserver]$ cat /proc/meminfo
MemTotal:        8062400 kB
MemFree:          201160 kB
Buffers:          294268 kB
Cached:          3373396 kB
SwapCached:        83540 kB
Active:          3994004 kB
Inactive:        2531980 kB
Active(anon):    1891580 kB
Inactive(anon):   968060 kB
Active(file):    2102424 kB
Inactive(file):  1563920 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       2097144 kB
SwapFree:         824900 kB
Dirty:               232 kB
Writeback:             0 kB
AnonPages:       2775564 kB
Mapped:            62636 kB
Shmem:              1308 kB
Slab:            1030904 kB
SReclaimable:     878300 kB
SUnreclaim:       152604 kB
KernelStack:       16280 kB
PageTables:       161188 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     6128344 kB
Committed_AS:   19296144 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      159412 kB
VmallocChunk:   34359571032 kB
HardwareCorrupted:     0 kB
AnonHugePages:   1036288 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       10240 kB
DirectMap2M:     8378368 kB

vmstat命令中关于内存、缓存的选项说明

vmstat -m

The -m switch displays slabinfo.
释放页面缓存,把那个比较连个 vmstat 得到变少的数据统计。
buffer_head/radix_tree_node:这是页面缓存

Free pagecache only use following command
# sync; echo 1 > /proc/sys/vm/drop_caches
lawrencechi@LawrencechideMacBook-Pro ~/Downloads » cat clean.1.txt| awk '{ if($7<$2){b=$2-$7; printf("%s, after-befor= %d\n",$0,b);}}'  | sort -t"=" -k 2 -n -r | head -n 10
buffer_head              969164 970584    104     37               buffer_head                8500  59977    104     37               , after-befor= 960664
radix_tree_node           38661  40180    560      7               radix_tree_node            4418  14392    560      7               , after-befor= 34243
filp                      21109  24260    192     20               filp                      20890  24260    192     20               , after-befor= 219
cred_jar                   1809   2620    192     20               cred_jar                   1705   2620    192     20               , after-befor= 104
proc_inode_cache         111179 111558    656      6               proc_inode_cache         111122 111558    656      6               , after-befor= 57
size-32                   19411  19600     32    112               size-32                   19356  19600     32    112               , after-befor= 55
size-128                   3420   3630    128     30               size-128                   3367   3630    128     30               , after-befor= 53
anon_vma_chain            93932 102025     48     77               anon_vma_chain            93884 102025     48     77               , after-befor= 48
size-1024                  2048   2384   1024      4               size-1024                  2004   2384   1024      4               , after-befor= 44
task_delay_info            1029   1292    112     34               task_delay_info             990   1292    112     34               , after-befor= 39
Free dentries and inodes use following command
# sync; echo 2 > /proc/sys/vm/drop_caches

这次得到的结果是:dentry,pro_inode_cache,ext4_inode_cache

lawrencechi@LawrencechideMacBook-Pro ~/Downloads » cat clean.2.txt| awk '{ if($7<$2){b=$2-$7; printf("%s, after-befor= %d\n",$0,b);}}'  | sort -t"=" -k 2 -n -r | head -n 15
dentry                   1392310 1392420    192     20  dentry                    14288  81380    192     20        , after-befor= 1378022
buffer_head              969164 970584    104     37    buffer_head               11576  59718    104     37    , after-befor= 957588
proc_inode_cache         111179 111558    656      6    proc_inode_cache             50    168    656      6    , after-befor= 111129
ext4_inode_cache          99263  99296   1016      4    ext4_inode_cache           1187   2644   1016      4    , after-befor= 98076
radix_tree_node           38661  40180    560      7    radix_tree_node            4685  14091    560      7    , after-befor= 33976
size-64                   44416  44840     64     59    size-64                   12858  41772     64     59    , after-befor= 31558
inode_cache                6530   6588    592      6    inode_cache                4266   4488    592      6    , after-befor= 2264
size-128                   3420   3630    128     30    size-128                   2013   3450    128     30    , after-befor= 1407
anon_vma_chain            93932 102025     48     77    anon_vma_chain            93401 102025     48     77    , after-befor= 531
vm_area_struct           101400 110903    200     19    vm_area_struct           101000 110903    200     19    , after-befor= 400
anon_vma                  54374  59340     40     92    anon_vma                  54095  59340     40     92    , after-befor= 279
filp                      21109  24260    192     20    filp                      20854  24260    192     20    , after-befor= 255
pid                        1324   1560    128     30    pid                        1245   1560    128     30    , after-befor= 79
fs_cache                    629    944     64     59    fs_cache                    563    944     64     59    , after-befor= 66
task_delay_info            1029   1292    112     34    task_delay_info             969   1292    112     34    , after-befor= 60
Free pagecache, dentries and inodes in cache memory# sync; echo 3 > /proc/sys/vm/drop_caches

结论,在使用 vmstat -m进行分析内存缓存的时候,我们最重要关注是:
buffer_head/radix_tree_node/dentry,pro_inode_cache,ext4_inode_cache

缓存项 说明
buffer_head 每个buffer_head管理的单元是内存页中对应的一个磁盘块,也就是说buffer_head管理单元是 “页”的一个子集. buffer_head理解、解析
radix_tree_node radix树(基数树)数据缓存.Linux 内核数据结构:Radix树 Radix tree(wiki)
dentry 高速目录缓存
pro_inode_cache /proc 文件系统缓存,可以侧面反映进程开启情况
ext4_inode_cache NULL

cache 命中率计算

Linux Page Cache Hit Ratio

vmstat -s

The -s switch displays a table of various event counters and memory statistics. This display does not repeat.

[LawrenceChi@LOCAL-96-157 ~]$ vmstat -s
8062400  total memory
7753332  used memory
4019728  active memory
2819196  inactive memory
309068  free memory
314848  buffer memory
3536000  swap cache
2097144  total swap
1106356  used swap
990788  free swap
30702361 non-nice user cpu ticks
12601 nice user cpu ticks
21383073 system cpu ticks
2601397816 idle cpu ticks
4037027 IO-wait cpu ticks
15937 IRQ cpu ticks
1583247 softirq cpu ticks
0 stolen cpu ticks
106072566 pages paged in
405098416 pages paged out
685075 pages swapped in
2390879 pages swapped out
495740634 interrupts
2065133133 CPU context switches
1457495157 boot time
13714053 forks
[LawrenceChi@LOCAL-96-157 ~]$