LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Busybox下Bootchart的使用

Busybox包含了很多小工具,这些工具短小精悍有效。bootchartd就是其中之一。

Bootchart is a tool for performance analysis and virtualization of the GNU/Linux boot process.

Resource utilization and process information are collected during the boot process and are later rendered in a PNG, SVG or EPS encoded chart.

将bootchartd使能

要使能bootchartd首先需要配置Busybox:

修改project/prj_phone/config/normal/common/config.vendor:

CONFIG_USER_BUSYBOX_BOOTCHARTD=y (使能bootchartd)
CONFIG_USER_BUSYBOX_TAR=y (bootchard需要使用tar工具)
CONFIG_USER_BUSYBOX_FEATURE_SEAMLESS_GZ=y (tar子命令,-x选项)

然后还需要修改UBoot设置,将rdinit设置为/bin/bootchartd:

drivers/mtd/partition/partition.c

add_partition_to_bootargs
  ->sprintf((char *)bootargs_cmd, "rdinit=/bin/bootchartd console=ttyS1,115200 no_console_suspend");

内核会去解析Kernel command line:

static int __init rdinit_setup(char *str)
{
    unsigned int i;

    ramdisk_execute_command = str;
    /* See "auto" comment in init_setup */
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("rdinit=", rdinit_setup);

在start_kernel->rest_init->kernel_init->init_post中会执行ramdisk_execute_command:

static noinline int init_post(void)
{
    /* need to finish all async __init code before freeing the memory */

    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s\n",
                ramdisk_execute_command);
    }

}

在生成的ramdisk中确保bootchartd连接到busybox。

在project/prj_phone/rootfs/normal/etc/rc中添加启动bootchart的脚本,然后在adb shell进入后停止bootchartd。

--- rc    (revision 1173)
+++ rc    (working copy)
@@ -1,5 +1,5 @@
#!/bin/sh

/bin/mount -t proc proc /proc
 
echo "Starting mdevd..."
@@ -8,6 +7,7 @@
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
 
+/bin/bootchartd start (将bootchartd放在proc挂载之后,因为bootchartd需要依赖proc节点。)

最后结果生成在/var/log/bootlog.tgz。

PS:默认情况下执行bootchartd错误信息没有吐出,可以bootchart start 2>&1这样子输出。

bootchart分析工具pybootchartgui

使用Ubuntu默认的bootchart解析会失败,在这里下载工具。然后开始编译bootchart:

make

编译后使用pybootchartgui.py处理,然后查看结果bootchart.png。

./bootchart/pybootchartgui.py bootlog.tgz --show-all

生成bootchart.png文件:

bootchart

这里可以清晰地看到CPU(user+sys)、I/O(wait)、Disk的使用情况,以及每个进程的Running、Unint.sleep、Sleeping、Zombie情况。

如果阅读输出结果

如下可以将Bootchart输出结果分为三部分:系统信息、系统资源概览、进程信息。

image

系统信息

这些信息主要保存在bootlog.tgz的header中,包括title、uname、kernel options等。还有整个bootchart分析时间段。

系统资源概览

CPU资源的占用率,可以看出统计只有在bootchart start之后才开始。可以看出整个bootchart期间的CPU占用率变化情况,在cfgnv_init.sh到internet.sh期间,CPU占用率非常高。

PS:I/O、Disk相关的显示可能存在问题。

磁盘信息对应proc_diskstats.log

进程信息

对应proc_ps.log和proc_stat.log

1.通过下图可以看出进程telnetd的开始时间和结束时间;结合image可以知道今晨执行期间不同阶段的状态。

image

2.通过下图可以看出进程间的父子关系,父进程引出虚线指向子进程。

image

使用Bootchart帮助文档

pybootchartgui简单分析

入口文件是pybootchartgui.py,实体在pybootchartgui文件夹中。

pybootchartgui.py->main.py-|->parsing.py-|->process_tree.py
                                       |->batch.py-|->draw.py
                                       |->gui.py

Bootchart代码分析

Busybox的官方代码github.com的镜像

在applet_tables.h定义了bootchartd的入口:

const char applet_names[] ALIGN1 = ""

"bootchartd" "\0"

;

int (*const applet_main[])(int argc, char **argv) = {

bootchartd_main,

};

bootchartd_main

 

int bootchartd_main(int argc UNUSED_PARAM, char **argv)
{
    unsigned sample_period_us;
    pid_t parent_pid, logger_pid;
    smallint cmd;
    int process_accounting;
    enum {
        CMD_STOP = 0,
        CMD_START,
        CMD_INIT,
        CMD_PID1, /* used to mark pid 1 case */
    };

    INIT_G();

    parent_pid = getpid();
    if (argv[1]) {
        cmd = index_in_strings("stop\0""start\0""init\0", argv[1]);
        if (cmd < 0)
            bb_show_usage();
        if (cmd == CMD_STOP) {
            pid_t *pidList = find_pid_by_name("bootchartd"); (stop时,找到bootchartd这个进程,并关闭)
            while (*pidList != 0) {
                if (*pidList != parent_pid)
                    kill(*pidList, SIGUSR1);
                pidList++;
            }
            return EXIT_SUCCESS;
        }
    } else {
        if (parent_pid != 1) (检查是否是init进程,如果不是,则说明是在命令行调用不带参数的bootchartd,显示usage)
            bb_show_usage();
        cmd = CMD_PID1;
    }
    /* Here we are in START, INIT or CMD_PID1 state */

    /* Read config file: */ (确认bootchartd的配置信息,采样间隔、采样数。)
    sample_period_us = 200 * 1000;
    process_accounting = 0;
    if (ENABLE_FEATURE_BOOTCHARTD_CONFIG_FILE) {
        char* token[2];
        parser_t *parser = config_open2("/etc/bootchartd.conf" + 5, fopen_for_read);
        if (!parser)
            parser = config_open2("/etc/bootchartd.conf", fopen_for_read);
        while (config_read(parser, token, 2, 0, "#=", PARSE_NORMAL & ~PARSE_COLLAPSE)) {
            if (strcmp(token[0], "SAMPLE_PERIOD") == 0 && token[1])
                sample_period_us = atof(token[1]) * 1000000;
            if (strcmp(token[0], "PROCESS_ACCOUNTING") == 0 && token[1]
             && (strcmp(token[1], "on") == 0 || strcmp(token[1], "yes") == 0)
            ) {
                process_accounting = 1;
            }
        }
        config_close(parser);
        if ((int)sample_period_us <= 0)
            sample_period_us = 1; /* prevent division by 0 */
    }
    /* Create logger child: */ (创建用于采样子进程)
    logger_pid = fork_or_rexec(argv);
    if (logger_pid == 0) { /* child */
        char *tempdir;

        bb_signals(0
            + (1 << SIGUSR1)
            + (1 << SIGUSR2)
            + (1 << SIGTERM)
            + (1 << SIGQUIT)
            + (1 << SIGINT)
            + (1 << SIGHUP)
            , record_signo);

        if (DO_SIGNAL_SYNC)
            /* Inform parent that we are ready */
            raise(SIGSTOP);

        /* If we are started by kernel, PATH might be unset.
         * In order to find "tar", let's set some sane PATH:
         */
        if (cmd == CMD_PID1 && !getenv("PATH"))
            putenv((char*)bb_PATH_root_path);

        tempdir = make_tempdir(); (生成存放采样数据的临时目录)
        do_logging(sample_period_us, process_accounting);
        finalize(tempdir, cmd == CMD_START ? argv[2] : NULL, process_accounting); (打包log文件,并清理中间文件)
        return EXIT_SUCCESS;
    }

    /* parent */

    USE_FOR_NOMMU(argv[0][0] &= 0x7f); /* undo fork_or_rexec() damage */

    if (DO_SIGNAL_SYNC) {
        /* Wait for logger child to set handlers, then unpause it.
         * Otherwise with short-lived PROG (e.g. "bootchartd start true")
         * we might send SIGUSR1 before logger sets its handler.
         */
        waitpid(logger_pid, NULL, WUNTRACED);
        kill(logger_pid, SIGCONT);
    }

    if (cmd == CMD_PID1) { (非init/start/stop三者之一的子命令)
        char *bootchart_init = getenv("bootchart_init");
        if (bootchart_init)
            execl(bootchart_init, bootchart_init, NULL);
        execl("/init", "init", NULL);
        execl("/sbin/init", "init", NULL);
        bb_perror_msg_and_die("can't execute '%s'", "/sbin/init");
    }

    if (cmd == CMD_START && argv[2]) { /* "start PROG ARGS" */
        pid_t pid = xvfork();
        if (pid == 0) { /* child */
            argv += 2;
            BB_EXECVP_or_die(argv);
        }
        /* parent */
        waitpid(pid, NULL, 0);
        kill(logger_pid, SIGUSR1);
    }
    return EXIT_SUCCESS;
}

 

do_logging

作为bootchartd的主题,将需要记录的log保存到文件中。

static void do_logging(unsigned sample_period_us, int process_accounting)
{
    FILE *proc_stat = xfopen("proc_stat.log", "w");
    FILE *proc_diskstats = xfopen("proc_diskstats.log", "w");
    FILE *proc_meminfo = xfopen("proc_meminfo.log", "w");
    //FILE *proc_netdev = xfopen("proc_netdev.log", "w");
    FILE *proc_ps = xfopen("proc_ps.log", "w");
    int look_for_login_process = (getppid() == 1);
    unsigned count = 60*1000*1000 / sample_period_us; /* ~1 minute */

    if (process_accounting) {
        close(xopen("kernel_pacct", O_WRONLY | O_CREAT | O_TRUNC));
        acct("kernel_pacct");
    }

    while (--count && !bb_got_signal) {
        char *p;
        int len = open_read_close("/proc/uptime", G.jiffy_line, sizeof(G.jiffy_line)-2);
        if (len < 0)
            goto wait_more;
        /* /proc/uptime has format "NNNNNN.MM NNNNNNN.MM" */
        /* we convert it to "NNNNNNMM\n" (using first value) */
        G.jiffy_line[len] = '\0';
        p = strchr(G.jiffy_line, '.');
        if (!p)
            goto wait_more;
        while (isdigit(*++p))
            p[-1] = *p;
        p[-1] = '\n';
        p[0] = '\0';

        dump_file(proc_stat, "/proc/stat");
        dump_file(proc_diskstats, "/proc/diskstats");
        dump_file(proc_meminfo, "/proc/meminfo");
        //dump_file(proc_netdev, "/proc/net/dev");
        if (dump_procs(proc_ps, look_for_login_process)) {
            /* dump_procs saw a getty or {g,k,x}dm (在从/proc/xxx/stat读取到getty、gdm、kdm、xdm后,在2s内停止记录log。)
             * stop logging in 2 seconds:
             */
            if (count > 2*1000*1000 / sample_period_us)
                count = 2*1000*1000 / sample_period_us;
        }
        fflush_all(); (fflush(NULL)会将所有打开输出流刷出)
wait_more:
        usleep(sample_period_us); (每次读取后睡眠sample_period_us段时间。)
    }
}

从这里可知,bootchartd从启动之后每个sample_period_us段时间,就从proc中读取信息,保存在相应的log中。

  /proc/uptime 16091.87 16040.75
proc_stat.log /proc/stat

cpu  550 0 1958 1583767 0 0 84 0 0 0
cpu0 550 0 1958 1583767 0 0 84 0 0 0
intr 1337973 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 29 0 0 0 0 0 0 1 64 0 0 0 43 0 885974 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25943 0 13160 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 339767 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 72990 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 1774228
btime 65536
processes 1541
procs_running 1
procs_blocked 0
softirq 72046 0 29501 1 639 0 0 29498 0 5045 7362

proc_diskstats.log /proc/diskstats

1       0 ram0 0 0 0 0 0 0 0 0 0 0 0
1       1 ram1 0 0 0 0 0 0 0 0 0 0 0
1       2 ram2 0 0 0 0 0 0 0 0 0 0 0
1       3 ram3 0 0 0 0 0 0 0 0 0 0 0
1       4 ram4 0 0 0 0 0 0 0 0 0 0 0
1       5 ram5 0 0 0 0 0 0 0 0 0 0 0
1       6 ram6 0 0 0 0 0 0 0 0 0 0 0
1       7 ram7 0 0 0 0 0 0 0 0 0 0 0
1       8 ram8 0 0 0 0 0 0 0 0 0 0 0
1       9 ram9 0 0 0 0 0 0 0 0 0 0 0
1      10 ram10 0 0 0 0 0 0 0 0 0 0 0
1      11 ram11 0 0 0 0 0 0 0 0 0 0 0
1      12 ram12 0 0 0 0 0 0 0 0 0 0 0
1      13 ram13 0 0 0 0 0 0 0 0 0 0 0
1      14 ram14 0 0 0 0 0 0 0 0 0 0 0
1      15 ram15 0 0 0 0 0 0 0 0 0 0 0
7       0 loop0 0 0 0 0 0 0 0 0 0 0 0
7       1 loop1 0 0 0 0 0 0 0 0 0 0 0
7       2 loop2 0 0 0 0 0 0 0 0 0 0 0
7       3 loop3 0 0 0 0 0 0 0 0 0 0 0
7       4 loop4 0 0 0 0 0 0 0 0 0 0 0
7       5 loop5 0 0 0 0 0 0 0 0 0 0 0
7       6 loop6 0 0 0 0 0 0 0 0 0 0 0
7       7 loop7 0 0 0 0 0 0 0 0 0 0 0
31       0 mtdblock0 0 0 0 0 0 0 0 0 0 0 0
31       1 mtdblock1 0 0 0 0 0 0 0 0 0 0 0
31       2 mtdblock2 0 0 0 0 0 0 0 0 0 0 0
35       3 zftl3 0 0 0 0 0 0 0 0 0 0 0
31       3 mtdblock3 0 0 0 0 0 0 0 0 0 0 0
35       4 zftl4 0 0 0 0 0 0 0 0 0 0 0
31       4 mtdblock4 0 0 0 0 0 0 0 0 0 0 0
35       5 zftl5 0 0 0 0 0 0 0 0 0 0 0
31       5 mtdblock5 0 0 0 0 0 0 0 0 0 0 0
35       6 zftl6 2 6 64 0 2 0 16 75 0 75 75
31       6 mtdblock6 0 0 0 0 0 0 0 0 0 0 0
31       7 mtdblock7 0 0 0 0 0 0 0 0 0 0 0
35       8 zftl8 0 0 0 0 0 0 0 0 0 0 0
31       8 mtdblock8 0 0 0 0 0 0 0 0 0 0 0
31       9 mtdblock9 0 0 0 0 0 0 0 0 0 0 0
31      10 mtdblock10 0 0 0 0 0 0 0 0 0 0 0
31      11 mtdblock11 0 0 0 0 0 0 0 0 0 0 0
31      12 mtdblock12 0 0 0 0 0 0 0 0 0 0 0
31      13 mtdblock13 0 0 0 0 0 0 0 0 0 0 0
35      14 zftl14 0 0 0 0 0 0 0 0 0 0 0
31      14 mtdblock14 0 0 0 0 0 0 0 0 0 0 0
31      15 mtdblock15 0 0 0 0 0 0 0 0 0 0 0
31      16 mtdblock16 0 0 0 0 0 0 0 0 0 0 0
31      17 mtdblock17 0 0 0 0 0 0 0 0 0 0 0

proc_meminfo.log /proc/meminfo

MemTotal:          23940 kB
MemFree:            8240 kB
Buffers:               0 kB
Cached:             3644 kB
SwapCached:            0 kB
Active:             3216 kB
Inactive:           2444 kB
Active(anon):       2016 kB
Inactive(anon):        0 kB
Active(file):       1200 kB
Inactive(file):     2444 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:          2032 kB
Mapped:             1540 kB
Shmem:                 0 kB
Slab:               7980 kB
SReclaimable:       1796 kB
SUnreclaim:         6184 kB
KernelStack:         728 kB
PageTables:          244 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:       11968 kB
Committed_AS:       6640 kB
VmallocTotal:     999424 kB
VmallocUsed:      130280 kB
VmallocChunk:     732668 kB

proc_ps.log /proc/xxx/stat

1 (init) S 0 1 1 0 -1 4194560 92 111177 6 7 0 135 223 301 20 0 1 0 3 1167360 91 4294967295 32768 461096 3201900288 32018
99704 3069296632 0 0 0 543239 3221332596 0 0 17 0 0 0 0 0 0 495616 497086 503808

     

dump_procs

 

static int dump_procs(FILE *fp, int look_for_login_process)
{
    struct dirent *entry;
    DIR *dir = opendir("/proc");
    int found_login_process = 0;

    fputs(G.jiffy_line, fp);
    while ((entry = readdir(dir)) != NULL) {
        char name[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
        int stat_fd;
        unsigned pid = bb_strtou(entry->d_name, NULL, 10);
        if (errno)
            continue;

        /* Android's version reads /proc/PID/cmdline and extracts
         * non-truncated process name. Do we want to do that? */

        sprintf(name, "/proc/%u/stat", pid); (通过readdir遍历,然后读取此pid下的stat)
        stat_fd = open(name, O_RDONLY);
        if (stat_fd >= 0) {
            char *p;
            char stat_line[4*1024];
            int rd = safe_read(stat_fd, stat_line, sizeof(stat_line)-2);

            close(stat_fd);
            if (rd < 0)
                continue;
            stat_line[rd] = '\0';
            p = strchrnul(stat_line, '\n');
            *p++ = '\n';
            *p = '\0';
            fputs(stat_line, fp); (将读取stat的内容写入文件)
            if (!look_for_login_process)
                continue;
            p = strchr(stat_line, '(');
            if (!p)
                continue;
            p++;
            strchrnul(p, ')')[0] = '\0';
            /* Is it gdm, kdm or a getty? */
            if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm')
             || strstr(p, "getty")
            ) {
                found_login_process = 1;
            }
        }
    }
    closedir(dir);
    fputc('\n', fp);
    return found_login_process;
}

 

finalize

写header文件,然后和之前保存的log文件一起打包,到/var/log/bootlog.tgz中。

优化提升

增加系统内存的监控

修改bootchartd的do_logging增加输出内容,比如增加meminfo。

     FILE *proc_stat = xfopen("proc_stat.log", "w");
     FILE *proc_diskstats = xfopen("proc_diskstats.log", "w");
+    FILE *proc_meminfo = xfopen("proc_meminfo.log", "w");
     //FILE *proc_netdev = xfopen("proc_netdev.log", "w");
     FILE *proc_ps = xfopen("proc_ps.log", "w");
     int look_for_login_process = (getppid() == 1);
@@ -240,6 +241,7 @@
 
         dump_file(proc_stat, "/proc/stat");
         dump_file(proc_diskstats, "/proc/diskstats");
+        dump_file(proc_meminfo, "/proc/meminfo");
         //dump_file(proc_netdev, "/proc/net/dev");
         if (dump_procs(proc_ps, look_for_login_process)) {
             /* dump_procs saw a getty or {g,k,x}dm

在bootlog.tgz压缩包中会增加proc_meminfo.log文件。分析结果如下,可以看出系统的内存使用情况。

修改pybootchartgui/draw.py的render_charts内存使用部分,mem_scale使用MemTotal,从上到下按照Free+Buffers+Cached+Used来显示。

image

可以清晰地看出整个内存的消耗情况,由于Buffers为0,所以MemTotal=MemFree+Cached+Used.

内核log分析

之前的内核启动和用户空间的启动都是分开处理的,如果能将两者整合到一张图表中,使用一根Timeline那么就能更好的进行度量启动时间。

pybootchartgui可以将dmesg的内容作为一个进程进行分析。

在busybox中使能busybox:

-# CONFIG_USER_BUSYBOX_DMESG is not set
+ CONFIG_USER_BUSYBOX_DMESG=y

修改bootchard.c,执行dmesg命令将内核log存放到dmesg中,然后打包给pybootchartgui分析。

+    system(xasprintf("dmesg >dmesg"));
+
     /* Package log files */
-    system(xasprintf("tar -zcf /var/log/bootlog.tgz header %s *.log", process_accounting ? "kernel_pacct" : ""));
+    system(xasprintf("tar -zcf /var/log/bootlog.tgz header dmesg %s *.log", process_accounting ? "kernel_pacct" : ""));
     /* Clean up (if we are not in detached tmpfs) */
     if (tempdir) {
         unlink("header");
@@ -315,6 +319,8 @@
         unlink("proc_diskstats.log");
         //unlink("proc_netdev.log");
         unlink("proc_ps.log");
+        unlink("proc_meminfo.log");
+        unlink("dmesg");
         if (process_accounting)
             unlink("kernel_pacct");
         rmdir(tempdir);

由于bootchart的单位是秒级,所以很多initcall细节被忽视了。只能看到一个大概的情况,其中k-boot是整个内核启动时间段。可以看出内核使用了多长时间。

image

内核和用户空间的同步

从上面可知内核和用户空间的Timeline图表都可以使用了,那么能否将这两者放到一个Timeline里面呢。

就来看看init_post这个函数。

static noinline int init_post(void)
{
    /* need to finish all async __init code before freeing the memory */
    async_synchronize_full();
    free_initmem(); 内核的终点
    mark_rodata_ro();
    system_state = SYSTEM_RUNNING;
    numa_default_policy();

         KERNEL_START_END=1;


    current->signal->flags |= SIGNAL_UNKILLABLE;

    printk(KERN_ALERT "%s ramdisk_execute_command=%s\n", __func__, ramdisk_execute_command);
    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s\n",
                ramdisk_execute_command);
    }

    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
    printk(KERN_ALERT "%s execute_command=%s\n", __func__, execute_command);
    if (execute_command) {
        run_init_process(execute_command);
        printk(KERN_WARNING "Failed to execute %s.  Attempting "
                    "defaults...\n", execute_command);
    }
    run_init_process("/sbin/init"); 用户空间的起点
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");
}

从上面可知,可以将k-boot的终点作为init的起点。

pybootchartgui增强功能

./bootchart/pybootchartgui.py bootlog --show-all

能显示多显示进程的pid。

很方便强大的工功能interactive模式

如下的图可以看出,可以进行矢量缩放,很方便可总体、细节。右上角的Show more可以看到进程pid。

image

Kernel boot视图则更形象的得出时间先后的瀑布形式结构图。

绿红+/-号适用于修改时间轴粒度,白色+/-使用缩放整体图像。

image

实战优化

从下图可以看出从init开始的进程之间的关系,init作为所有用户空间进程的父进程。

image

观察上面的图表,重点查看init分支出来的进程。

1.由于是单核CPU,所以他们应该是顺序执行。init的子进程,rc占用了最多的运行时间。

2.这个图形就像一个一个倒楼梯下来,那个楼梯距离长就表示独占时间长。

结合/etc/rc文件分析,更能看出问题所在。

1.nvserver

2.cfgnv_init.sh

3.internet.sh

4.sleep 7,有一个睡眠硬生生睡了7秒。

在进行简单的修改rc之后,效果明显提升。

-cfgnv_init.sh
+cfgnv_init.sh &
 
zte_usbCfgMng &
sbin/at_ctl &
@@ -175,8 +175,8 @@
 
# apps start
#####sunquan start
-netdog_init_set.sh
-internet.sh
+netdog_init_set.sh &
+internet.sh &
#####sunquan end
 
 
@@ -188,7 +188,7 @@
#pc_server &
## please confirm your app should place after or before
## for dial up and wifi go faster
-sleep 7
+sleep 2

结果如下,可以看出整个rc进程的执行时间从19s降低到7s。

小结:

1.如果daemon的内容可以推迟的话,尽量推迟。

2.如果可以并行执行,尽量并行。只留下必须串行的在rc中。

3.不能有长延时,sleep 7很恐怖。

image

扩展阅读

/proc/stat

bootchart主要读取第一行的数据。

cpu  433 0 1276 63007 0 1 92 0 0 0
cpu0 433 0 1276 63007 0 1 92 0 0 0
intr 458145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 0 0 0 0 0 0 1 63 0 0 0 38 0 41926 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 27851 0 867 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 372841 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14531 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 97452
btime 65536
processes 1437
procs_running 1
procs_blocked 0
softirq 41439 0 6091 1 147 0 0 28275 0 247 6678

第一行的数据表示的是CPU总的使用情况,依次是:user nice system idle iowait irq softirq steal guest guest_nice。

这些数值的单位是jiffies,jiffies是内核中的一个全局变量,用来记录系统以来产生的节拍数。在Linux中,一个节拍大致可理解为操作系统进程调度的最小时间片。

user(433) 从系统开始累计到当前时刻,处于用户态的运行时间,不包含nice值为负进程。

nice(0) 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间。

system(1276) 从系统启动开始累计到当前时刻,处于核心态的运行时间。

idle(63007) 从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间

iowait(0) 从系统启动开始累计到当前时刻,IO等待时间

irq(1) 从系统启动开始累计到当前时刻,硬中断时间

softirq(92) 从系统启动开始累计到当前时刻,软中断时间

steal(0)

guest(0)

guest_nice(0)

总的CPU时间=user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice

/proc/xxx/stat

包含了某一进程的所有活动信息,该文件都是从进程启动开始累计到当前时刻。

proc下pid节点信息在fs/proc/base.c中定义:

static const struct pid_entry tid_base_stuff[] = {

    INF("cmdline",   S_IRUGO, proc_pid_cmdline),
    ONE("stat",      S_IRUGO, proc_tid_stat),
    ONE("statm",     S_IRUGO, proc_pid_statm),

}

proc_tid_stat->do_task_stat,这里面有打印这个节点的代码。和读取的信息对照一下,可以知道每个指的含义。

1(pid) (init) S(state) 0 1 1 0 -1 4194560 92 111177 6 7 0(utime) 145(stime) 326(cutime) 623(cstime) 20(priority) 0(nice) 1 0 3(start_time) 1167360 91 4294967295 32768 461096 3198336768 31983 36184 3069464568 0 0 0 543239 3221332596 0 0 17 0 0(rt_priority) 0(policy) 0 0 0 495616 497086 503808

pid:6873 进程号

stae:

"R (running)",        /*   0 */
"S (sleeping)",        /*   1 */
"D (disk sleep)",    /*   2 */
"T (stopped)",        /*   4 */
"t (tracing stop)",    /*   8 */
"Z (zombie)",        /*  16 */
"X (dead)",        /*  32 */
"x (dead)",        /*  64 */
"K (wakekill)",        /* 128 */
"W (waking)",        /* 256 */

utime:0该任务在用户态运行的时间,单位为jiffies

stime:145该任务在核心态运行的时间,单位为jiffies

cutime:326所有已死线程在用户态运行的时间,单位为jiffies

cstime:623 所有已死在核心态运行的时间,单位为jiffies

待研究

    • 使用taskstats.log,需要内核使能taskstats,通过CONFIG_TASKSTATS来打开。

posted on 2017-05-16 15:29  ArnoldLu  阅读(195)  评论(0编辑  收藏  举报

导航