2009年6月14日 星期日

[Linux] 基本的Timer介紹

Linux提供了兩種基本的Timer機制可以使用:
1.alarm
2.setitimer

● alarm
#include
unsigned int alarm(unsigned int seconds);
這是一個簡單的設定Timer介面。當呼叫了alarm( n )後,等待n秒後,就會觸發一次的SIGALRM的signal,所以必須要在呼叫alarm前,先設好SIGALRM的handler function才行。而當乎呼alarm(0)時,則表示停止當前的timer處理,不要發出SIGALRM的signal。

Return value : 返回上一次呼叫alarm的剩餘秒數。
若未設定alarm,則返回0。

● setitimer

#include
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value,struct itimerval *ovalue);

setitimer與getitimer提供了三種類別的Timer使用:

ITIMER_REAL : 以系統真實的時間來計算,觸發時會送出SIGALRM。

ITIMER_VIRTUAL : 只計算process真正在執行的時間(在User Mode的處理),觸發時會送出SIGVTALRM。

ITIMER_PROF : 計算該process在User Mode與Kernel Mode的處理時間,觸發時送出SIGPROF。

透過第一個參數which指定要使用哪一種Timer (ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF )。setitimer是用來設定該種Timer的觸發時間為多少。getitimer則是取得上一次Timer設定的時間。設定的內容是一個系統內建的struct itimerval

setitimer由第二個參數value設定觸發的時間。第三個參數ovalue用來取得上一次 setitimer設定的itimerval值(此參數可以為NULL)。
值得注意的是,根據itimerval裡變數的意義,當it_interval設定為0時,Timer只會觸發一次。而it_value設定為0時,代表Timer結束。
Return value : 如果成功則return 0,失敗則return -1。

Example : 第一次等待1秒後觸發Timer,之後每隔2秒觸發一次


#include
#include using namespace std;
void my_alarm_handler(int a)
{
cerr<<"test "<}
int main()
{
struct itimerval t;
t.it_interval.tv_usec = 0;
t.it_interval.tv_sec = 2;
t.it_value.tv_usec = 0;
t.it_value.tv_sec = 1;
if( setitimer( ITIMER_REAL, &t, NULL) <> }
signal( SIGALRM, my_alarm_handler );
while(1)
{
sleep(2);
}
return 0;}

● 根據以上,可知Linux內建的Timer還是有點簡陋,而且setitimer同一時間只能處理3個Timer,如果應用程式需要多個Timer的話,這個Linux內建的Timer可能就不敷需求了!

deadlock

1. Deadlock: System中存在一些processes互相等待彼此所擁有的resources,即形成circular waiting,造成所有processes皆無法繼續執行(infinite blocking),使得CPU utilization與system throughput大幅降低。
2. Process使用resources的三個步驟
􀁺 Request: 可透過wait() semaphore實作
􀁺 Use
􀁺 Release: 可透過signal() semaphore實作
3. Deadlock v.s. Starvation
􀁺 Starvation只限於單一(或某些/少數) process因長期拿不到resource而形成自身停滯的現象,但其他processes仍可正常執行;且CPU utilization與system throughput未必會降低。
􀁺 兩者皆為不公平的resource allocation的結果
􀁺 兩者通常在preemptive環境下較顯著
4. 形成deadlock的四個必要條件(缺一不可) (deadlock ⇒ 4 conditions)
􀁺 Mutual Exclusion: 至少有一個resource是non-sharable的狀態
􀁺 Hold and Wait: 某個process必定hold住部份的resources、且等待其他processes所hold住的resources
􀁺 No Preemption: 某些resources是non-preemptible;processes不能搶奪其他processes的resources,必須等到其他processes自動release這些resources,才能使用這些resources。
􀁺 Circular Wait: 存在一組processes {P0, P1, ..., Pn},其中P0在等P1、P1在等P2、…、Pn在等P0。
􀁺 “Circular Wait” implies “Hold and Wait”.
5. Resource Allocation Graph, G = (V, E)
􀁺 Vertex set V = {process P, resource types R}
􀁺 Edge set E = {request edge (Pi → Rj), assignment edge (Ri → Pj)}
􀁺 Graph中,若沒有存在cycle,則不會產生deadlock。
􀁺 Graph中有cycle,未必造成deadlock
􀂕 每個resource type為multiple instances,則未必有deadlock
􀂕 每個reosource type皆是signle instance,則cycle ⇔ deadlock1

Process

工作程序-Process

什麼是 Process?簡單的說就是在系統中執行的一些程式。不論是因為系統運行過程所啟動的程式,或是使用者在登入後自行執行的程式、或是常駐執行、或是短暫執行,這些程式在系統中執行時,都會佔用系統的資源(CPU、記憶體等),此時系統就會替程式安排一個程序(Process),以便控管資源的使用!

因此,在系統中執行或運作的程式,我們可以稱之為 Process。誠如上述所言,當程式執行時會產生一個 Process,系統則在此時會配給每個 Process 一個辨識碼,這個辨識碼就是 Process ID 簡稱 PID。而分配的 Process ID 是不會重複的,當程式結束或終止後,PID 就會消失系統也會拋棄不再使用,等到下次重新啟動系統後再從頭計數。有些程序(Process)在執行時,會呼叫或啟用其它的程序,這些被呼叫的程序可稱為子程序,而最先被執行的程序就被當成父程序(Parent Process)。

子程序也有一個 PID,同時也會註記父程序的 PID,稱之為 PPID。整個系統最初的父程序是 init 這支程式,不論程序呼叫了幾層的子程序,往前推算最終還是到了 init 這個程式所產生的程序,而 init 程序的 PID 為 0。

內核診斷工具 SystemTap

內核診斷工具 SystemTap
在 SystemTap 出現之前,對于 Linux 程序員或者系統管理員而言,調試內核往往是一場噩夢。
例如,你懷疑傳遞給系統調用 read 的參數 fd 出了問題,想把它打印出來,你需要做的是︰
首先得到一份內核源碼,找到 sys_read() 的函數體中插入 printk() 語句,接下來重新編譯內核,然后用新的內核重新啟動系統。
謝天謝地,你總算看到了你想要看到的東西,不過你馬上會發現遇到了一個新的麻煩︰
除非重新啟動系 統到原來的內核,printk() 會無休止地打印下去。
SystemTap 的目的就是要把人們從這種泥潭中解救出來。
SystemTap 提供了一個簡單的命令行界面和強大的腳本語言,同時預定義了豐富的腳本庫。
基于內核中的 kprobe,SystemTap允許你自由地從營運中的內核無害地收集調試訊息和性能數據,來用于之后的分析和處理。
你可以隨時開始或者停止這種收集過程,而無需漫長的修改代碼、編譯內核和重啟系統的悲慘循環。
SystemTap 使得上面的問題變得簡單了,簡單得只需要一條命令就可以做到︰
stap -e 'probe syscall.read { printf("fd = %d\n",fd) }'
SystemTap的功能和Sun的DTrace和IBM的dprobe工具相似。
但是和它們不同的是, SystemTap 是遵循GPL的開源軟體項目。
它的出現使得Linux社群也擁有了功能強大而且易于使用的動態調試內核工具。
目前,SystemTap 的主要開發成員來自于RedHat、IBM、Intel 和 Hitachi。
安裝SystemTap
  在安裝SystemTap之前,需要確保系統中已經安裝了其它兩個套裝軟件︰
kernel-debuginfo RPM︰SystemTap需要透過內核調試訊息來定位內核函數和變量的位置。
對於通常的發行版,并沒有安裝 kernel-debuginfo RPM,我們可以到發行版的下載站下載。
對于 Fedora Core 6,這個位址是︰ http://download.fedora.redhat.com/pub/fedora/linux/core/6/i386/debug/
編譯 kernel
例如 linux-2.6.18.5
make menuconfig 需選 CONFIG_KPROBES, CONFIG_DEBUG_INFO, CONFIG_RELAY, CONFIG_DEBUG_FS
make; make modules_install; make install
cd /usr/lib/debug/lib/modules/
ln -s /lib/modules/2.6.18.5/

cd 2.6.18.5
ln -s /usr/src/kernels/linux-2.6.18.5/vmlinux
elfutils RPM︰SystemTap需要elfutils套裝軟件提供的庫函數來分析調試訊息。
目前的SystemTap要求安裝elfutils-0.123以上版本。
目前最新的版本是0.124-0.1。
如果需要,我們可以從SystemTap的站點下載RPM或者源碼來升級。
下載位址是︰ ftp://sources.redhat.com/pub/SystemTap/elfutils/i386/
  接下來就可以安裝SystemTap了,這有透過RPM或者源碼安裝兩種模式︰
  1. 透過RPM安裝 Fedora Core 6缺省情況下已經安裝了systemtap。
如果沒有,也可以從如下的位址下載︰ http://download.fedora.redhat.com/pub/fedora/linux/core/updates/testing/6/i386/SystemTap-0.5.10-1.fc6.i386.rpm
  2.透過源碼安裝︰
  從SystemTap的FTP站點下載最新的源碼
  然后安裝如下︰
  /root > tar -jxf SystemTap-20061104.tar.bz2
  /root > cd src
  /root/src> ./configure
  /root/src> make
  /root/src> make install

  運行SystemTap
  營運SystemTap首先需要root權限。
  營運SystemTap有三種形式︰
  1. 從文件(通常以.stp作為文件名后綴)中讀入並營運腳本︰stap [選項] 文件名
  2. 從標準輸入中讀入並營運腳本︰ stap [選項] -
  3. 營運命令行中的腳本︰stap [選項] -e 腳本
  4. 直接營運腳本文件(需要可執行屬性並且第一行加上#!/usr/bin/stap)︰./腳本文件名使用"Ctrl+C"中止SystemTap的營運。
systemtap的選項還在不斷的擴展和更新中,其中最常用的選項包括︰
  -v -- 打印中間訊息
  -p NUM -- 營運完Pass Num后停止(缺省是營運到Pass 5)
  -k -- 營運結束后保留臨時文件不刪除
  -b -- 使用RelayFS文件系統來將數據從內核空間傳輸到用戶空間
  -M -- 僅當使用-b選項時有效,營運結束時不合併每個CPU的單獨數據文件
  -o FILE -- 輸出到文件,而不是輸出到標準輸出
  -c CMD -- 啟動探測后,營運CMD命令,直到命令結束后退出
  -g -- 採用guru模式,允許腳本中嵌入C語句
  其它更多選項請參看stap的手冊。
SystemTap的語法
我們利用一個簡單的systemtap腳本來介紹一下SystemTap的語法︰
#!/usr/local/bin/stap
global count
function report(stat) {
printf("stat=%d\n", stat)
}
probe kernel.function("sys_read") {
++count
}
probe end {
report()
}
探測點(probe)︰每 個systemtap腳本中至少需要定義一個探測點,也就是指定了在內核的什么位置進行探測。
探測點名稱后面緊跟的一組大括號內定義了每次內核營運到該探測點時需要營運的操作,這些操作完成后再返回探測點,繼續下面的指令。
這裡給出了systemtap目前支持的所有探測點類型。
全局變量(global)︰用來定義全局變量。
單個探測點函數體中使用的局部變量不需要預先定義,但是如果一個變量需要在多個探測點函數體中使用,則需要定義為全局變量。
函數(function)︰用來定義探測點函數體中需要用到的函數。
除了可以用腳本語言定義函數以外,還可以用C語言來定義函數,只是這時函數名后面的大括號對需要換成%{ %}。
例如,前面的report()函數可以寫成︰
  function report(stat) %{
  _stp_printf("stat=%d\n", THIS->stat);
 %}

SystemTap的例子
  了解了SystemTap的基本用法,下面讓我們來看幾個有趣的例子。
  統計當前系統中調用最多的前10個系統調用
  在進行性能分析的時候,我們常常需要知道那些函數調用次數最多,才能有的放矢地展開分析。
下面這個簡單的例子可以打印出在過去的5秒鐘裡調用次數最多的那些系統調用。
#!/usr/bin/env stap
#
# display the top 10 syscalls called in last 5 seconds
#
global syscalls
function print_top () {
cnt=0
log ("SYSCALL\t\t\t\tCOUNT")
foreach ([name] in syscalls-) {
printf("%-20s\t\t%5d\n",name, syscalls[name])
if (cnt++ == 10)
break
}
printf("--------------------------------------\n")
delete syscalls
}
probe syscall.* {
syscalls[probefunc()]++
}
probe timer.ms(5000) {
print_top ()
}
看誰在動我的文件
有時候,我們如果中了惡意的病毒軟體,會發現某些文件莫名其妙的被修改,下面這個例子可以幫你監視誰在修改你的文件。
 #!/usr/bin/env stap
 #
 # monitor who is messing my file of secrets
 #
 probe generic.fop.open {
if(filename == "secrets")
printf("%s is opening my file: %s\n", execname(), filename)
 }
SystemTap的基本原理
現下,大家已經熟悉了SystemTap的基本用法。
在結束之前,讓我們再來了解一下SystemTap的基本原理和工作流程以加深理解。
  可以看出,SystemTap營運的過程依次分為五個階段,通常稱為Pass 1 - Pass 5。
就像前面介紹用法的時候提到的,在命令行中加上-p NUM選項可以使得SystemTap在營運完Pass NUM之后停止,而不是營運到Pass 5。
這允許你分析SystemTap在每一個階段的輸出,對于調試腳本尤其有用。
下面來介紹每一個階段的主要功能︰
Pass 1 - parse︰這個階段主要是檢查輸入腳本是否存在語法錯誤,例如大括號是否匹配,變量定義是否規範等
Pass 2 - elaborate︰這個階段主要是對輸入腳本中定義的探測點或者用到的函數展開,不但需要綜合SystemTap的預定義腳本庫,還需要分析內核或者內核模塊的調試訊息
Pass 3 - translate: 在這個階段,將展開后的腳本轉換成C文件。前三個階段的功能類似于編譯器,將.stp文件編譯成為完整的.c文件,因此又被合起來稱為轉換器(translator)
Pass 4 - build︰在這個階段,將C源文件編譯成內核模塊,在這過程中還會用到SystemTap的營運時庫函數。
Pass 5 - run︰這個階段,將編譯好的內核模塊插入內核,開始進行數據收集和傳輸。

今日主要的OS

蘋果Mac OS

Mac OS是一套執行于蘋果Macintosh系列電腦上的作業系統。Mac OS是首個在商用領域成功的圖形使用者介面。Macintosh組包括比爾·阿特金森(Bill Atkinson)、傑夫·拉斯金(Jef Raskin)和安迪·赫茨菲爾德(Andy Hertzfeld)。現行的最新的系統版本是Mac OS X v10.5版。









Mac OS X 10.3
-----------------------------------------------------------------------
微軟Windows

Microsoft Windows 系列作業系統是在微軟給IBM機器設計的MS-DOS的基礎上設計的圖形作業系統。現在的Windows系統,如Windows 2000、Windows XP皆是建立於現代的Windows NT核心。NT核心是由OS/2OpenVMS等系統上借用來的。Windows 可以在32位元和64位元的IntelAMD的處理機上執行,但是早期的版本也可以在DEC AlphaMIPSPowerPC架構上執行。 雖然由於人們對於開放原始碼作業系統興趣的提升,Windows的市場佔有率有所下降,但是到2004年為止,Windows作業系統在世界範圍內佔據了桌面作業系統90%的市場。[4] Windows系統也被用在低階和中階伺服器上,並且支援網頁服務的資料庫服務等一些功能。最近微軟花費了很大研究與開發的經費用於使Windows擁有能執行企業的大型程式的能力。 Windows XP在2001年10月25日發布,2004年8月24日發布服務包 2,2008年4月21日發布最新的服務包 3。 微軟最新的作業系統 Windows Vista(開發代碼為Longhorn)於2007年1月30日發售[5]。Windows Vista增加了許多功能,尤其是系統的安全性和網路管理功能。Windows Vista擁有介面華麗的Aero Glass









Windows XP桌面
------------------------------------------------------------------------------------------
類Unix系統

一個在Linux底下執行的客製化KDE桌面系統 所謂的類Unix家族指的是一族種類繁多的OS,此族包含了System VBSDLinux。由於Unix是The Open Group的註冊商標,特指遵守此公司定義的行為的作業系統。而類Unix通常指的是比原先的Unix包含更多特徵的OS。 Unix系統可在非常多的處理器架構下執行,在伺服器系統上有很高的使用率,例如大專院校或專案應用的工作站自由軟體Unix變種,例如Linux與BSD近來越來越受歡迎,它們也在個人桌面電腦市場上大有斬獲,例如Ubuntu系統,但大部分都是電腦高手在使用。 某些Unix變種,例如HPHP-UX以及IBM的AIX僅設計用於自家的硬體產品上,而SUNSolaris可安裝於自家的硬體或x86電腦上。蘋果電腦的Mac OS X是一個從NeXTSTEPMach以及FreeBSD共同衍生出來的微核心BSD系統,此OS取代了蘋果電腦早期非Unix家族的Mac OS。經歷數年的披荊斬棘,自由開源的Unix系統逐漸蠶食鯨吞以往專利軟體的專業領域,例如以往電腦動畫運算巨擘──SGIIRIX系統已被Linux家族及Plan 9[3]叢集所取代。









個在Linux底下執行的客製化KDE桌面系統

thread

Thread 就是 Lightweight process

一個Thread 就是一個小型的 Process ,若我們把 Process 分為兩個部份----- Threads 和 Resources,Threads 就是這個 Process 的動態執行者(Dynamic Object),而每一個 Thread 的開頭就是這個程式的一個 Control Point. 因為同屬一個Process,所以 Thread 的 context switch 不同於Process 的 context switch,因為後者必須做 Address space 的置換(很費時). 傳統的 UNIX Process 就是一個 單一 Thread 的 Process. 而另外也有 Multithread in uniproceeor and on multiprocessors. 而因為一個 Thread 是一個小型的 Process, 所以它必須有自己的 PC (Program Counter), Stack 和 Register Set, 以儲存被 context switch 時, 相關 registers 的內容.

2009年6月13日 星期六

關於GDB

基本gdb
gdb是個命令列模式的交談(interactive)除錯器, 跟telnet或其它的unix交談式程式一樣有個提示符號,然後要下命令
(gdb)COMMAND
不要忘了gcc編譯時要加 -g 參數, 基本gdb命令
檔案處理
========
file a.out 載入可執行檔a.out
path 告訴gdb obj code在那
directory 告訴gdb source code在那裡
SHELL
=====
shell ls 就會執行ls了
cd xxx 不過用shell的方法跟Makefile一樣喚起sub shell而已
要真的cd到目錄要用cd
中斷點(Break point and watch point)處理
=======================================
break 設定中斷點
clear 清除中斷點
delete 清除中斷點
disable 暫時使中斷無作用
enable 使中斷再作用
condition 進一步設中斷點的條件 如果條件為true則中斷
commands 如果中斷了則執行commands與end中的一連串gdb命令
.....
end
其中 中斷點可以用source code的行數來代表(這些資訊藏在ELF格式 裡的.line這個section裡),也可以用中斷點的流水號來表示
br 在目前位置設中斷點
br 100 在100行中斷
br func1 在func1中斷
br +100 目前位置+100行中斷
br *0x08048123 在這位址中斷
br file.c:100 因為如果是多個c檔案時指定file.c
tbreak 同break的寫法 不過中斷一次後 此中斷點就失效
br 100 if (var == 5) 條件中斷 後面跟著c語法的條件判斷式
br 100 在第100行中斷並且執行command...end中的gdb命令
commands
silent
printf "x is %d\n",x
end
break String::func1 C++ Function Overloading的中斷 String是class
clear 100 清除中斷點 後面跟著行號或函數名
clear func1
delete 5 清除5號中斷點 後面是中斷點流水編號
disable 3 暫時使3號中斷點沒作用 後面是中斷點流水編號
enable 2 使2號中斷點作用 後面是中斷點流水編號
condition 3 (var > 3) 設3號中斷點的條件 如果條件為true則中斷
condition 3 清除3號中斷點的條件
程式執行
========
set args xxx 給執行程式參數xxx,就是main裡的**argv
run 開始跑程式
continue 中斷後繼續跑
next 往下跳一步c程式 如果有副程式 執行完整個副程式
step 往下跳一步c程式 如果有副程式 追進副程式
until 跳離一個while for迴圈
nexti 往下一步CPU組語的指令(Instruction)執行完整個副程式
stepi 往下一步CPU組語的指令(Instruction)追進副程式
until 執行到source code的行數比目前的大
如果目前所在行是loop的最後一行就會跳離loop
程式變數值(data)處理
====================
print var 看var的值
print &var 印出var的位址(其時這就是C 啦)
print *var 印出*var值 var是pointer
display var display會每次step, next時都會印出值來,print只印一次
print (var=value) 設var的值為value
其實print 可以只用p代替 很多指令都可以簡寫代替
p/x /x表示印hex值
/u表示unsigned digit
/d signed digit
/t 二進位值

/是列印的選項 在Solaris上的adb也有相似形式
x/3uh 0x8048012 印出記憶體
其中
3表示看3個
u unsigned digit(跟上面p命令一樣意義)
h halfword就是2bytes(bhwg分別是1248bytes)
GDB內定變數(跟程式變數不一樣喔)
===============================
一些gdb方便的變數(convenience variable)
$_ 用x命令所得到的最後一個位址
$__ 用x命令所得到的最後一個位址的值
$_exitcode 程式離開的code就是用exit時的code
CPU暫存器(registers)
$pc program counter就是目前cpu指到的執行位置啦
$sp stack pointer
訊息觀看與設定
==============
info 得到一些program debug資訊
info break
info frame
info display
info program
info share
info registers

show 得到一些系統(OS, CPU Arch), GDB資訊
show args (系統傳進來的argv[0],argv[1]...)
show os (OS是什麼)
show endian
show prompt (gdb的提示符號)
list 看原始碼
list x 從第x行的source code印出,x不寫從目前行印出
list *addr 秀出addr所在source code的行
可以先用info program找出目前PC的值
再用list *addr
search REGEXP 在目前source code做RE搜尋
disas 想看machine code用這個
whatis var 告訴我var的資料型別是啥 int, char or double
ptype var 告訴我var的資料型別是啥 這用來看struct用的
set 設定gdb, 系統的控制變數值(這些變數不是program內的)
set listsize xx 設定要看xx行source code
set $pc xx 把PC設到 xx
set convenience可以自己設變數
help 可以得到命令HELP
程序與副程式(process and sub-function)
======================================
backtrace(bt)2 程式執行到這裡前的兩個副程式,2不寫則列出全部
frame 2 選擇2號frame跳過去 2不寫就列出現在執行到那裡
up 2 往上走2個副程式
down 3 往下走2個副程式
return expression 不要玩了,回到上一層呼叫的routine去並return一個值
finish 繼續玩完一個選擇的stack frame(副程式)
kill 砍掉child process
signal procss-id 送signal給process
attach procss-id debug一個已經在記憶體跑的process
detach procss-id 釋放attach的process脫離gdb的控制
其中每次程式呼叫副程式時, 原本的執行的世界的東西(變數值啊等等)必需先保存起來, 然後再跳到新世界(將要執行的副程式)這就是stack, 每叫一個sub routine就等於進到一個stack frame
(gdb)frame 2
就是選擇2號frame,而0號frame就是目前在執行的副程式, 1號是呼叫0號的副程式,以此類推, finish搭配frame這個命令來用
所以bt這個命令很重要,可以追回之前叫了那些function來到目前的地方。 通常在命令列也有類似的追蹤system call的程式,因為system call很重要, 在Solaris上我們可以用
$ truss prog1
在Linux上
$ strace prog1
來看現在程式到底叫了甚麼system call導致他毀掉。
attach, detach必需在有支援process 的環境, 因為有的沒記憶體保護OS,或embadded system沒有支援, 另外也要有能力送signal給process的環境才行, 這主要可以來debug deamon或做multiprocess的除錯