linux time 命令詳解

G8bao7發表於2015-06-16
from:http://blog.chinaunix.net/uid-26557245-id-3782974.html


用途說明
time命令常用於測量一個命令的執行時間,注意不是用來顯示和修改系統時間的(這是date命令乾的事情)。但是今天我透過檢視time命令的手冊頁,發現它能做的不僅僅是測量執行時間,還可以測量記憶體、I/O等的使用情況,手冊頁上的說法是time a simple command or give resource usage,其中time一詞我認為它應該是測量或測定的意思,並不單指時間。一個程式在執行時使用的系統資源通常包括CPU、Memory和I/O等,其中CPU資源的統計包括實際使用時間(real time)、使用者態使用時間(the process spent in user mode)、核心態使用時間(the process spent in kernel mode)。但是簡單的使用time命令並不能得到記憶體和I/O的統計資料,請看後文慢慢道來。
常用引數
time命令最常用的使用方式就是在其後面直接跟上命令和引數:
time []
在命令執行完成之後就會列印出CPU的使用情況:
real    0m5.064s      <== 實際使用時間(real time)
user    0m0.020s     <== 使用者態使用時間(the process spent in user mode)
sys     0m0.040s      <== 核心態使用時間(the process spent in kernel mode)
time命令跟上-p引數可以只列印時間數值(秒數),不列印單位。
使用示例
示例一 統計執行時間
[root@web186 root]# time find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh

real    0m14.837s
user    0m0.030s
sys     0m0.120s
[root@web186 root]#
注:real遠大於user加上sys,因為find需要遍歷各個目錄,需要大量的I/O操作,而磁碟I/O通常是最慢的環節,因此大部分時間find程式都在等待磁碟I/O完成。
[root@web186 root]# time find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh

real    0m0.230s
user    0m0.040s
sys     0m0.030s
注:再次執行的時候,發現real time變得很小了,應該是作業系統將剛才操作過的一些檔案快取了的緣故,因而大大減少了磁碟I/O。
[root@web186 root]# time -p find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh
real 0.15
user 0.04
sys 0.03
注:使用-p引數時,直接列印所需時間的數值,單位為秒,而不是更友好的格式,包括分鐘、秒鐘的顯示方式。
[root@web186 root]#
示例二 Linux系統中time命令其實不止一個
看過手冊頁的朋友,會發現有個-f引數可以來指定統計資訊的輸出格式,我們也來試一下。
[root@web186 root]# time -f "real %f\nuser %f\nsys %f\n" find . -name "mysql.sh" 
-bash: -f: command not found

real    0m0.024s
user    0m0.000s
sys     0m0.000s
怪哉,不靈啊。使用type -a來看一下。使用這個shell內建命令經常會有意想不到的發現。
[root@web186 root]# type -a time
time is a shell keyword
time is /usr/bin/time
注:透過這條命令我們可以發現我們常用的time其實是一個Shell關鍵字,還有一個外部命令/usr/bin/time,它有何不同呢?
[root@web186 root]# /usr/bin/time
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]
      [--portability] [--format=format] [--output=file] [--version]
      [--help] command [arg...]
注:外部命令/usr/bin/time功能更強大,下面來嘗試一下。
[root@web186 root]# /usr/bin/time find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh
0.03user 0.04system 0:00.12elapsed 55%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (154major+63minor)pagefaults 0swaps
注:注意後面兩行,列印了很多資訊,但看不太清楚。它有一個引數-v,可以列印得更清楚些。
[root@web186 root]# /usr/bin/time -v find . -name "mysql.sh"
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh
       Command being timed: "find . -name mysql.sh"
       User time (seconds): 0.03
       System time (seconds): 0.05
       Percent of CPU this job got: 47%
       Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.17
       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): 0
       Average resident set size (kbytes): 0
       Major (requiring I/O) page faults: 153
       Minor (reclaiming a frame) page faults: 64
       Voluntary context switches: 0
       Involuntary context switches: 0
       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
[root@web186 root]#
嘗試完這個之後,我看了一下Google搜尋的結果,發現有位大蝦早已發現了這個秘密,見相關資料【1】。
示例三 解決time命令輸出資訊的重定向問題
time命令的輸出資訊是列印在標準錯誤輸出上的, 我們透過一個簡單的嘗試來驗證一下。
[root@web186 root]# time find . -name "mysql.sh" >1.txt

real    0m0.081s
user    0m0.060s
sys     0m0.020s
[root@web186 root]# time find . -name "mysql.sh" 2>2.txt
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh

real    0m0.068s
user    0m0.040s
sys     0m0.030s
透過上面的嘗試,發現無法將time的輸出資訊重定向到檔案裡面,為什麼?因為time是shell的關鍵字,shell做了特殊處理,它會把time命令後面的命令列作為一個整體來進行處理,在重定向時,實際上是針對後面的命令來的,time命令本身的輸出並不會被重定向的。那現在怎麼辦呢?網上提供了兩種解決方法【2,3】,我們一一嘗試一下。
第一種解決方法,就是將time命令和將要執行的命令列放到一個shell程式碼塊中,也就是一對大括號中,要注意空格和分號的使用。
[root@web186 root]# {time find . -name "mysql.sh"} 2>2.txt
好像成功了。慢,看一下對不對。
[root@web186 root]# cat 2.txt
-bash: {time: command not found
原來bash把 {time 作為一個整體來處理了,前後都加上空格試試。
[root@web186 root]# { time find . -name "mysql.sh" } 2>2.txt
> Ctrl+C
這次Bash認為命令都沒有輸入完成,少了分號。因為Bash認為後面的 } 是find命令的引數。
[root@web186 root]# { time find . -name "mysql.sh"; } 2>2.txt
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh
[root@web186 root]# cat 2.txt

real    0m0.068s
user    0m0.030s
sys     0m0.040s
第一種方式的嘗試成功了,總結起來就是 { time command-line; } 2>file  注意分隔符的使用。
另外一種方式就是使用子Shell的方式,如下所示:
[root@web186 root]# (time find . -name "mysql.sh") 2>2.txt
./work186/sms/bin/mysql.sh
./work186/sms/src/scripts/mysql.sh
./work186/sms/src/scripts1/mysql.sh
./work186/sms1/bin/mysql.sh
./work186/sms1/src/scripts/mysql.sh
./temp/sms/bin/mysql.sh
./temp/sms/src/scripts/mysql.sh
[root@web186 root]# cat 2.txt

real    0m0.083s
user    0m0.040s
sys     0m0.020s
[root@web186 root]#
第二種方式的嘗試也成功了,總結起來就是 (time command-line) 2>file 這裡time緊貼著小括號(也可以的,命令列結束也不必帶分號。當然最好還是用第一種方式,畢竟啟動一個子shell是要多佔些資源的。
問題思考
1. 為什麼執行find命令的多次時間統計差別很大,一次實際時間需要12秒,另外幾次卻不足1秒?
相關資料
【1】孵夢森林 Linux中的兩個time
【2】Nine Rivers 重定向 Bash “time” 命令的輸出
【3】seizeF的專欄 重定向Bash命令——time
http://codingstandards.javaeye.com/blog/798788

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26250550/viewspace-1701149/,如需轉載,請註明出處,否則將追究法律責任。