關於核心執行緒(kernel_thread)(轉)
關於核心執行緒(kernel_thread)(轉)[@more@]我們知道Linux核心使用核心執行緒來將核心分成幾個功能模組,
像kswapd,kflushd等,系統中的init程式也是由idle程式呼叫
kernel_thread()來實現產生的.
我們先來看看核心執行緒的實現,再來分析核心執行緒的性質.
int kernel_thread(int(*fn)(void*arg),void *arg,int flags)
{
long retval,d0; /* 至少是兩個區域性變數 */
__asm__ __volitate__(
"movl %%esp,%%esint"
"int $0x80nt"
"cmpl %%esp,%%esint"
"je 1f nt"
"movl %4,%%eaxnt"
"pushl %%eaxnt"
"call *%5nt"
"movl %3,%0nt"
"int $0x80nt"
"1:t"
:"=&a"(retval),"=&S"(d0)
:"0"(__NR_clone),"i"(__NR_exit),
"r"(arg),"r"(fn),
"b"(flags | CLONE_VM)
:"memory"
);
return retval;
}
這段程式碼翻譯成直觀的ASM碼:
{
movl __NR_clone,%0; /* 將clone的系統呼叫號載入reg */
movl __NR_exit,%3; /* 將exit的系統呼叫號載入reg */
movl arg,%4; /* 將函式fn的引數載入reg */
movl fn,%5; /* 將函式fn的指標載入reg */
movl flags|CLONE_VM,%ebx; /* 將flags移入%ebx中 */
mov %%esp,%%esi; /* 將暫存器%esp儲存在%esi中 */
int $0x80; /* 由於%eax中是clone的系統呼叫號,所以
sys_clone會被呼叫,同時又由於系統呼叫門
會把所有的暫存器壓棧,所以子程式會在相應
的暫存器中獲取flags,fn,__NR_exit等,系統呼叫
返回後,子程式幾乎繼承了父程式的一切,但是由
我們對do_fork的分析可知,子程式將獲取新的核心棧,
棧上就是各暫存器的內容,同時修改TSS使:
EIP=ret_from_fork,
ESP=新的核心棧底-sizeof(pt_regs),
SSO=__KERNEL_DS,
ESP0=新的核心棧頂,
(??)修改棧上的OLDESP=新的核心棧底
子程式恢復執行後,載入eip,esp,當RESTORE_ALL執行後
(pops,iret),暫存器被恢復了,同時我們知道當前的ESP
與ESI已是不同了.
*/
cmpl %%esp,%%esi;
je 1f; /* %esp,%esi相同,則是父程式 */
movl %4,%%eax; /* 將引數載入EAX,這樣不管fn是否使用-mregparam屬性
參看GCC manual for more information
*/
pushl %%eax /* 將引數壓棧 */
call *%5; /* 呼叫fn */
movl %3,%0;
int $0x80; /* 系統呼叫exit退出 */
1: movl %%eax,retval /* 將子程式的pid付給retval(系統呼叫的返回值在%eax中) */
movl %%esi,d0 /* ?? */
}
它的偽C碼為:
int kernel_thread()
{
pid=clone(flags);
if(child)
{
fn(arg);
exit(0);
}
return pid;
}
從上面的程式碼可以看出,核心執行緒有以下性質:
1.
核心執行緒是透過系統呼叫clone()來實現的,使用CLONE_VM標誌(使用者還可以
提供其他標誌,CLONE_PID,CLONE_FS,CLONE_FILES等),因此核心執行緒與呼叫
的程式(current)具有相同的程式空間.
2.
由於呼叫程式是在核心裡呼叫kernel_thread(),因此當系統呼叫返回時,子程式也處於
核心態中,而子程式隨後呼叫fn,當fn退出時,子程式呼叫exit()退出,所以子程式是在
核心態執行的.
3.
由於核心執行緒是在核心態執行的,因此核心執行緒可以訪問核心中資料,呼叫核心函式.
執行過程中不能被搶佔等等.
請注意在kernel_thread是如何呼叫系統呼叫的,我們知道kernel_thread是在核心中
呼叫,所以他是可以直接呼叫系統呼叫的,像sys_open()等,但是在這裡kernel_thread
透過系統呼叫門(int$80)來間接呼叫clone()函式,就提出以下問題:
1.為什麼這樣?
2.如果我們直接呼叫sys_clone()會有什麼樣的結果呢?
int kernel_thread()
{
int pid;
pid=sys_clone();
if(!pid)
{
/* child */
exit();
}
return pid;
}
這樣,當子程式獲取CPU資源時(執行時),從ret_from_fork恢復執行,棧佈局對於子程式而言
是不對的,問題在於當子程式執行到RESTORE_ALL的IRET,仔細想一想棧佈局的變化.
由sys_clone()的申明可知呼叫sys_clone需要pt_regs的棧結構,如果我們直接呼叫sys_clone
是沒用辦法做到的(如果可以我們也需要精心為它準備棧,//:-(,真是傷神)
同理,其他的類似系統呼叫,我們也必須透過int$80的系統呼叫門來實現.
而對於sys_execl,sys_open,sys_close,sys_exit,則可以直接呼叫.//xixi,我們可以
改動kernel_thread來測試sys_exit是否可以直接呼叫,同時也可以使用sys_clone的直接呼叫
來證明我們的分析是否正確.
而如果我們使用系統呼叫門(int$80)來解決問題,我們使用同樣的方法來分析:
A2)
ebx ecx
...
oldeip oldcs
eflags
d0 retval
fn arg
clone_flags
eip ..
由於kernel_thread在核心的程式碼段中,所以沒有發生棧切換,所有的壓棧/退棧都是在
核心棧中進行的.請注意這樣棧中便沒有(OLDSS,OLDESP),所以在kernel_thread宣告瞭
兩個區域性引數(retval,d0),對於retval的意義是明顯的,而d0大概是(dummy local
variable
0,...n)的意思吧,:)
B2)子程式執行前:
子程式的TSS,棧佈局
ebx ecx
...
oldeip
oldcs
eflags
d0 retval
執行到RESTORE_ALL時,將恢復CPU各暫存器,當執行到IRET時,
由於在相同特權等級的轉移,所以沒有發生特權級切換,所以ESP,SS沒有發生變化.
BTW,由上面的分析可知,kernel_thread建立的程式是不能轉到使用者態執行的。
像kswapd,kflushd等,系統中的init程式也是由idle程式呼叫
kernel_thread()來實現產生的.
我們先來看看核心執行緒的實現,再來分析核心執行緒的性質.
int kernel_thread(int(*fn)(void*arg),void *arg,int flags)
{
long retval,d0; /* 至少是兩個區域性變數 */
__asm__ __volitate__(
"movl %%esp,%%esint"
"int $0x80nt"
"cmpl %%esp,%%esint"
"je 1f nt"
"movl %4,%%eaxnt"
"pushl %%eaxnt"
"call *%5nt"
"movl %3,%0nt"
"int $0x80nt"
"1:t"
:"=&a"(retval),"=&S"(d0)
:"0"(__NR_clone),"i"(__NR_exit),
"r"(arg),"r"(fn),
"b"(flags | CLONE_VM)
:"memory"
);
return retval;
}
這段程式碼翻譯成直觀的ASM碼:
{
movl __NR_clone,%0; /* 將clone的系統呼叫號載入reg */
movl __NR_exit,%3; /* 將exit的系統呼叫號載入reg */
movl arg,%4; /* 將函式fn的引數載入reg */
movl fn,%5; /* 將函式fn的指標載入reg */
movl flags|CLONE_VM,%ebx; /* 將flags移入%ebx中 */
mov %%esp,%%esi; /* 將暫存器%esp儲存在%esi中 */
int $0x80; /* 由於%eax中是clone的系統呼叫號,所以
sys_clone會被呼叫,同時又由於系統呼叫門
會把所有的暫存器壓棧,所以子程式會在相應
的暫存器中獲取flags,fn,__NR_exit等,系統呼叫
返回後,子程式幾乎繼承了父程式的一切,但是由
我們對do_fork的分析可知,子程式將獲取新的核心棧,
棧上就是各暫存器的內容,同時修改TSS使:
EIP=ret_from_fork,
ESP=新的核心棧底-sizeof(pt_regs),
SSO=__KERNEL_DS,
ESP0=新的核心棧頂,
(??)修改棧上的OLDESP=新的核心棧底
子程式恢復執行後,載入eip,esp,當RESTORE_ALL執行後
(pops,iret),暫存器被恢復了,同時我們知道當前的ESP
與ESI已是不同了.
*/
cmpl %%esp,%%esi;
je 1f; /* %esp,%esi相同,則是父程式 */
movl %4,%%eax; /* 將引數載入EAX,這樣不管fn是否使用-mregparam屬性
參看GCC manual for more information
*/
pushl %%eax /* 將引數壓棧 */
call *%5; /* 呼叫fn */
movl %3,%0;
int $0x80; /* 系統呼叫exit退出 */
1: movl %%eax,retval /* 將子程式的pid付給retval(系統呼叫的返回值在%eax中) */
movl %%esi,d0 /* ?? */
}
它的偽C碼為:
int kernel_thread()
{
pid=clone(flags);
if(child)
{
fn(arg);
exit(0);
}
return pid;
}
從上面的程式碼可以看出,核心執行緒有以下性質:
1.
核心執行緒是透過系統呼叫clone()來實現的,使用CLONE_VM標誌(使用者還可以
提供其他標誌,CLONE_PID,CLONE_FS,CLONE_FILES等),因此核心執行緒與呼叫
的程式(current)具有相同的程式空間.
2.
由於呼叫程式是在核心裡呼叫kernel_thread(),因此當系統呼叫返回時,子程式也處於
核心態中,而子程式隨後呼叫fn,當fn退出時,子程式呼叫exit()退出,所以子程式是在
核心態執行的.
3.
由於核心執行緒是在核心態執行的,因此核心執行緒可以訪問核心中資料,呼叫核心函式.
執行過程中不能被搶佔等等.
請注意在kernel_thread是如何呼叫系統呼叫的,我們知道kernel_thread是在核心中
呼叫,所以他是可以直接呼叫系統呼叫的,像sys_open()等,但是在這裡kernel_thread
透過系統呼叫門(int$80)來間接呼叫clone()函式,就提出以下問題:
1.為什麼這樣?
2.如果我們直接呼叫sys_clone()會有什麼樣的結果呢?
int kernel_thread()
{
int pid;
pid=sys_clone();
if(!pid)
{
/* child */
exit();
}
return pid;
}
這樣,當子程式獲取CPU資源時(執行時),從ret_from_fork恢復執行,棧佈局對於子程式而言
是不對的,問題在於當子程式執行到RESTORE_ALL的IRET,仔細想一想棧佈局的變化.
由sys_clone()的申明可知呼叫sys_clone需要pt_regs的棧結構,如果我們直接呼叫sys_clone
是沒用辦法做到的(如果可以我們也需要精心為它準備棧,//:-(,真是傷神)
同理,其他的類似系統呼叫,我們也必須透過int$80的系統呼叫門來實現.
而對於sys_execl,sys_open,sys_close,sys_exit,則可以直接呼叫.//xixi,我們可以
改動kernel_thread來測試sys_exit是否可以直接呼叫,同時也可以使用sys_clone的直接呼叫
來證明我們的分析是否正確.
而如果我們使用系統呼叫門(int$80)來解決問題,我們使用同樣的方法來分析:
A2)
ebx ecx
...
oldeip oldcs
eflags
d0 retval
fn arg
clone_flags
eip ..
由於kernel_thread在核心的程式碼段中,所以沒有發生棧切換,所有的壓棧/退棧都是在
核心棧中進行的.請注意這樣棧中便沒有(OLDSS,OLDESP),所以在kernel_thread宣告瞭
兩個區域性引數(retval,d0),對於retval的意義是明顯的,而d0大概是(dummy local
variable
0,...n)的意思吧,:)
B2)子程式執行前:
子程式的TSS,棧佈局
ebx ecx
...
oldeip
oldcs
eflags
d0 retval
執行到RESTORE_ALL時,將恢復CPU各暫存器,當執行到IRET時,
由於在相同特權等級的轉移,所以沒有發生特權級切換,所以ESP,SS沒有發生變化.
BTW,由上面的分析可知,kernel_thread建立的程式是不能轉到使用者態執行的。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-949652/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於多執行緒(轉)執行緒
- (轉貼)關於程式和執行緒 (轉)執行緒
- 關於“UI執行緒”UI執行緒
- 核心執行緒執行緒
- 關於多執行緒控制執行緒
- 關於執行緒的講解(出自Java原著)(轉)執行緒Java
- 關於執行緒的問題...執行緒
- 關於多執行緒的一些細節 (轉)執行緒
- 關於redis單執行緒的分析Redis執行緒
- 關於執行緒設計的感受執行緒
- java基礎 關於執行緒安全Java執行緒
- 關於執行緒池的面試題執行緒面試題
- 證明執行緒池ThreadPoolExecutor的核心執行緒數,最大執行緒數,佇列長度的關係執行緒thread佇列
- Linux Kernel 2.6 核心執行緒嚐鮮(轉)Linux執行緒
- java多執行緒核心api以及相關概念(一)Java執行緒API
- 執行緒 (轉)執行緒
- 多執行緒核心技術(1)-執行緒的基本方法執行緒
- 關於執行緒的幾個函式執行緒函式
- 關於 Python 多執行緒/多程式Python執行緒
- 關於linux的執行緒實現Linux執行緒
- 小小問題―關於java多執行緒Java執行緒
- 關於Python多執行緒的理解Python執行緒
- 跪求關於〖執行緒遷移〗的相關資料!執行緒
- 關於js執行緒問題的解讀JS執行緒
- Java 關於執行緒的一些使用Java執行緒
- 關於多執行緒協作的疑問執行緒
- Java執行緒池核心原理剖析Java執行緒
- 執行緒池核心原理淺析執行緒
- 執行緒池中的最大執行緒數、核心執行緒數和佇列大小的合理設定執行緒佇列
- python多執行緒中:如何關閉執行緒?Python執行緒
- 執行緒池相關執行緒
- 玩轉java多執行緒 之多執行緒基礎 執行緒狀態 及執行緒停止實戰Java執行緒
- 關於iOS多執行緒通訊的相關總結iOS執行緒
- java 多執行緒(關於Thread的講解)Java執行緒thread
- python關於執行緒的一點介紹Python執行緒
- 關於多執行緒的兩種實現方式執行緒
- 關於Numba的執行緒實現的說明執行緒
- 關於iOS多執行緒,你看我就夠了iOS執行緒