清楚說明 同步+擱置 , 同步+不擱置, 異步+擱置, 異步+不擱置 四種方式
1. 詳解 異步+擱置模式的 select()
2. 詳解 異步+非擱置模式的AIO
3. linux上最佳的處理方法 epoll...
read more
2008年12月31日 星期三
2008年12月26日 星期五
2008年12月17日 星期三
[+/-] : fork與vfork的區別
vfork用於創建一個新進程,而該新進程的目的是exec一個新進程,vfork和fork一樣都創建一個子進程,但是它並不將父進程的地址空間完全複製到子進程中,不會複製頁表。因為子進程會立即調用exec,於是也就不會存放該地址空間。不過在子進程中調用exec或exit之前,他在父進程的空間中運行。
為什麼會有vfork,因為以前的fork當它創建一個子進程時,將會創建一個新的地址空間,並且拷貝父進程的資源,而往往在子進程中會執行exec調用,這樣,前面的拷貝工作就是白費力氣了,這種情況下,聰明的人就想出了vfork,它產生的子進程剛開始暫時與父進程共享地址空間(其實就是線程的概念了),因為這時候子進程在父進程的地址空間中運行,所以子進程不能進行寫操作,並且在兒子「霸佔」著老子的房子時候,要委屈老子一下了,讓他在外面歇著(阻塞),一旦兒子執行了exec或者exit後,相當於兒子買了自己的房子了,這時候就相當於分家了。
vfork和fork之間的另一個區別是: vfork保證子進程先運行,在她調用exec或exit之後父進程才可能被調度運行。如果在調用這兩個函數之前子進程依賴於父進程的進一步動作,則會導致死鎖。
由此可見,這個系統調用是用來啟動一個新的應用程序。其次,子進程在vfork()返回後直接運行在父進程的棧空間,並使用父進程的內存和數據。這意味著子進程可能破壞父進程的數據結構或棧,造成失敗。
為了避免這些問題,需要確保一旦調用vfork(),子進程就不從當前的棧框架中返回,並且如果子進程改變了父進程的數據結構就不能調用exit函數。子進程還必須避免改變全局數據結構或全局變量中的任何信息,因為這些改變都有可能使父進程不能繼續。
通常,如果應用程序不是在fork()之後立即調用exec(),就有必要在fork()被替換成vfork()之前做仔細的檢查。
用fork函數創建子進程後,子進程往往要調用一種exec函數以執行另一個程序,當進程調用一種exec函數時,該進程完全由新程序代換,而新程序則從其main函數開始執行,因為調用exec並不創建新進程,所以前後的進程id 並未改變,exec只是用另一個新程序替換了當前進程的正文,數據,堆和棧段。...
read more
為什麼會有vfork,因為以前的fork當它創建一個子進程時,將會創建一個新的地址空間,並且拷貝父進程的資源,而往往在子進程中會執行exec調用,這樣,前面的拷貝工作就是白費力氣了,這種情況下,聰明的人就想出了vfork,它產生的子進程剛開始暫時與父進程共享地址空間(其實就是線程的概念了),因為這時候子進程在父進程的地址空間中運行,所以子進程不能進行寫操作,並且在兒子「霸佔」著老子的房子時候,要委屈老子一下了,讓他在外面歇著(阻塞),一旦兒子執行了exec或者exit後,相當於兒子買了自己的房子了,這時候就相當於分家了。
vfork和fork之間的另一個區別是: vfork保證子進程先運行,在她調用exec或exit之後父進程才可能被調度運行。如果在調用這兩個函數之前子進程依賴於父進程的進一步動作,則會導致死鎖。
由此可見,這個系統調用是用來啟動一個新的應用程序。其次,子進程在vfork()返回後直接運行在父進程的棧空間,並使用父進程的內存和數據。這意味著子進程可能破壞父進程的數據結構或棧,造成失敗。
為了避免這些問題,需要確保一旦調用vfork(),子進程就不從當前的棧框架中返回,並且如果子進程改變了父進程的數據結構就不能調用exit函數。子進程還必須避免改變全局數據結構或全局變量中的任何信息,因為這些改變都有可能使父進程不能繼續。
通常,如果應用程序不是在fork()之後立即調用exec(),就有必要在fork()被替換成vfork()之前做仔細的檢查。
用fork函數創建子進程後,子進程往往要調用一種exec函數以執行另一個程序,當進程調用一種exec函數時,該進程完全由新程序代換,而新程序則從其main函數開始執行,因為調用exec並不創建新進程,所以前後的進程id 並未改變,exec只是用另一個新程序替換了當前進程的正文,數據,堆和棧段。...
read more
2008年9月23日 星期二
[+/-] : pid hash table
1. 基本概念
每個進程控制塊都有4個有關ID、含義不同的值,內核根據它們組成了4個全局的2維的HASH表,每個進程都要鏈接到這四個不同含義的Hash表當中。
/* 4種類型的值*/
enum pid_type
{
PIDTYPE_PID, 進程的PID
PIDTYPE_TGID, 線程組ID
PIDTYPE_PGID, 進程組ID
PIDTYPE_SID, 會話ID
PIDTYPE_MAX
};
struct task_struct {
......
/* PID/PID hash table linkage. */
struct pid pids[PIDTYPE_MAX];
......
}
2. Hash表的數組
四個全局的Hash表頭位於: static struct hlist_head *pid_hash[PIDTYPE_MAX];
每一個Hash表都是一個數組,每一個元素是一個Hash值的鏈表頭。默認有2048個元素
-----------------------------------------------------------------------------
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |...|2047| |
-----------------------------------------------------------------------------
3. Hash表數組元素的鏈表
我們拿進程的進程組ID(PIDTYPE_PGID)來舉例,假如有3個進程組,分別是:
GROUP1:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
10 10 hash(10)=88 (進程組領頭)
11 10 hash(10)=88
12 10 hash(10)=88
GROUP2:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
100 100 hash(100)=99 (進程組領頭)
101 100 hash(100)=99
102 100 hash(100)=99
GROUP3:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
550 550 hash(550)=88 (進程組領頭)
551 550 hash(550)=88
552 550 hash(550)=88
struct pid
{
/* Try to keep pid_chain in the same cacheline as nr for find_pid */
/* 值 */
int nr;
/* HASH_PGID 值相同、且為進程組領頭的進程鏈在這裡,如PID值為10和550的兩個進程會通過這個字段鏈接,這裡可以認為是1個維度鏈,非進程組的進程的這個域為NULL(這裡不考慮其它3種類型的值) */
struct hlist_node pid_chain;
/* PGID 值相同的進程鏈在這裡,如上3個進程組,分別各自通過這個域鏈接起來,這裡可以認為是第2個維度鏈 */
/* list of pids with the same nr, only one of them is in the hash */
struct list_head pid_list;
};
PGID的Hash表(即全局的pid_hash[PIDTYPE_PGID])
----
0
----
1
----
2
----
88 ---> 10 ---> 11 ---> 12 通過pid_list域鏈接
---- |
... | 通過pid_chain域鏈接
---- |
90 550 ---> 551 ---> 552 通過pid_list域鏈接
----
..
----
99 ---> 100 ---> 101 ---> 102 通過pid_list域鏈接
----
2047
文章出處:http://www.diybl.com/course/6_system/linux/Linuxjs/200888/135051.html...
read more
每個進程控制塊都有4個有關ID、含義不同的值,內核根據它們組成了4個全局的2維的HASH表,每個進程都要鏈接到這四個不同含義的Hash表當中。
/* 4種類型的值*/
enum pid_type
{
PIDTYPE_PID, 進程的PID
PIDTYPE_TGID, 線程組ID
PIDTYPE_PGID, 進程組ID
PIDTYPE_SID, 會話ID
PIDTYPE_MAX
};
struct task_struct {
......
/* PID/PID hash table linkage. */
struct pid pids[PIDTYPE_MAX];
......
}
2. Hash表的數組
四個全局的Hash表頭位於: static struct hlist_head *pid_hash[PIDTYPE_MAX];
每一個Hash表都是一個數組,每一個元素是一個Hash值的鏈表頭。默認有2048個元素
-----------------------------------------------------------------------------
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |...|2047| |
-----------------------------------------------------------------------------
3. Hash表數組元素的鏈表
我們拿進程的進程組ID(PIDTYPE_PGID)來舉例,假如有3個進程組,分別是:
GROUP1:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
10 10 hash(10)=88 (進程組領頭)
11 10 hash(10)=88
12 10 hash(10)=88
GROUP2:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
100 100 hash(100)=99 (進程組領頭)
101 100 hash(100)=99
102 100 hash(100)=99
GROUP3:
PID PGIP HASH_PGID (根據進程組ID[PGIP]得到的Hash值)
550 550 hash(550)=88 (進程組領頭)
551 550 hash(550)=88
552 550 hash(550)=88
struct pid
{
/* Try to keep pid_chain in the same cacheline as nr for find_pid */
/* 值 */
int nr;
/* HASH_PGID 值相同、且為進程組領頭的進程鏈在這裡,如PID值為10和550的兩個進程會通過這個字段鏈接,這裡可以認為是1個維度鏈,非進程組的進程的這個域為NULL(這裡不考慮其它3種類型的值) */
struct hlist_node pid_chain;
/* PGID 值相同的進程鏈在這裡,如上3個進程組,分別各自通過這個域鏈接起來,這裡可以認為是第2個維度鏈 */
/* list of pids with the same nr, only one of them is in the hash */
struct list_head pid_list;
};
PGID的Hash表(即全局的pid_hash[PIDTYPE_PGID])
----
0
----
1
----
2
----
88 ---> 10 ---> 11 ---> 12 通過pid_list域鏈接
---- |
... | 通過pid_chain域鏈接
---- |
90 550 ---> 551 ---> 552 通過pid_list域鏈接
----
..
----
99 ---> 100 ---> 101 ---> 102 通過pid_list域鏈接
----
2047
文章出處:http://www.diybl.com/course/6_system/linux/Linuxjs/200888/135051.html...
read more
2008年9月16日 星期二
[+/-] : linux 32bit platform memo
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 4
sizeof(long long) = 8
sizeof(size_t) = 4
sizeof(off_t) = 4
sizeof(void *) = 4...
read more
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 4
sizeof(long long) = 8
sizeof(size_t) = 4
sizeof(off_t) = 4
sizeof(void *) = 4...
read more
2008年8月27日 星期三
[+/-] : Netfilter 中實現 NAT 與 Conntrack 的程式教學
1. NAT
Netfilter 地址轉換的實現
2. Connection Track
Netfilter 連接跟蹤與狀態檢測的實現(架構流程與函數)
Netfilter 連接跟蹤與狀態檢測的實現(structure 細節)
3. Iptables
iptables執行的流程分析
附錄
1.ipt_do_table函數的實現
ipt_do_table函數的實現(詳細版)
(用內核已註冊到TARGET或match的規則 比對封包)
2.註冊 table 與 hook的實現
3.連接計數的變化 - netfilter...
read more
Netfilter 地址轉換的實現
2. Connection Track
Netfilter 連接跟蹤與狀態檢測的實現(架構流程與函數)
Netfilter 連接跟蹤與狀態檢測的實現(structure 細節)
3. Iptables
iptables執行的流程分析
附錄
1.ipt_do_table函數的實現
ipt_do_table函數的實現(詳細版)
(用內核已註冊到TARGET或match的規則 比對封包)
2.註冊 table 與 hook的實現
3.連接計數的變化 - netfilter...
read more
2007年12月13日 星期四
2007年12月10日 星期一
2007年11月16日 星期五
2007年11月12日 星期一
[+/-] : Thread functions
線程基本編程
索引:
1.創建線程pthread_create
2.等待線程結束pthread_join
3.分離線程pthread_detach
4.創建線程鍵pthread_key_create
5.刪除線程鍵pthread_key_delete
6.設置線程數據pthread_setspecific
7.獲取線程數據pthread_getspecific
8.獲取線程標示符pthread_self
9.比較線程pthread_equal
10.一次執行pthread_once
11.出讓執行權sched_yield
12.修改優先級pthread_setschedparam
13.獲取優先級pthread_getschedparam
14.發送信號pthread_kill
15.設置線程掩碼pthread_sigmask
16.終止線程pthread_exit
17.退出線程pthread_cancel
18.允許/禁止退出線程pthread_setcancelstate
19.設置退出類型pthread_setcanceltype
20.創建退出點pthread_testcancel
21.壓入善後處理函數
22.彈出善後處理函數
------------------------------------------------------------------------------
--
1.創建線程pthread_create
#include
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void *(*start_
routine)(void *), void *arg);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
創建一個線程。
參數tattr中含有初始化線程所需要的屬性,start_routine是線程入口函數的地址,當st
art_routine返回時,相應的線程就結束了。
當函數成功時,線程標示符保存在參數tid指向的內存中。
如果不指定屬性對象,將其置為NULL,則創建一個缺省的線程,有如下屬性:
非綁定的;
未分離的;
由一個缺省大小的堆棧;
具有和父線程一樣的優先級。
注意:在創建子線程時,傳給子線程的輸入參數最好是由malloc()函數返回的指針或指向
全局變量的指針,而不要是指向局部變量的指針。要保證子線程處理參數時,該區域仍然
有效。
------------------------------------------------------------------------------
--
2.等待線程結束pthread_join
#include
int pthread_join(pthread_t tid, void **status);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
等待一個線程結束。
該函數阻塞調用它線程,直到參數tid指定的線程結束。
tid指定的線程必須在當前進程中,同時tid指定的線程必須是非分離的。
不能有多個線程等待同一個線程終止。如果出現這種情況,一個線程將成功返回,別的線
程將返回錯誤ESRCH。
如果參數status不為NULL,則將線程的退出狀態放在status指向的內存中。
------------------------------------------------------------------------------
--
3.分離線程pthread_detach
#include
int pthread_detach(pthread_t tid);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
將非分離的線程設置為分離線程。即通知線程庫在指定的線程終止時回收線程佔用的內存
等資源。
在一個線程上使用多次pthread_detach的結果是不可預見的。
------------------------------------------------------------------------------
--
4.創建線程鍵pthread_key_create
#include
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
返回值:函數成功返回0。任何其他返回值都表示錯誤。
在進程中分配一個鍵值,這個鍵被用來表示一個線程數據項。這個鍵對進程中所有的線程
都是可見的。剛創建線程數據鍵時,在所有線程中和這個鍵相關聯的值都是NULL。
函數成功返回後,分配的鍵放在key參數指向的內存中,必須保證key參數指向的內存區的
有效性。
如果指定瞭解析函數destructor,那麼當線程結束時並且將非空的值綁定在這個鍵上,系
統將調用destructor函數,參數就是相關線程與這個鍵綁定的值。綁定在這個鍵上的內存
塊可由destructor函數釋放。
------------------------------------------------------------------------------
--
5.刪除線程鍵pthread_key_delete
#include
int pthread_key_delete(pthread_key_t key);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
刪除線程數據鍵。這個鍵佔用的內存將被釋放,該鍵再被引用將返回錯誤。
在調用該函數之前,程序必須釋放和本線程相關聯的資源,該函數不會引發線程數據鍵的
解析函數。
------------------------------------------------------------------------------
--
6.設置線程數據pthread_setspecific
#include
int pthread_setspecific(pthread_key_t key, const void *value);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
設置和某個線程數據鍵綁定在一起的線程專用數據(一般是指針)。
函數不會釋放原來綁定在鍵上的內存,給一個鍵值綁定新的指針時,必須釋放原指針指向
的內存,否則會發生內存洩漏。
------------------------------------------------------------------------------
--
7.獲取線程數據pthread_getspecific
#include
void pthread_getspecific(pthread_key_t key, void **value);
無返回值。出錯時value指向NULL。
獲取綁定在線程數據鍵上的值,並在指定的位置存儲取來的值。
------------------------------------------------------------------------------
--
8.獲取線程標示符pthread_self
#include
pthread_t pthread_self(void);
返回當前線程的標示符。
------------------------------------------------------------------------------
--
9.比較線程pthread_equal
#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
如果tid1和tid2相同,函數返回一個非0值,否則返回0。
如果tid1或tid2中任何一個是非法值,則返回將是不可預料的。
------------------------------------------------------------------------------
--
10.一次執行pthread_once
#include
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
返回值:函數成功返回0。任何其他返回值都表示錯誤。
函數用來調用初始化函數。如果已經有線程通過pthread_once調用過這個初始化函數一次
,那麼以後通過pthread_once函數再調用這個初始化函數將無效。
參數once_control決定了相應的初始化函數是否被調用過。它一般如下使用:
[static] pthread_once_t once_control = PTHREAD_ONCE_INIT。
------------------------------------------------------------------------------
--
11.出讓執行權sched_yield
#include
int sched_yield(void);
返回值:函數成功返回0。-1表示錯誤。
把當前線程的執行權(即對處理器的控制權)出讓給另一個有相同或更高優先級的線程。
------------------------------------------------------------------------------
--
12.修改優先級pthread_setschedparam
#include
int pthread_setschedparam(pthread_t tid, int policy, const struct sched_param
*param);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
修改線程的優先權。
------------------------------------------------------------------------------
--
13.獲取優先級pthread_getschedparam
#include
int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param)
;
返回值:函數成功返回0。任何其他返回值都表示錯誤。
獲取線程的優先級。
------------------------------------------------------------------------------
--
14.發送信號pthread_kill
#include
int pthread_kill(pthread_t tid, int sig);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
向tid指定的線程發送一個信號,tid指定的線程必須和當前線程在同一個進程中。
當sig參數為0時,函數將進行錯誤檢查,不發送信號,這常常用來檢查tid的合法性。
------------------------------------------------------------------------------
--
15.設置線程掩碼pthread_sigmask
#include
#include
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
改變或檢驗當前線程的信號掩碼。
參數how表示對當前信號掩碼進行什麼操作,有如下值:SIG_BLOCK、SIG_UNBLOCK、SIG_S
ETMASK。
當參數new為NULL時,不論how的值是什麼,當前線程的信號掩碼都不會改變。
舊的信號掩碼保存在參數old指向的內存中,當old不為NULL時。
------------------------------------------------------------------------------
--
16.終止線程pthread_exit
#include
void pthread_exit(void *status);
終止當前線程,所有綁定在線程數據鍵上的內存將被釋放。如果當前線程是非分離的,那
麼這個線程的標示符合退出代碼將被保留,直到其他線程用pthread_join來等待當前線程
的終止。如果當前線程是分離的,status將被忽略,線程標示符將被立即回收。
若status不為NULL,線程的退出代碼被置為status參數指向的值。
------------------------------------------------------------------------------
--
17.退出線程pthread_cancel
#include
int pthread_cancel(pthread_t thread);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
退出一個線程。如何響應退出請求取決於目標線程的狀態。
------------------------------------------------------------------------------
--
18.允許/禁止退出線程pthread_setcancelstate
#include
int pthread_setcancelstate(int state, int *oldstate);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
參數state取值為PTHREAD_CANCEL_ENABLE或PTHREAD_CANCEL_DISABLE。
------------------------------------------------------------------------------
--
19.設置退出類型pthread_setcanceltype
#include
int pthread_setcanceltype(int type, int *oldtype);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
將線程退出類型設置為延遲類型或異步類型。參數type的取值為PTHREAD_CANCEL_DEFERRE
D或PTHREAD_CANCEL_ASYNCHRONOUS。
當一個線程被創建後,缺省值是延遲類型。在異步方式下,線程可以在執行的任何時候被
退出。
------------------------------------------------------------------------------
--
20.創建退出點pthread_testcancel
#include
void pthread_testcancel(void);
無返回值。
設置線程的退出點。
只有當線程的退出狀態是允許退出的,而且線程的退出類型是延遲時,調用該函數才有效
。如果調用時線程的退出狀態是禁止的,則該調用不起作用。
小心使用該函數,只有在能夠安全的被退出的地方才能夠設置退出點。
------------------------------------------------------------------------------
--
21.壓入善後處理函數
#include
void pthread_cleanup_push(void (*routine)(void *), void *args);
將一個善後處理函數壓入善後處理函數堆棧。
------------------------------------------------------------------------------
--
22.彈出善後處理函數
#include
void pthread_cleanup_pop(int execute);
從善後處理函數堆棧中彈出一個善後處理函數。如果參數execute非0,則執行彈出的函數
;如果參數為0,則不執行彈出函數。
如果一個線程顯式或隱式的調用pthread_exit()函數或線程接受了退出請求,線程庫實際
上將會以非0參數調用pthread_cleanup_pop函數。...
read more
索引:
1.創建線程pthread_create
2.等待線程結束pthread_join
3.分離線程pthread_detach
4.創建線程鍵pthread_key_create
5.刪除線程鍵pthread_key_delete
6.設置線程數據pthread_setspecific
7.獲取線程數據pthread_getspecific
8.獲取線程標示符pthread_self
9.比較線程pthread_equal
10.一次執行pthread_once
11.出讓執行權sched_yield
12.修改優先級pthread_setschedparam
13.獲取優先級pthread_getschedparam
14.發送信號pthread_kill
15.設置線程掩碼pthread_sigmask
16.終止線程pthread_exit
17.退出線程pthread_cancel
18.允許/禁止退出線程pthread_setcancelstate
19.設置退出類型pthread_setcanceltype
20.創建退出點pthread_testcancel
21.壓入善後處理函數
22.彈出善後處理函數
------------------------------------------------------------------------------
--
1.創建線程pthread_create
#include
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void *(*start_
routine)(void *), void *arg);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
創建一個線程。
參數tattr中含有初始化線程所需要的屬性,start_routine是線程入口函數的地址,當st
art_routine返回時,相應的線程就結束了。
當函數成功時,線程標示符保存在參數tid指向的內存中。
如果不指定屬性對象,將其置為NULL,則創建一個缺省的線程,有如下屬性:
非綁定的;
未分離的;
由一個缺省大小的堆棧;
具有和父線程一樣的優先級。
注意:在創建子線程時,傳給子線程的輸入參數最好是由malloc()函數返回的指針或指向
全局變量的指針,而不要是指向局部變量的指針。要保證子線程處理參數時,該區域仍然
有效。
------------------------------------------------------------------------------
--
2.等待線程結束pthread_join
#include
int pthread_join(pthread_t tid, void **status);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
等待一個線程結束。
該函數阻塞調用它線程,直到參數tid指定的線程結束。
tid指定的線程必須在當前進程中,同時tid指定的線程必須是非分離的。
不能有多個線程等待同一個線程終止。如果出現這種情況,一個線程將成功返回,別的線
程將返回錯誤ESRCH。
如果參數status不為NULL,則將線程的退出狀態放在status指向的內存中。
------------------------------------------------------------------------------
--
3.分離線程pthread_detach
#include
int pthread_detach(pthread_t tid);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
將非分離的線程設置為分離線程。即通知線程庫在指定的線程終止時回收線程佔用的內存
等資源。
在一個線程上使用多次pthread_detach的結果是不可預見的。
------------------------------------------------------------------------------
--
4.創建線程鍵pthread_key_create
#include
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
返回值:函數成功返回0。任何其他返回值都表示錯誤。
在進程中分配一個鍵值,這個鍵被用來表示一個線程數據項。這個鍵對進程中所有的線程
都是可見的。剛創建線程數據鍵時,在所有線程中和這個鍵相關聯的值都是NULL。
函數成功返回後,分配的鍵放在key參數指向的內存中,必須保證key參數指向的內存區的
有效性。
如果指定瞭解析函數destructor,那麼當線程結束時並且將非空的值綁定在這個鍵上,系
統將調用destructor函數,參數就是相關線程與這個鍵綁定的值。綁定在這個鍵上的內存
塊可由destructor函數釋放。
------------------------------------------------------------------------------
--
5.刪除線程鍵pthread_key_delete
#include
int pthread_key_delete(pthread_key_t key);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
刪除線程數據鍵。這個鍵佔用的內存將被釋放,該鍵再被引用將返回錯誤。
在調用該函數之前,程序必須釋放和本線程相關聯的資源,該函數不會引發線程數據鍵的
解析函數。
------------------------------------------------------------------------------
--
6.設置線程數據pthread_setspecific
#include
int pthread_setspecific(pthread_key_t key, const void *value);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
設置和某個線程數據鍵綁定在一起的線程專用數據(一般是指針)。
函數不會釋放原來綁定在鍵上的內存,給一個鍵值綁定新的指針時,必須釋放原指針指向
的內存,否則會發生內存洩漏。
------------------------------------------------------------------------------
--
7.獲取線程數據pthread_getspecific
#include
void pthread_getspecific(pthread_key_t key, void **value);
無返回值。出錯時value指向NULL。
獲取綁定在線程數據鍵上的值,並在指定的位置存儲取來的值。
------------------------------------------------------------------------------
--
8.獲取線程標示符pthread_self
#include
pthread_t pthread_self(void);
返回當前線程的標示符。
------------------------------------------------------------------------------
--
9.比較線程pthread_equal
#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
如果tid1和tid2相同,函數返回一個非0值,否則返回0。
如果tid1或tid2中任何一個是非法值,則返回將是不可預料的。
------------------------------------------------------------------------------
--
10.一次執行pthread_once
#include
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
返回值:函數成功返回0。任何其他返回值都表示錯誤。
函數用來調用初始化函數。如果已經有線程通過pthread_once調用過這個初始化函數一次
,那麼以後通過pthread_once函數再調用這個初始化函數將無效。
參數once_control決定了相應的初始化函數是否被調用過。它一般如下使用:
[static] pthread_once_t once_control = PTHREAD_ONCE_INIT。
------------------------------------------------------------------------------
--
11.出讓執行權sched_yield
#include
int sched_yield(void);
返回值:函數成功返回0。-1表示錯誤。
把當前線程的執行權(即對處理器的控制權)出讓給另一個有相同或更高優先級的線程。
------------------------------------------------------------------------------
--
12.修改優先級pthread_setschedparam
#include
int pthread_setschedparam(pthread_t tid, int policy, const struct sched_param
*param);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
修改線程的優先權。
------------------------------------------------------------------------------
--
13.獲取優先級pthread_getschedparam
#include
int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param)
;
返回值:函數成功返回0。任何其他返回值都表示錯誤。
獲取線程的優先級。
------------------------------------------------------------------------------
--
14.發送信號pthread_kill
#include
int pthread_kill(pthread_t tid, int sig);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
向tid指定的線程發送一個信號,tid指定的線程必須和當前線程在同一個進程中。
當sig參數為0時,函數將進行錯誤檢查,不發送信號,這常常用來檢查tid的合法性。
------------------------------------------------------------------------------
--
15.設置線程掩碼pthread_sigmask
#include
#include
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
改變或檢驗當前線程的信號掩碼。
參數how表示對當前信號掩碼進行什麼操作,有如下值:SIG_BLOCK、SIG_UNBLOCK、SIG_S
ETMASK。
當參數new為NULL時,不論how的值是什麼,當前線程的信號掩碼都不會改變。
舊的信號掩碼保存在參數old指向的內存中,當old不為NULL時。
------------------------------------------------------------------------------
--
16.終止線程pthread_exit
#include
void pthread_exit(void *status);
終止當前線程,所有綁定在線程數據鍵上的內存將被釋放。如果當前線程是非分離的,那
麼這個線程的標示符合退出代碼將被保留,直到其他線程用pthread_join來等待當前線程
的終止。如果當前線程是分離的,status將被忽略,線程標示符將被立即回收。
若status不為NULL,線程的退出代碼被置為status參數指向的值。
------------------------------------------------------------------------------
--
17.退出線程pthread_cancel
#include
int pthread_cancel(pthread_t thread);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
退出一個線程。如何響應退出請求取決於目標線程的狀態。
------------------------------------------------------------------------------
--
18.允許/禁止退出線程pthread_setcancelstate
#include
int pthread_setcancelstate(int state, int *oldstate);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
參數state取值為PTHREAD_CANCEL_ENABLE或PTHREAD_CANCEL_DISABLE。
------------------------------------------------------------------------------
--
19.設置退出類型pthread_setcanceltype
#include
int pthread_setcanceltype(int type, int *oldtype);
返回值:函數成功返回0。任何其他返回值都表示錯誤。
將線程退出類型設置為延遲類型或異步類型。參數type的取值為PTHREAD_CANCEL_DEFERRE
D或PTHREAD_CANCEL_ASYNCHRONOUS。
當一個線程被創建後,缺省值是延遲類型。在異步方式下,線程可以在執行的任何時候被
退出。
------------------------------------------------------------------------------
--
20.創建退出點pthread_testcancel
#include
void pthread_testcancel(void);
無返回值。
設置線程的退出點。
只有當線程的退出狀態是允許退出的,而且線程的退出類型是延遲時,調用該函數才有效
。如果調用時線程的退出狀態是禁止的,則該調用不起作用。
小心使用該函數,只有在能夠安全的被退出的地方才能夠設置退出點。
------------------------------------------------------------------------------
--
21.壓入善後處理函數
#include
void pthread_cleanup_push(void (*routine)(void *), void *args);
將一個善後處理函數壓入善後處理函數堆棧。
------------------------------------------------------------------------------
--
22.彈出善後處理函數
#include
void pthread_cleanup_pop(int execute);
從善後處理函數堆棧中彈出一個善後處理函數。如果參數execute非0,則執行彈出的函數
;如果參數為0,則不執行彈出函數。
如果一個線程顯式或隱式的調用pthread_exit()函數或線程接受了退出請求,線程庫實際
上將會以非0參數調用pthread_cleanup_pop函數。...
read more
2007年11月8日 星期四
2007年11月2日 星期五
[+/-] : 何時使用哪個spin_lock?
需要澄清的是,互斥手段的選擇,不是根據臨界區的大小,而是根據臨界區的性質,以及 有哪些部分的代碼,即哪些內核執行路徑來爭奪。
從嚴格意義上說,semaphore和spinlock_XXX屬於不同層次的互斥手段,前者的 實現有賴於後者,這有點象HTTP和TCP的關係,都是協議,但層次是不同的。
先 說semaphore,它是進程級的,用於多個進程之間對資源的互斥,雖然也是在 內核中,但是該內核執行路徑是以進程的身份,代表進程來爭奪資源的。如 果 競爭不上,會有context switch,進程可以去sleep,但CPU不會停,會接著運行 其他的執行路徑。從概念上說,這和單CPU或多 CPU沒有直接的關係,只是在 semaphore本身的實現上,為了保證semaphore結構存取的原子性,在多CPU中需要spinlock來互 斥。
在內核中,更多的是要保持內核各個執行路徑之間的數據訪問互斥,這是最基本的互斥問題,即保持數據修改的原子性。 semaphore的實現,也要依賴這個。在單CPU中,主要是中斷和bottom_half的問題,因此,開關中斷就可以了。在多CPU中,又加上了其 他CPU的干擾,因此需要spinlock來幫助。這兩個部分結合起來,就形成了spinlock_XXX。它的特點是,一旦CPU進入了 spinlock_XXX,它就不會幹別的,而是一直空轉,直到鎖定成功為止。因此,這就決定了被spinlock_XXX鎖住的臨界區不能停,更不能 context switch,要存取完數據後趕快出來,以便其他的在空轉的執行路徑能夠獲得spinlock。這也是spinlock的原則所在。如果 當前執行路徑一定要進行context switch,那就要在schedule()之前釋放spinlock,否則,容易死鎖。因為在中斷和bh中,沒 有context,無法進行context switch,只能空轉等待spinlock,你context switch走了,誰知道猴年馬月才能回 來。
因為spinlock的原意和目的就是保證數據修改的原子性,因此也沒有理由在spinlock
鎖住的臨界區中停留。
spinlock_XXX有很多形式,有
spin_lock()/spin_unlock(),
spin_lock_irq()/spin_unlock_irq(),
spin_lock_irqsave/spin_unlock_irqrestore()
spin_lock_bh()/spin_unlock_bh()
local_irq_disable/local_irq_enable
local_bh_disable/local_bh_enable
那麼,在什麼情況下具體用哪個呢?這要看是在什麼內核執行路徑中,以及要與哪些內核
執行路徑相互斥。我們知道,內核中的執行路徑主要有:
1 用戶進程的內核態,此時有進程context,主要是代表進程在執行系統調用
等。
2 中斷或者異常或者自陷等,從概念上說,此時沒有進程context,不能進行
context switch。
3 bottom_half,從概念上說,此時也沒有進程context。
4 同時,相同的執行路徑還可能在其他的CPU上運行。
這樣,考慮這四個方面的因素,通過判斷我們要互斥的數據會被這四個因素中
的哪幾個來存取,就可以決定具體使用哪種形式的spinlock。如果只要和其他CPU
互斥,就要用spin_lock/spin_unlock,如果要和irq及其他CPU互斥,就要用
spin_lock_irq/spin_unlock_irq,如果既要和irq及其他CPU互斥,又要保存
EFLAG的狀態,就要用spin_lock_irqsave/spin_unlock_irqrestore,如果
要和bh及其他CPU互斥,就要用spin_lock_bh/spin_unlock_bh,如果不需要和
其他CPU互斥,只要和irq互斥,則用local_irq_disable/local_irq_enable,
如果不需要和其他CPU互斥,只要和bh互斥,則用local_bh_disable/local_bh_enable,
等等。值得指出的是,對同一個數據的互斥,在不同的內核執行路徑中,
所用的形式有可能不同(見下面的例子)。
舉一個例子。在中斷部分中有一個irq_desc_t類型的結構數組變量irq_desc[],
該數組每個成員對應一個irq的描述結構,裡面有該irq的響應函數等。
在irq_desc_t結構中有一個spinlock,用來保證存取(修改)的互斥。
對於具體一個irq成員,irq_desc[irq],對其存取的內核執行路徑有兩個,一是
在設置該irq的響應函數時(setup_irq),這通常發生在module的初始化階段,或
系統的初始化階段;二是在中斷響應函數中(do_IRQ)。代碼如下:
int setup_irq(unsigned int irq, struct irqaction * new)
{
int shared = 0;
unsigned long flags;
struct irqaction *old, **p;
irq_desc_t *desc = irq_desc + irq;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
if (new->flags & SA_SAMPLE_RANDOM) {
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq);
}
/*
* The following block of code has to be executed atomically
*/
[1] spin_lock_irqsave(&desc->lock,flags);
p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
[2] spin_unlock_irqrestore(&desc->lock,flags);
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new;
if (!shared) {
desc->depth = 0;
desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
desc->handler->startup(irq);
}
[3] spin_unlock_irqrestore(&desc->lock,flags);
register_irq_proc(irq);
return 0;
}
asmlinkage unsigned int do_IRQ(struct pt_regs regs)
{
/*
* We ack quickly, we don't want the irq controller
* thinking we're snobs just because some other CPU has
* disabled global interrupts (we have already done the
* INT_ACK cycles, it's too late to try to pretend to the
* controller that we aren't taking the interrupt).
*
* 0 return value means that this irq is already being
* handled by some other CPU. (or is disabled)
*/
int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code */
int cpu = smp_processor_id();
irq_desc_t *desc = irq_desc + irq;
struct irqaction * action;
unsigned int status;
kstat.irqs[cpu][irq]++;
[4] spin_lock(&desc->lock);
desc->handler->ack(irq);
/*
REPLAY is when Linux resends an IRQ that was dropped earlier
WAITING is used by probe to mark irqs that are being tested
*/
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
status |= IRQ_PENDING; /* we _want_ to handle it */
/*
* If the IRQ is disabled for whatever reason, we cannot
* use the action we have.
*/
action = NULL;
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status &= ~IRQ_PENDING; /* we commit to handling */
status |= IRQ_INPROGRESS; /* we are handling it */
}
desc->status = status;
/*
* If there is no IRQ handler or it was disabled, exit early.
Since we set PENDING, if another processor is handling
a different instance of this same irq, the other processor
will take care of it.
*/
if (!action)
goto out;
/*
* Edge triggered interrupts need to remember
* pending events.
* This applies to any hw interrupts that allow a second
* instance of the same irq to arrive while we are in do_IRQ
* or in the handler. But the code here only handles the _second_
* instance of the irq, not the third or fourth. So it is mostly
* useful for irq hardware that does not mask cleanly in an
* SMP environment.
*/
for (;;) {
[5] spin_unlock(&desc->lock);
handle_IRQ_event(irq, ®s, action);
[6] spin_lock(&desc->lock);
if (!(desc->status & IRQ_PENDING))
break;
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
out:
/*
* The ->end() handler has to deal with interrupts which got
* disabled while the handler was running.
*/
desc->handler->end(irq);
[7] spin_unlock(&desc->lock);
if (softirq_pending(cpu))
do_softirq();
return 1;
}
在setup_irq()中,因為其他CPU可能同時在運行setup_irq(),或者在運行setup_irq()時,
本地irq中斷來了,要執行do_IRQ()以修改desc->status。為了同時防止來自其他CPU和
本地irq中斷的干擾,如[1][2][3]處所示,使用了spin_lock_irqsave/spin_unlock_irqrestore()
而在do_IRQ()中,因為do_IRQ()本身是在中斷中,而且此時還沒有開中斷,本CPU中沒有
什麼可以中斷其運行,其他CPU則有可能在運行setup_irq(),或者也在中斷中,但這二者
對本地do_IRQ()的影響沒有區別,都是來自其他CPU的干擾,因此只需要用spin_lock/spin_unlock,
如[4][5][6][7]處所示。值得注意的是[5]處,先釋放該spinlock,再調用具體的響應函數。
再舉個例子:
static void tasklet_hi_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
[8] local_irq_disable();
list = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = NULL;
[9] local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
[10] local_irq_disable();
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
[11] local_irq_enable();
}
}
這裡,對tasklet_hi_vec[cpu]的修改,不存在CPU之間的競爭,因為每個CPU有各自獨立的數據,
所以只要防止irq的干擾,用local_irq_disable/local_irq_enable即可,如[8][9][10][11]處所示。...
read more
從嚴格意義上說,semaphore和spinlock_XXX屬於不同層次的互斥手段,前者的 實現有賴於後者,這有點象HTTP和TCP的關係,都是協議,但層次是不同的。
先 說semaphore,它是進程級的,用於多個進程之間對資源的互斥,雖然也是在 內核中,但是該內核執行路徑是以進程的身份,代表進程來爭奪資源的。如 果 競爭不上,會有context switch,進程可以去sleep,但CPU不會停,會接著運行 其他的執行路徑。從概念上說,這和單CPU或多 CPU沒有直接的關係,只是在 semaphore本身的實現上,為了保證semaphore結構存取的原子性,在多CPU中需要spinlock來互 斥。
在內核中,更多的是要保持內核各個執行路徑之間的數據訪問互斥,這是最基本的互斥問題,即保持數據修改的原子性。 semaphore的實現,也要依賴這個。在單CPU中,主要是中斷和bottom_half的問題,因此,開關中斷就可以了。在多CPU中,又加上了其 他CPU的干擾,因此需要spinlock來幫助。這兩個部分結合起來,就形成了spinlock_XXX。它的特點是,一旦CPU進入了 spinlock_XXX,它就不會幹別的,而是一直空轉,直到鎖定成功為止。因此,這就決定了被spinlock_XXX鎖住的臨界區不能停,更不能 context switch,要存取完數據後趕快出來,以便其他的在空轉的執行路徑能夠獲得spinlock。這也是spinlock的原則所在。如果 當前執行路徑一定要進行context switch,那就要在schedule()之前釋放spinlock,否則,容易死鎖。因為在中斷和bh中,沒 有context,無法進行context switch,只能空轉等待spinlock,你context switch走了,誰知道猴年馬月才能回 來。
因為spinlock的原意和目的就是保證數據修改的原子性,因此也沒有理由在spinlock
鎖住的臨界區中停留。
spinlock_XXX有很多形式,有
spin_lock()/spin_unlock(),
spin_lock_irq()/spin_unlock_irq(),
spin_lock_irqsave/spin_unlock_irqrestore()
spin_lock_bh()/spin_unlock_bh()
local_irq_disable/local_irq_enable
local_bh_disable/local_bh_enable
那麼,在什麼情況下具體用哪個呢?這要看是在什麼內核執行路徑中,以及要與哪些內核
執行路徑相互斥。我們知道,內核中的執行路徑主要有:
1 用戶進程的內核態,此時有進程context,主要是代表進程在執行系統調用
等。
2 中斷或者異常或者自陷等,從概念上說,此時沒有進程context,不能進行
context switch。
3 bottom_half,從概念上說,此時也沒有進程context。
4 同時,相同的執行路徑還可能在其他的CPU上運行。
這樣,考慮這四個方面的因素,通過判斷我們要互斥的數據會被這四個因素中
的哪幾個來存取,就可以決定具體使用哪種形式的spinlock。如果只要和其他CPU
互斥,就要用spin_lock/spin_unlock,如果要和irq及其他CPU互斥,就要用
spin_lock_irq/spin_unlock_irq,如果既要和irq及其他CPU互斥,又要保存
EFLAG的狀態,就要用spin_lock_irqsave/spin_unlock_irqrestore,如果
要和bh及其他CPU互斥,就要用spin_lock_bh/spin_unlock_bh,如果不需要和
其他CPU互斥,只要和irq互斥,則用local_irq_disable/local_irq_enable,
如果不需要和其他CPU互斥,只要和bh互斥,則用local_bh_disable/local_bh_enable,
等等。值得指出的是,對同一個數據的互斥,在不同的內核執行路徑中,
所用的形式有可能不同(見下面的例子)。
舉一個例子。在中斷部分中有一個irq_desc_t類型的結構數組變量irq_desc[],
該數組每個成員對應一個irq的描述結構,裡面有該irq的響應函數等。
在irq_desc_t結構中有一個spinlock,用來保證存取(修改)的互斥。
對於具體一個irq成員,irq_desc[irq],對其存取的內核執行路徑有兩個,一是
在設置該irq的響應函數時(setup_irq),這通常發生在module的初始化階段,或
系統的初始化階段;二是在中斷響應函數中(do_IRQ)。代碼如下:
int setup_irq(unsigned int irq, struct irqaction * new)
{
int shared = 0;
unsigned long flags;
struct irqaction *old, **p;
irq_desc_t *desc = irq_desc + irq;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
if (new->flags & SA_SAMPLE_RANDOM) {
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq);
}
/*
* The following block of code has to be executed atomically
*/
[1] spin_lock_irqsave(&desc->lock,flags);
p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
[2] spin_unlock_irqrestore(&desc->lock,flags);
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new;
if (!shared) {
desc->depth = 0;
desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
desc->handler->startup(irq);
}
[3] spin_unlock_irqrestore(&desc->lock,flags);
register_irq_proc(irq);
return 0;
}
asmlinkage unsigned int do_IRQ(struct pt_regs regs)
{
/*
* We ack quickly, we don't want the irq controller
* thinking we're snobs just because some other CPU has
* disabled global interrupts (we have already done the
* INT_ACK cycles, it's too late to try to pretend to the
* controller that we aren't taking the interrupt).
*
* 0 return value means that this irq is already being
* handled by some other CPU. (or is disabled)
*/
int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code */
int cpu = smp_processor_id();
irq_desc_t *desc = irq_desc + irq;
struct irqaction * action;
unsigned int status;
kstat.irqs[cpu][irq]++;
[4] spin_lock(&desc->lock);
desc->handler->ack(irq);
/*
REPLAY is when Linux resends an IRQ that was dropped earlier
WAITING is used by probe to mark irqs that are being tested
*/
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
status |= IRQ_PENDING; /* we _want_ to handle it */
/*
* If the IRQ is disabled for whatever reason, we cannot
* use the action we have.
*/
action = NULL;
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status &= ~IRQ_PENDING; /* we commit to handling */
status |= IRQ_INPROGRESS; /* we are handling it */
}
desc->status = status;
/*
* If there is no IRQ handler or it was disabled, exit early.
Since we set PENDING, if another processor is handling
a different instance of this same irq, the other processor
will take care of it.
*/
if (!action)
goto out;
/*
* Edge triggered interrupts need to remember
* pending events.
* This applies to any hw interrupts that allow a second
* instance of the same irq to arrive while we are in do_IRQ
* or in the handler. But the code here only handles the _second_
* instance of the irq, not the third or fourth. So it is mostly
* useful for irq hardware that does not mask cleanly in an
* SMP environment.
*/
for (;;) {
[5] spin_unlock(&desc->lock);
handle_IRQ_event(irq, ®s, action);
[6] spin_lock(&desc->lock);
if (!(desc->status & IRQ_PENDING))
break;
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
out:
/*
* The ->end() handler has to deal with interrupts which got
* disabled while the handler was running.
*/
desc->handler->end(irq);
[7] spin_unlock(&desc->lock);
if (softirq_pending(cpu))
do_softirq();
return 1;
}
在setup_irq()中,因為其他CPU可能同時在運行setup_irq(),或者在運行setup_irq()時,
本地irq中斷來了,要執行do_IRQ()以修改desc->status。為了同時防止來自其他CPU和
本地irq中斷的干擾,如[1][2][3]處所示,使用了spin_lock_irqsave/spin_unlock_irqrestore()
而在do_IRQ()中,因為do_IRQ()本身是在中斷中,而且此時還沒有開中斷,本CPU中沒有
什麼可以中斷其運行,其他CPU則有可能在運行setup_irq(),或者也在中斷中,但這二者
對本地do_IRQ()的影響沒有區別,都是來自其他CPU的干擾,因此只需要用spin_lock/spin_unlock,
如[4][5][6][7]處所示。值得注意的是[5]處,先釋放該spinlock,再調用具體的響應函數。
再舉個例子:
static void tasklet_hi_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
[8] local_irq_disable();
list = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = NULL;
[9] local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
[10] local_irq_disable();
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
[11] local_irq_enable();
}
}
這裡,對tasklet_hi_vec[cpu]的修改,不存在CPU之間的競爭,因為每個CPU有各自獨立的數據,
所以只要防止irq的干擾,用local_irq_disable/local_irq_enable即可,如[8][9][10][11]處所示。...
read more
2007年10月28日 星期日
2007年10月26日 星期五
[+/-] : 自己寫Netfilter匹配器(iptables module)
相當實用的一篇文章, 感謝作者Nicolas Bouliane,
http://www.linuxfocus.org/ChineseGB/February2005/article367.shtml
...
read more
http://www.linuxfocus.org/ChineseGB/February2005/article367.shtml
...
read more
2007年8月23日 星期四
[+/-] : 有趣的 script (二)
1.將 file.txt 裏的123改爲 456...
sed -i 's/123/456/g' file.txt 直接修改原文件
2.lsof 用法小全
lsof abc.txt 顯示開啓文件 abc.txt 的進程
lsof -i :22 知道 22 埠現在運行什麽程式
lsof -c nsd 顯示 nsd 進程現在打開的文件
lsof -g gid 顯示歸屬 gid 的進程情況
lsof +d /usr/local/ 顯示目錄下被進程開啓的文件
lsof +D /usr/local/ 同上,但是會搜索目錄下的目錄,時間較長
lsof -d 4 顯示使用 fd 爲4 的進程
lsof -i [i] 用以顯示符合條件的進程情況
lsof -i[46] [protocol][@hostname|hostaddr][:service|port]
46 --> IPv4 or IPv6
protocol --> TCP or UDP
hostname --> Internet host name
hostaddr --> IPv4 位置
service --> /etc/service中的 service name (可以不止一個)
port --> 埠號(可以不止一個)
例子: TCP:25 - TCP and port 25
@1.2.3.4 - Internet IPv4 host address 1.2.3.4
tcp@www.ccu.edu.tw:ftp
(TCP protocol host:www.ccu.edu.tw service name:ftp)
lsof -n 不將 IP轉換爲 hostname,預設是不加上-n參數
例子: lsof -i tcp@ohaha.ks.edu.tw:ftp -n
lsof -p 12 看進程號爲 12的進程打開了哪些文件
3.將 top的結果輸出到文件中
top -d 2 -n 3 -b >test.txt
把 top 的結果每隔 2秒,列印 3次,這樣後面頁的進程也能夠看見了
4.過濾掉#號打頭的行,和所有的空行(對於查看配置文檔很有用)
awk '/^[^#]/&&/^[^$]/' filename
5.利用現存兩個文件,生成一個新的文件
1) 取出兩個文件的並集(重復的行只保留一份)
cat file1 file2 | sort | uniq
2) 取出兩個文件的交集(只留下同時存在於兩個文件中的文件)
cat file1 file2 | sort | uniq -d
3) 刪除交集,留下其他的行
cat file1 file2 | sort | uniq -u
6.tr高級用法(必看)
7.Unix系列shell程式編寫從入門到精通
8.aaa這檔案內容為 $* $# $1 $$
# i=0;for j in `sed '1 !d' aaa`;do
> i=`expr $i + 1`
> eval A$i='$j';done
# echo $A1 $A2 $A3 $A4
# $* $# $1 $$
read more
2007年8月17日 星期五
[+/-] : 目前想要學的語言 qt
看到以下的qt入門範例 , 只能說就像用netbean 來寫java swing一樣
同樣也是 視窗化 + 跨平台 + 物件導向
不過c++總比 java 讓我好過一點...
網路上可供入門的資料如下
qt入門範例 (網友版 - 繁體中文)
qt turtorial (trolltech - english)
qt 4.0 overall (trolltech - english)...
read more
同樣也是 視窗化 + 跨平台 + 物件導向
不過c++總比 java 讓我好過一點...
網路上可供入門的資料如下
qt入門範例 (網友版 - 繁體中文)
qt turtorial (trolltech - english)
qt 4.0 overall (trolltech - english)...
read more
2007年8月16日 星期四
[+/-] : 有趣的 script (一)
有趣的 script (一)
張貼者: Issaac 位於 上午 2:07
1. 每三小按一次空白鍵
until false;do expect -c 'send " ";sleep 3';done
2. 送出ctrl + c中斷程式a
kill -s 2 $(pidof a)
3. 從a.txt檔案找到並輸入帳號b的密碼
passwd b --stdin < a.txt
4. 一個process 要怎樣偵測出自己是否在背景狀態執行?
if [ -t 0 ]; then ... fi #C語言的寫法為 if(isatty(0)) { ... }
5. 用帳號 aaa 密碼 bbb 用程式 AutoLogin 自動登入telnet
#!/bin/sh
# Usage: ./AutoLogin aaa bbb
set password $2
spawn passwd $1
expect "*password:"
send "$password\r"
expect "*password:"
send "$password\r"
expect eof
6.Command 太長直接跳到底 , 輸入 ctrl + e
跳到開頭, 輸入 ctrl + a...
read more
張貼者: Issaac 位於 上午 2:07
1. 每三小按一次空白鍵
until false;do expect -c 'send " ";sleep 3';done
2. 送出ctrl + c中斷程式a
kill -s 2 $(pidof a)
3. 從a.txt檔案找到並輸入帳號b的密碼
passwd b --stdin < a.txt
4. 一個process 要怎樣偵測出自己是否在背景狀態執行?
if [ -t 0 ]; then ... fi #C語言的寫法為 if(isatty(0)) { ... }
5. 用帳號 aaa 密碼 bbb 用程式 AutoLogin 自動登入telnet
#!/bin/sh
# Usage: ./AutoLogin aaa bbb
set password $2
spawn passwd $1
expect "*password:"
send "$password\r"
expect "*password:"
send "$password\r"
expect eof
6.Command 太長直接跳到底 , 輸入 ctrl + e
跳到開頭, 輸入 ctrl + a...
read more
2007年7月20日 星期五
[+/-] : Qos module
...
sch_ingress.c
http://bbs.chinaunix.net/viewthread.php?tid=849145&highlight=qtdszws
使用ingress的內核處理流程
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: pref 10 protocol ip u32
1.init_module->nf_register_hook
註冊ingress的鉤子到
static struct nf_hook_ops ing_ops =
{
{ NULL, NULL},
ing_hook,
PF_INET,// 2
NF_IP_PRE_ROUTING,//註冊到PRE_ROUTING
NF_IP_PRI_FILTER + 1
};
2.網卡接收到一個ip資料包後調用ip_input.c中的ip_rcv
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish)
NF_HOOK定義如下
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
(list_empty(&nf_hooks[(pf)][(hook)]) \
? (okfn)(skb) \
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
因此將調用nf_hook_slow,在core/netfilter.c中
nf_hook_slow->nf_iterate-> hook-> ing_hook(ingress的鉤子函數)-> ingress_enqueue(入隊)-> tc_classify(分類)
3.根據分類結果決定繼續處理該包,還是丟棄(例如被管制器管制)
======================================================================
sch_teql.c
teql原理
1.在Linux的高級路由和流量控制HOWTO.pdf有一些說明,可以參照
2.teql實現了一個簡單的多網卡負載均衡.簡單,是因為它不考慮實際的硬體情況,只是在關聯的實際設備之間輪轉轉發包,即使是一個千兆網卡和MODEM被關聯在一起.可以稱做發包數負載均衡吧.
3.insmod sch_teql.o時, init_module會註冊一個名為teql0的虛網路介面和一個名為teql0的佇列規程,他們被集成在struct teql_master結構中,彼此分不開.
可以使用一下命令綁定實設備到佇列規程teql0
tc qdisc add dev eth0 root teql0
tc qdisc add dev eth1 root teql0
然後用命令ip link set dev teql0 up啟動虛設備teql0
這樣一個teql設備就配置完成了
接下來可以再配置另一個teql設備
insmod –o sch_teql1 sch_teql.o
我這是RED HAT 7.2,由於已經載入了sch_teql.o模組,只能重命名載入了.
新版本內核已經實現一次insmod就可指定若干個teql
tc qdisc add dev eth2 root teql1
tc qdisc add dev eth3 root teql1
ip link set dev teql1 up
4.當有資料包到達teql0時先進入自己的佇列,缺省為pfifo_fast,然後內核就調用teql_master_xmit來在從設備中迴圈輪轉選擇一個設備eth*發包.如果teql*是該實設備的根佇列規程,就直接調用eth*的hard_start_xmit函數發包.如果不是就選擇下一個設備,只至成功或失敗.而配置在該設備上的teql*規程,它的行為就類似於pfifo規程.這是給那些直接通過該設備發送的包準備的規程.
teql.rar
======================================================================
sch_red.c random early detection
隨機早期探測
參考http://www.icir.org/floyd/papers/red/red.html 中的early.pdf
說明
1.qlen 為當前佇列長度
2.qave 平均佇列長度,它的計算公式是qave=qave*(1-W)+qlen*W,W為權重,W一般選得很小 ,這可以使突發包對qave不會產生太大波動
3.qmin,qmax 當qmin<=qave<=qmax時,開始隨機標記/丟包,標記/丟包的概率為 max_P * (qave- qmin)/(qmax-qmin),,隨著qave增長,概率逼近max_P 標記包是希望用戶端知道目前網路已經開始擁擠,放慢發包速度吧 如果qave>=qmax,標記/丟棄所有的包
4.如果網路空閒了一段時間,就應該相應地減少qave的大小,qave=qave*(1-W)^m,m為空閒時間段長度
sch_red.rar
路由分類器cls_route.c
路由分類器的原理很簡單,就是在路由的時候由路由規則對資料包打標記.路由分類器就用這個標記來對資料包分類.
路由規則語法如下
ip route add Host/Network via Gateway dev Device realm RealmNumber
例
ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10
至於如何打標記,我現在還不瞭解
在設置了路由規則以後,就可以使用路由分類器分類資料了
tc filter add dev eth0 protocol ip prio 100 route to 10 classid :2
tc filter add dev eth0 protocol ip prio 100 route from 10 classid :1
路由分類器參考的參數有fromdev/from,to,fromdev和from互斥,只能使用其中一個,由他們在分類器的二層hash表中索引和查找.
資源預留協議分類器cls_rsvp.h
資源預留協定分類器的原理也很簡單,就是用ip協定(也支援ipv6,此處不涉及)的目的地址,可能的目的埠,源位址,可能的源埠來索引和查找一個二層hash表.
"可能的"意思是不一定使用,由用戶決定.也就是它是基於session(會話)的.
tc filter add dev eth0 pref 10 protocol ip rsvp ipproto tcp session 10.1.1.1/80 classid :1
這條規則的意思就是,到10.1.1.1:80的資料流程分類到:1的佇列
還可以加入sender,指定發送者,或者不指定埠,而指定一個GPI,也就是在offset處的值&mask==key方式更加靈活地指定一個匹配.
route_rsvp.rar
sch_dsmark.c和cls_tcindex.c
diffserv是一個體系,需要一個有若干個路由和一定規模的網路來協作實現,就單個主機來說用處不大.在這個網路中,大家都遵守同樣的diffserv協定,例如哪個dsfield的優先順序高,哪個低,以及怎樣處理等.
這裏引入了域的概念,就是遵守某一diffserv協定的網路組成一個ds域.剩下的就是非ds域.一台域中的主機要發的包,可以在自己的出口佇列分類,或在接入的路由上分類.
1.在本機分類,出口綁定dsmark佇列規程和u32等分類器
2.在路由分類,路由入口綁定ingress佇列規程和u32等分類器
注:上面的分類器一般不用tcindex分類器,因為是從非ds域到ds域的轉換,而tcindex實用於使用已有ds field來分類流(見3)和不同ds域之間的轉換,不同域之間的轉換一般發生在入口上,例如上面的2,如果資料是從另外一個ds域來的話.
這樣所有的流就被區分開了.
3.然後路由器就可以在自己的出口綁定dsmark佇列規程(和一些內部佇列,例如cbq)和tcindex分類器,讓tcidnex分類來對不同級別的流(只根據ds field)進行整形和qos.
上面都是我的理解,化了我很長時間,不對之處,請大家指正.大家參考lartc上的說明.
下面的例子也是摘之lartc
tc qdisc add dev eth0 handle 1:0 root dsmark indices 64 set_tc_index#綁定dsmark佇列規程
tc filter add dev eth0 parent 1:0 protocol ip prio 1 tcindex mask 0xfc shift 2#建立tcindex分類器
tc qdisc add dev eth0 parent 1:0 handle 2:0 cbq bandwidth 10Mbit cell 8 avpkt 1000 mpu 64 # EF traffic class內部佇列
tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated allot 1514 weight 1 maxburst 10 # Packet fifo qdisc for EF traffic子類
tc qdisc add dev eth0 parent 2:1 pfifo limit 5 #子類的佇列規程
tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 0x2e tcindex classid 2:1 pass_on #例子中是parent 2:0,我認為是parent 1:0,把EF流分類到2:1子類,不懂EF流是怎麼回事,半桶水^_^
sch_gred.c Generic Random Early Detection
這個演算法是在red的基礎上擴展引入virtual queue的概念形成的.
在gred中最多可以引入16個vq,其中至少有一個缺省vq.
每個vq都基本按red演算法來操作(有區別),但所有的這些vq都公用一個實際的佇列
sch->q.
gred演算法有四種模式,由參數eqp和grio控制(不知道是什麼的縮寫)
(注意是否開始隨機丟包由qave+q->qaveqth_min控制)
eqp grio
0 0 每個vq一個獨立red (qave=0),但共用一個實佇列
0 1 每個vq和比它優先順序高的vq構成一個部分相關red,保證優先順序高的vq優先發包
qave+q->qave的值是按照相關的每個vq自己計算的ave的總和
1 0 每個vq一個部分相關red,和sch->backlog相關 (qave=0)
q->qave的值是把sch->backlog按本vq的方式計算ave的值
1 1 每個vq一個全部相關red (qave=0)
q->qave的值是把sch->backlog按本vq的方式計算ave的累計總和
我認為比較有用的是(0,0)(有點類似於sfq)和(0,1)(有點類似於prio)
gred實際上和red一樣都比較難配置,主要應用于路由器上
因為它是採用skb->tc_index來選擇vq的,可以和dsmark規程和tcindex分類器協作
gred.rar
estimator.c和police.c
--------------------------------------------------------------
estimator用於估計包的速度,包括bps(每秒位元組數)和pps(每秒包數).
estimator的調用獨立於qos架構,每個estimator一個內核計時器,可以每1/4s,1/2s,1s,2s,4s,8s被調用一次,定時計算
它可以應用在佇列規程上和分類器上.
1.在佇列規程上,它把計算結果放在qdisc->stats中,到目前為止我還沒有看到誰在使用這個結果,不過這可以讓用戶監視和評估該規程上的流量
2.在分類器上,在分類器的每個策略中都有一個tcf_police結構,估計結果就放入該結構的stats成員中.在策略被命中後,將調用tcf_police,如果使用了估計器,tc_police就根據估計結果決定通過該策略的資料流程量是否超限,是的話就執行規定的動作,ok,drop,reclassify,unspec.
Usage: ... estimator INTERVAL TIME-CONST
interval為定時間隔,time-const的計算方法是w=interval/time-const,w為加權比,例est 1s 8s,則定時間隔為1s,w=1/8=0.125
------------------------------------------------------------------
police用於分類器上,前面已經提到,策略被命中就會調用tcf_police,對通過該策略資料的進行管制
除了使用可選的estimator的結果進行管制以外,police主要使用tbf(權杖桶篩檢程式)對流進行管制操作.tbf的理論在前面已經敍述過了.tbf使用常規桶和峰值桶來控制流量.對於超限的包執行規定的動作.
Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]
[ peakrate BPS ] [ avrate BPS ]
[ ACTION ]
Where: ACTION := reclassify drop continue
avrate用於estimator,判斷流量是否超限
lartc上有一個"防護SYN洪水攻擊例子"用到police
iptables -A PREROUTING -i $INDEV -t mangle -p tcp --syn -j MARK --set-mark 1
$TC qdisc add dev $INDEV handle ffff: ingress
$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw
police rate 1kbit burst 40 mtu 9k drop flowid :1
read more
訂閱:
文章 (Atom)