我的站点

一个系统软件工程师的随手涂鸦

Tag: Linux (Page 1 of 19)

time和/usr/bin/time

当我在bash中敲入time命令时,运行的其实是bash内置的time命令:

$ time

real    0m0.000s
user    0m0.000s
sys     0m0.000s
$ type time
time is a shell keyword

这个time命令有一个-p选项,表示以posix格式输出:

$ time -p
real 0.00
user 0.00
sys 0.00

除此以外,还有一个time命令。不过我当前的机器并没有安装这个程序:

$ which time
which: no time in (/home/xiaonan/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/opt/cuda/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl)

安装一下,对比bash内置的time命令:

$ sudo pacman -S time
$ type time
time is a shell keyword
$ which time
/usr/bin/time

单独运行“/usr/bin/time -p”,只会输出命令的帮助选项:

$ /usr/bin/time -p
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]
       [--portability] [--format=format] [--output=file] [--version]
       [--help] command [arg...]

需要加上具体的需要度量时间的命令:

$ /usr/bin/time -p echo

real 0.00
user 0.00
sys 0.00

此外也可以给出命令执行的详细信息:

$ /usr/bin/time -v echo

    Command being timed: "echo"
    User time (seconds): 0.00
    System time (seconds): 0.00
    Percent of CPU this job got: 0%
    Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
    Average shared text size (kbytes): 0
    Average unshared data size (kbytes): 0
    Average stack size (kbytes): 0
    Average total size (kbytes): 0
    Maximum resident set size (kbytes): 1536
    Average resident set size (kbytes): 0
    Major (requiring I/O) page faults: 0
    Minor (reclaiming a frame) page faults: 70
    Voluntary context switches: 1
    Involuntary context switches: 1
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size (bytes): 4096
    Exit status: 0

参考资料:
/usr/bin/time: not the command you think you know

Linux系统查看可用内存

http://www.linuxatemyram.com/提到使用free命令查看Linux系统使用内存时,used一项会把当前cache的大小也会加进去,这样会造成free这一栏显示的内存特别少:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:           1504        1491          13           0         855      869
Swap:          2047           6        2041

可是实际上,cache根据应用程序的需要是可以回收利用的,因此free这一栏并不能真实地表现有多少“可以使用”的内存。实际系统可用内存应该以available数据为准。

linuxatemyram所提到的free命令也许是比较老的版本,我尝试了RHEL 7.2Ubuntu 16.04Arch Linux3Linux发行版,均没有出现used包含cache的情况:

$ free -m
              total        used        free      shared  buff/cache   available
Mem:          64325       47437        3150        1860       13737       14373

另外,从man free命令中也可以得到,目前计算used的值是要减掉freebuff/cache的:

used Used memory (calculated as total – free – buffers – cache)

可以使用-w命令行选项得到buffcache各自使用的数量:

$ free -wm
              total        used        free      shared     buffers       cache   available
Mem:          64325       48287        2476        1859        1430       12131       13524

需要注意的是,free表示的是当前完全没有被程序使用的内存;而cache在有需要时,是可以被释放出来以供其它进程使用的(当然,并不是所有cache都可以释放,比如当前被用作ramfs的内存)。而available才真正表明系统目前可以提供给应用程序使用的内存。/proc/meminfo3.14内核版本开始提供MemAvailable的值;在2.6.27~3.14版本之间,是free程序自己计算available的值;早于2.6.27版本,available的值则同free一样。

参考资料:
Understanding output of free in Ubuntu 16.04
How can I get the amount of available memory portably across distributions?

与*NIX有关的杂志

本文介绍一些我接触过的与*nix有关的杂志。

首先要提到的就是Linux Journal(官方网址:http://www.linuxjournal.com/)。根据Wikipedia的介绍,这应该是最早的一本介绍Linux的杂志:

Linux Journal was the first magazine to be published about the Linux kernel and operating systems based on it. It was established in 1994.

不过Linux Journal现在不再发行纸质版了,只提供电子版。该杂志这段时间有一项促销活动:即截至到今年328日前,你只需花28.5美元(使用优惠码:2017ARCH可免10美元),就可以购买到从1994年到2016年杂志的电子合订版:

1

个人觉得还是很划算的,虽然有些文章已经年代久远,但是还是很有参考价值的。另外,Linux Journal也会将其文章发布到官网上供读者免费阅读。因次,是否愿意花钱买合订本或者订阅,就“仁者见仁,智者见智”了。

再说一下Linux Format(官方网址:http://www.linuxformat.com/)和Linux Voice(官方网址:https://www.linuxvoice.com/),二者都是英国出版的Linux杂志。不知是否因为Linux Voice的主创团队均出自Linux Format的缘故,二者有太多类似之处:都同时提供纸质版和电子版,都提供过刊的免费下载,等等。也许是由于版权原因,我在国内没见到过这两种杂志。在国外我阅读过纸质版,制作很精美,每期还附赠光盘。感兴趣的朋友可以下载它们提供的过刊了解一下。

以上提到的都是以Linux为主题的杂志,再介绍一个以BSD为主打内容的杂志:BSD magazine(官方网址:https://bsdmag.org/)。这是一本真正免费的杂志,订阅以后,你不需花一分钱,就会收到每一期。从这本杂志里,你可以获悉时下BSD家族的最新动态,虽然偶尔也会有Linux的内容出现。去年年底,这本杂志一度宣布要停刊了,不过目前又撤销了这个决定。我个人很希望这本杂志可以继续办下去。

最后,我很希望自己的国家能有一本中文版的Linux杂志。但是想想现在的情形,我们的时间都被其它的事物占去了,也许根本无法诞生这样一本杂志了。。。

后记:对于其它的类似杂志,比如:http://www.linux-magazine.com/。因为我没有一点了解,就不发表评论了。

如何选择Linux发行版?

@YeimMeMeMes 在其个人社交媒体主页上贴出了一张如何选择Linux发行版的图(原图链接:https://pbs.twimg.com/media/C3PoAagWMAAe5YT.jpg:large):

C3PoAagWMAAe5YT

因为这幅图上所列举的大部分Linux发行版我都没怎么使用过,所以我没有资格对这张图是否合理发表评论,仅供读者参考。下面我仅对我曾经使用过的一些Linux发行版谈些感受:

(1)这个图上并没有出现一些Linux的商业版本,譬如,RedHatRed Hat Enterprise LinuxSuSESUSE Linux Enterprise Server,等等。我以前做过这两个Linux商业版本上的测试工作,感觉还是很稳定的。给我一个很深的印象就是在Red Hat Enterprise Linux上编译和安装最新版本的Linux内核总是非常顺利,而在其它发行版上有时会遇到一些莫名其妙的问题。从去年开始,Red Hat Enterprise Linux对开发者已经免费了(可参考这篇文章:As in beer: Red Hat offers RHEL free to developers),有兴趣的朋友可以体验一下。

(2)最近两个月,在工作中我主要使用Arch LinuxArch Linux在软件包更新方面非常及时。有时你发现当前软件包不是最新的,只要在其网站上提一个请求,很快就会有维护者响应。如果你总是希望可以使用上最新的软件版本,不妨尝试一下Arch Linux

Linux系统上如何查看进程(线程)所运行的CPU

本文介绍如何在Linux系统上查看某个进程(线程)所运行的CPU,但在此之前我们需要弄清楚两个基本概念:

(1)Linux操作系统上的进程和线程没有本质区别,在内核看来都是一个task。属于同一个进程的各个线程共享某些资源,每一个线程都有一个ID,而“主线程”的线程ID同进程ID,也就是我们常说的PID是一样的。

(2)使用lscpu命令,可以得到当前系统CPU的数量:

$ lscpu
......
CPU(s):                24
On-line CPU(s) list:   0-23
Thread(s) per core:    2
Core(s) per socket:    6
Socket(s):             2
......

系统有2个物理CPUSocket(s): 2),每个CPU6coreCore(s) per socket: 6),而每个core又有2hardware threadThread(s) per core: 2)。所以整个系统上一共有2X6X2=24CPU(s):24)个逻辑CPU,也就是实际运行程序的CPU

使用htop命令可以得到进程(线程)所运行的CPU信息,但是htop默认情况下不会显示这一信息:

1
开启方法如下:
(1)启动htop后,按F2Setup):

2
(2) Setup中选择Columns,然后在Available Columns中选择PROCESSOR - ID of the CPU the process last executed, 接下来按F5Add)和F10Done)即可:

3

现在htop就会显示CPU的相关信息了。需要注意的是,其实htop显示的只是“进程(线程)之前所运行的CPU”,而不是“进程(线程)当前所运行的CPU”,因为有可能在htop显示的同时,操作系统已经把进程(线程)调度到其它CPU上运行了。

下面是一个运行时会包含4个线程的程序:

#include <omp.h>

int main(void){

        #pragma omp parallel num_threads(4)
        for(;;)
        {
        }

        return 0;
}

编译并运行代码:

$ gcc -fopenmp thread.c
$ ./a.out &
[1] 17235

使用htop命令可以得到各个线程ID,以及在哪个CPU上运行:

4

参考资料:
How to find out which CPU core a process is running on
闲侃CPU(一)

Linux线程模型浅析

Linux的线程是“轻量级进程”(Light-Weight Process,即LWP)。在Linux系统上运行一个程序时,操作系统会为这个程序创建一个进程,其实也就是“主线程”,后续则可以产生出更多的线程。每个进程都有一个PIDProcess ID),每个线程也会有一个TIDThread ID),属于同一进程的线程各自有拥有不同的TID,但它们的PID是相同的,都等于“主线程”的TID。因此从本质上来讲,Linux系统下的进程和线程没有区别,只不过同一进程中的线程可以共享某些资源。下面看一个例子:

#include <unistd.h>
#include <omp.h>

int main(void){

        #pragma omp parallel num_threads(4)
        for(;;)
        {
            sleep(1);
        }

        return 0;
}

编译并在后台运行这个程序:

$ gcc -fopenmp threads.c
$ ./a.out &
[1] 9802

进程的PID9802,用ps -T pid命令查看进程的线程信息:

$ ps -T 9802
  PID  SPID TTY      STAT   TIME COMMAND
 9802  9802 pts/1    Sl     0:00 ./a.out
 9802  9803 pts/1    Sl     0:00 ./a.out
 9802  9804 pts/1    Sl     0:00 ./a.out
 9802  9805 pts/1    Sl     0:00 ./a.out

其中SPID即为TID。可以看到当前进程的PID9802,共包含4个线程,其TID依次为:9802980398049805,其中PIDSPID相同的线程即为主线程。

Unix之殇

前几天网上出现了Solaris项目将会被Oracle停掉的谣言。尽管消息一直未被证实,但是以Solaris为代表的传统Unix操作系统的没落却是不争的事实。在上个月,top500发布的目前世界上运行最快的500台超级计算机中,有498台运行的是Linux

capture

由此可见,LinuxUnix目前的境遇可谓是天壤之别。

我不知道究竟是什么原因造成了目前Linux一统天下的局面,但是可以确定的是一定不是技术领域方面的原因。我没有为IBM公司工作过,也从未接触过AIX操作系统,所以对AIX没有发言权。而对于BSD系列操作系统(FreeBSDOpenBSDNetBSD等等),仅仅限于安装和使用过,并没有什么太深的体会。我为HP/HPE公司效力过,虽然并没有使用过HP-UX,但是周围有很多同事以前是做HP-UX相关工作的:开发新功能,做Unix认证等等。听他们讲,HP-UX非常稳定,很多电信,银行等对稳定性要求特别高的环境仍然在使用着HP-UX,也许这些企业慢慢地会转向Linux?我不知道。。。至于Solaris,我曾经在上面做过4年多的全职开发。Solaris上面有很多很cool的工具供用户使用,比如mdb,比如DTrace,这些工具为我工作提供了巨大的帮助,极大地满足了一个底层软件工程师的好奇心。此外Solaris也是以运行稳定而著称,比如这台已经连续运行了10年的装有Solaris的机器(图片出处:https://pbs.twimg.com/media/CjtxiOmWYAA5lHB.jpg):

cjtxiomwyaa5lhb

再来看看Linux,其实一直以来,Linux系统上并没有可以匹敌DTrace的系统tracing工具,直至最近BPF功能的成熟,可以说在tracing领域落后了Solaris整整12年(可以参考这篇文章:Linux in 2016 catches up to Solaris from 2004);再比如目前Ubuntu发行版中引入的ZFS文件系统,也是出自Solaris。所以,其实如果单单从技术领域来看,Linux不仅不见得做的比Unix好,某些方面甚至还是处于下风的。

在上面提到的几种Unix中,除了BSD系列,其余3种可以说都是某个传统硬件服务器厂商的私有操作系统。虽然曾经有OpenSolaris这个开源产品,但是也仅仅是昙花一现(个人觉得OpenSolaris最大的意义在于由其衍生出了illumos内核,以及基于illumos内核的类Solaris系统。比如smartos。)。所以说,是不是由于最近这些年互联网的日渐强势,硬件厂商的效益江河日下,而“城门失火,殃及池鱼”,随之而来的就是这些Unix也会受到不小的冲击呢?个人觉得应该有一定关系吧。但如果仅仅把Linux成功的原因归结于“开源”,似乎也有失偏颇,BSD系列操作系统也是开源的,且其在license上更为宽松(参考这里:Comparing BSD and Linux)。所以说对于Linux目前具有如此统治力的原因,真的是很难说清。

相信目前很多的中小公司都完全转向Linux了。最直白的原因:人好招。你见过多少招聘信息要求熟悉FreeBSD?肯定没有要求Linux的多。至于要求熟悉NetBSD的?也许有,但是我是没见过。所以对目前Unix人才的需要还是主要在大公司,也只有大公司有意愿和实力做这些“日渐小众”的Unix的相关工作。例如,Brendan Gregg在其社交账号中为对Solaris工程师提到Netflix目前使用FreeBSD

capture2

我很怀念十几年前各大操作系统“百花齐放”的时代,这样想并不是因为我对Linux有任何成见,只是当你的服务器都运行着清一色的Linux操作系统时,实在是觉得有些单调和乏味,就像现在人类使用的手机也基本可以分为两大阵营:iOSAndroid(又是Linux)。世界本应该就是多样化的,丰富多彩的,所以希望其它的Unix有朝一日可以“复兴”吧。。。

HP/HPE公司的*nix操作系统

HP/HPE公司(即通常说的惠普公司,因其在2015年已经拆分成HPHPE两家独立运营公司,且拆分后是由HPE延续操作系统的相关工作,所以在这里使用HP/HPE。)拥有自己的Unix操作系统:HP-UX。以前中国是有团队参与HP-UX的相关工作:功能开发,Unix认证等等,现在相应的工作应该都转到印度了。目前HP-UX应该在一些银行,电信系统还在使用,不过的确是很难见到了。可以通过Wikipedia来了解HP-UX的一些信息。

再来说一下Linux,其实以前HP/HPE公司有一个很大的Linux团队,其甚至有能力做出自己的Linux发行版:

img_20161123_140305_hdr

此外,这个团队也曾经是Linux kernel的一个很重要的贡献者。不过,随着这些年公司的战略调整,这个团队的绝大部分工程师都已经离开了,其中的很多人加盟了其它公司,继续为Linux贡献着力量。目前HP/HPELinux上的工作重心侧重在同Linux厂商的合作,譬如今年与SuSE的合作(详情请参考Sweet SUSE! HPE snags itself a Linux distro)。

Linux操作系统的pstack工具

Solaris操作系统提供了pstack工具,用来打印运行程序的线程堆栈信息。RedHat公司发行的Linux操作系统(RHELCentOS等等)也提供了pstack工具,只要安装gdb

# yum install gdb

就会把pstack也一并安装成功。

首先看一下pstack

# which pstack
/usr/bin/pstack
# ls -lt /usr/bin/pstack
lrwxrwxrwx. 1 root root 6 Nov 19 06:32 /usr/bin/pstack -> gstack

可以看出pstack实际上只是一个指向了gstack的符号链接。再看一下gstack

# cat /usr/bin/gstack
#!/bin/sh

if test $# -ne 1; then
    echo "Usage: `basename $0 .sh` <process-id>" 1>&2
    exit 1
fi

if test ! -r /proc/$1; then
    echo "Process $1 not found." 1>&2
    exit 1
fi

# GDB doesn't allow "thread apply all bt" when the process isn't
# threaded; need to peek at the process to determine if that or the
# simpler "bt" should be used.

backtrace="bt"
if test -d /proc/$1/task ; then
    # Newer kernel; has a task/ directory.
    if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
    backtrace="thread apply all bt"
    fi
elif test -f /proc/$1/maps ; then
    # Older kernel; go by it loading libpthread.
    if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
    backtrace="thread apply all bt"
    fi
fi

GDB=${GDB:-/usr/bin/gdb}

# Run GDB, strip out unwanted noise.
# --readnever is no longer used since .gdb_index is now in use.
$GDB --quiet -nx $GDBARGS /proc/$1/exe $1 <<EOF 2>&1 |
set width 0
set height 0
set pagination no
$backtrace
EOF
/bin/sed -n \
    -e 's/^\((gdb) \)*//' \
    -e '/^#/p' \
    -e '/^Thread/p'

可以看到gstack仅仅是一个shell脚本。简单浏览一下这个脚本:

(1)

if test $# -ne 1; then
    echo "Usage: `basename $0 .sh` <process-id>" 1>&2
    exit 1
fi

脚本要求一个参数:进程ID

(2)

if test ! -r /proc/$1; then
    echo "Process $1 not found." 1>&2
    exit 1
fi

通过检测/proc目录下进程子目录是否可读,来查看相应进程是否存在。

(3)

# GDB doesn't allow "thread apply all bt" when the process isn't
# threaded; need to peek at the process to determine if that or the
# simpler "bt" should be used.

backtrace="bt"
if test -d /proc/$1/task ; then
    # Newer kernel; has a task/ directory.
    if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
    backtrace="thread apply all bt"
    fi
elif test -f /proc/$1/maps ; then
    # Older kernel; go by it loading libpthread.
    if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
    backtrace="thread apply all bt"
    fi
fi

如果进程只有一个线程,那么使用gdb的“bt”命令打印线程堆栈信息,否则使用“thread apply all bt”命令。

(4)

GDB=${GDB:-/usr/bin/gdb}

# Run GDB, strip out unwanted noise.
# --readnever is no longer used since .gdb_index is now in use.
$GDB --quiet -nx $GDBARGS /proc/$1/exe $1 <<EOF 2>&1 |
set width 0
set height 0
set pagination no
$backtrace
EOF
/bin/sed -n \
    -e 's/^\((gdb) \)*//' \
    -e '/^#/p' \
    -e '/^Thread/p'

最后调用gdb,使用“bt”或“thread apply all bt”命令,并把输出重定向到sed工具,由sed工具打印出线程堆栈信息。

最后看一个使用pstack的例子:

# pstack 707
Thread 3 (Thread 0x7f69600d8700 (LWP 713)):
#0  0x00007f6968af269d in poll () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007f6969027a84 in g_main_context_iterate.isra.24 () from /lib64/libglib-2.0.so.0
#2  0x00007f6969027bac in g_main_context_iteration () from /lib64/libglib-2.0.so.0
#3  0x00007f6969027be9 in glib_worker_main () from /lib64/libglib-2.0.so.0
#4  0x00007f696904d4f5 in g_thread_proxy () from /lib64/libglib-2.0.so.0
#5  0x00007f696af9fdc5 in start_thread (arg=0x7f69600d8700) at pthread_create.c:308
#6  0x00007f6968afcced in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
Thread 2 (Thread 0x7f695eec3700 (LWP 716)):
#0  0x00007f6968af269d in poll () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007f6969027a84 in g_main_context_iterate.isra.24 () from /lib64/libglib-2.0.so.0
#2  0x00007f6969027dca in g_main_loop_run () from /lib64/libglib-2.0.so.0
#3  0x00007f6969641336 in gdbus_shared_thread_func () from /lib64/libgio-2.0.so.0
#4  0x00007f696904d4f5 in g_thread_proxy () from /lib64/libglib-2.0.so.0
#5  0x00007f696af9fdc5 in start_thread (arg=0x7f695eec3700) at pthread_create.c:308
#6  0x00007f6968afcced in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
Thread 1 (Thread 0x7f696c5738c0 (LWP 707)):
#0  0x00007f6968af269d in poll () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007f6969027a84 in g_main_context_iterate.isra.24 () from /lib64/libglib-2.0.so.0
#2  0x00007f6969027dca in g_main_loop_run () from /lib64/libglib-2.0.so.0
#3  0x0000560a080a80a3 in main ()

如果使用的Linux发行版没有pstack这个工具,可以考虑直接把gstack脚本拷贝过去。

strace命令介绍

straceLinux上的一个很好用的工具,它可以用来输出程序在运行过程中发生的系统调用以及收到的信号的相关信息,因此在调试和诊断问题时有很大的帮助,特别是在程序没有源码,或是在前期做一些粗略的分析时。strace命令格式如下:

strace [options] command [args]

举个例子:

# strace sleep 300
execve("/usr/bin/sleep", ["sleep", "300"], [/* 24 vars */]) = 0
brk(0)                                  = 0x22fa000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70d1ef8000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
......
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=20243, si_uid=0} ---
......

从上面例子可以看出,对于系统调用,比如openaccessstrace都会输出详细的参数和返回值,如果发生了错误,也会输出细致的错误信息。而对于接收到的信号,除了输出信息外,还要注意信号信息的前后都加了“---”,以示与系统调用的区别。

以下是一些常用的选项:
(1)-o:把strace执行结果输出到指定文件里:

# strace -o out ls

(2)-t:打印时间:

# strace -t ls
10:30:07 execve("/usr/bin/ls", ["ls"], [/* 24 vars */]) = 0
10:30:07 brk(0)
......

(3)-e:只关注某一系统调用:

# strace -e open ls
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
......

(4)-y:显示和文件描述符关联的文件路径:

# strace -y ls
......
fstat(3</etc/ld.so.cache>, {st_mode=S_IFREG|0644, st_size=32951, ...}) = 0
mmap(NULL, 32951, PROT_READ, MAP_PRIVATE, 3</etc/ld.so.cache>, 0) = 0x7fba3db13000
close(3</etc/ld.so.cache>)              = 0
......

(5)-f:追踪运行进程所生成的子进程。

参考资料:
strace(1) – Linux man page
A swiss army knife of debugging tools

Page 1 of 19

Powered by WordPress & Theme by Anders Norén