欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
內核等待隊列機制介紹
內核等待隊列機制介紹


相信很多寫(xiě)程序的人都寫(xiě)過(guò) socket 的程序。當我們 open 一個(gè) socket 之后,接著(zhù)去
讀取這個(gè) socket,如果此時(shí)沒(méi)有任何資料可供讀取,那 read 就會(huì ) block 住。(這是
沒(méi)有加上 O_NONBLOCK 的情形),直到有資料可讀取才會(huì )傳回來(lái)。在 Linux kernel 里
有一個(gè)數據結構可以幫助我們做到這樣的功能。這個(gè)數據結構就是這里要跟各位介紹的
wait queue。在 kernel 里,wait_queue 的應用很廣,舉凡 device driver
semaphore 等方面都會(huì )使用到 wait_queue 來(lái) implement。所以,它算是 kernel 里蠻
基本的一個(gè)數據結構。

接下來(lái),我要跟各位介紹一下 wait_queue 的用法,以及用一個(gè)例子來(lái)說(shuō)明如何使用
wait_queue。最后,我會(huì )帶各位去 trace 一下 wait_queue 的原始程序代碼,看看
wait_queue 是如何做到的。

我想有件事要先提及的是 Linux 在 user space 跟在 kernel space 上的差異。我們
知道 Linux 是 multi-tasking 的環(huán)境,同時(shí)可以有很多人執行很多的程序。這是從
user 的觀(guān)點(diǎn)來(lái)看的。如果就 kernel 的觀(guān)點(diǎn)來(lái)看,是沒(méi)有所謂的 multi-tasking 的。
在 kernel 里,只有 single-thread。也就是說(shuō),如果你的 kernel code 正在執行,
那系統里只有那部分在執行。不會(huì )有另一部分的 kernel code 也在運作。當然,這是
指 single processor 的情況下,如果是 SMP 的話(huà),那我就不清楚了。我想很多人都
在 Windows 3.1 下寫(xiě)過(guò)程序,在那種環(huán)境下寫(xiě)程序,每一個(gè)程序都必須適當的將 CPU
讓給別的程序使用。如果有個(gè)程序里面有一個(gè)

while (1);

的話(huà),那保證系統就停在那里了。這種的多任務(wù)叫做 non-preemptive。它多任務(wù)的特
性是由各個(gè)程序相互合作而造成的。在 Linux 的 user space 下,則是所謂的
preemptive,各個(gè) process 喜歡執行什么就執行什么,就算你在你的程序里加上
while(1); 這一行也不會(huì )影響系統的運作。反正時(shí)間到了,系統自動(dòng)就會(huì )將你的程序停
住,讓別的程序去執行。這是在 user space 的情況下,在 kernel 這方面,就跟
Windows 3.1 程序是一樣的。在 kernel 里,你必須適當的將 CPU 的執行權釋放出
來(lái)。如果你在 kernel里加入 while(1); 這一行。那系統就會(huì )跟 Windows 3.1 一樣。
卡在那里。當然啦,我是沒(méi)試過(guò)這樣去改 kernel,有興趣的人可以去試試看,如果有
不同的結果,請記得告訴我。

假設我們在 kernel 里產(chǎn)生一個(gè) buffer,user 可以經(jīng)由 read,write 等 system
call 來(lái)讀取或寫(xiě)資料到這個(gè) buffer 里。如果有一個(gè) user 寫(xiě)資料到 buffer 時(shí),此
時(shí) buffer 已經(jīng)滿(mǎn)了。那請問(wèn)你要如何去處理這種情形呢 ? 第一種,傳給 user 一個(gè)
錯誤訊息,說(shuō) buffer 已經(jīng)滿(mǎn)了,不能再寫(xiě)入。第二種,將 user 的要求 block 住,
等有人將 buffer 內容讀走,留出空位時(shí),再讓 user 寫(xiě)入資料。但問(wèn)題來(lái)了,你要怎
么將 user 的要求 block 住。難道你要用

while ( is_full );
write_to_buffer;

這樣的程序代碼嗎? 想想看,如果你這樣做會(huì )發(fā)生什么事? 第一,kernel會(huì )一直在這個(gè)
while 里執行。第二個(gè),如果 kernel 一直在這個(gè) while 里執行,表示它沒(méi)有辦法去
maintain系統的運作。那此時(shí)系統就相當于當掉了。在這里 is_full 是一個(gè)變量,當
然,你可以讓 is_full 是一個(gè) function,在這個(gè) function里會(huì )去做別的事讓 kernel
可以運作,那系統就不會(huì )當。這是一個(gè)方式。但是,如果我們使用 wait_queue 的話(huà),
那程序看起來(lái)會(huì )比較漂亮,而且也比較讓人了解,如下所示:


struct wait_queue *wq = NULL; /* global variable */
while ( is_full )
interruptible_sleep_on( &wq );
}
write_to_buffer();

interruptible_sleep_on( &wq ) 是用來(lái)將目前的 process,也就是要求寫(xiě)資料到
buffer 的 process放到 wq 這個(gè) wait_queue 里。在 interruptible_sleep_on 里,
則是最后會(huì )呼叫 schedule() 來(lái)做 schedule 的動(dòng)作,也就是去找另一個(gè) process 來(lái)
執行以維持系統的運作。當執行完 interruptible_sleep_on 之后,要求 write 的
process 就會(huì )被 block 住。那什么時(shí)候會(huì )恢復執行呢 ? 這個(gè) process 之所以會(huì )被
block 住是因為 buffer 的空間滿(mǎn)了,無(wú)法寫(xiě)入。但是如果有人將 buffer 的資料讀取
掉,則 buffer 就有空間可以讓人寫(xiě)入。所以,有關(guān)于叫醒 process 的動(dòng)作應該是在
read buffer 這方面的程序代碼做的。

extern struct wait_queue *wq;
if ( !is_empty )
{
read_from_buffer();
wake_up_interruptible( &wq );
}
...

以上的程序代碼應該要放在 read buffer 這部分的程序代碼里,當 buffer 有多余的
空間時(shí),我們就呼叫 wake_up_interruptible( &wq ) 來(lái)將掛在 wq 上的所有 process
叫醒。請記得,我是說(shuō)將 wq 上的所有 process 叫醒,所以,如果如果有10個(gè)
process 掛在 wq 上的話(huà),那這 10 個(gè)都會(huì )被叫醒。之后,至于誰(shuí)先執行。則是要看
schedule 是怎么做的。就是因為這 10 個(gè)都會(huì )被叫醒。如果 A 先執行,而且萬(wàn)一很不
湊巧的,A 又把 buffer 寫(xiě)滿(mǎn)了,那其它 9 個(gè) process 要怎么辦呢? 所以在 write
buffer 的部分,需要用一個(gè) while 來(lái)檢查 buffer 目前是否滿(mǎn)了.如果是的話(huà),那就
繼續掛在 wq 上面.

上面所談的就是 wait_queue 的用法。很簡(jiǎn)單不是嗎? 接下來(lái),我會(huì )再介紹一下
wait_queue 提供那些 function 讓我們使用。讓我再重申一次。wait_queue 應設為
global variable,比方叫 wq,只要任何的 process 想將自己掛在上面,就可以直接
叫呼叫 sleep_on 等 function。要將 wq 上的 process 叫醒。只要呼叫 wake_up 等
function 就可以了.

就我所知,wait_queue 提供4個(gè) function 可以使用,兩個(gè)是用來(lái)將 process 加到
wait_queue 的:

sleep_on( struct wait_queue **wq );
interruptible_sleep_on( struct wait_queue **wq );

另外兩個(gè)則是將process從wait_queue上叫醒的。

wake_up( struct wait_queue **wq );
wake_up_interruptible( struct wait_queue **wq );

我現在來(lái)解釋一下為什么會(huì )有兩組。有 interruptible 的那一組是這樣子的。當我們
去 read 一個(gè)沒(méi)有資料可供讀取的 socket 時(shí),process 會(huì ) block 在那里。如果我們
此時(shí)按下 Ctrl+C,那 read() 就會(huì )傳回 EINTR。像這種的 block IO 就是使用
interruptible_sleep_on() 做到的。也就是說(shuō),如果你是用
interruptible_sleep_on() 來(lái)將 process 放到 wait_queue 時(shí),如果有人送一個(gè)
signal 給這個(gè) process,那它就會(huì )自動(dòng)從 wait_queue 中醒來(lái)。但是如果你是用
sleep_on() 把 process 放到 wq 中的話(huà),那不管你送任何的 signal 給它,它還是不
會(huì )理你的。除非你是使用 wake_up() 將它叫醒。sleep 有兩組。wake_up 也有兩組。
wake_up_interruptible() 會(huì )將 wq 中使用 interruptible_sleep_on() 的 process
叫醒。至于 wake_up() 則是會(huì )將 wq 中所有的 process 叫醒。包括使用
interruptible_sleep_on() 的 process。

在使用 wait_queue 之前有一點(diǎn)需要特別的小心,呼叫 interruptible_sleep_on() 以
及 sleep_on() 的 function 必須要是 reentrant。簡(jiǎn)單的說(shuō),reentrant 的意思是說(shuō)
此 function不會(huì )改變任何的 global variable,或者是不會(huì ) depend on 任何的
global variable,或者是在呼叫 interruptible_sleep_on() 或 sleep_on() 之后不
會(huì ) depend on 任何的 global variable。因為當此 function 呼叫 sleep_on() 時(shí),
目前的 process 會(huì )被暫停執行??赡芰硪粋€(gè) process 又會(huì )呼叫此 function。若之前
的 process 將某些 information 存在 global variable,等它恢復執行時(shí)要使用,結
果第二行程進(jìn)來(lái)了,又把這個(gè) global variable 改掉了。等第一個(gè) process 恢復執行
時(shí),放在 global variable 中的 information 都變了。產(chǎn)生的結果恐怕就不是我們所
能想象了。其實(shí),從 process 執行指令到此 function 中所呼叫的 function 都應該
是要 reentrant 的。不然,很有可能還是會(huì )有上述的情形發(fā)生.

由于 wait_queue 是 kernel 所提供的,所以,這個(gè)例子必須要放到 kernel 里去執
行。我使用的這個(gè)例子是一個(gè)簡(jiǎn)單的 driver。它會(huì ) maintain 一個(gè) buffer,大小是
8192 bytes。提供 read跟 write 的功能。當 buffer 中沒(méi)有資料時(shí),read() 會(huì )馬上
傳回,也就是不做 block IO。而當 write buffer 時(shí),如果呼叫 write() 時(shí),空間已
滿(mǎn)或寫(xiě)入的資料比 buffer 大時(shí),就會(huì )被 block 住,直到有人將 buffer 里的資料讀
出來(lái)為止。在 write buffer 的程序代碼中,我們使用 wait_queue 來(lái)做到 block IO
的功能。在這里,我會(huì )將此 driver 寫(xiě)成 module,方便加載 kernel。

第一步,這個(gè) driver 是一個(gè)簡(jiǎn)單的 character device driver。所以,我們先在
/dev 下產(chǎn)生一個(gè) character device。major number 我們找一個(gè)比較沒(méi)人使用的,像
是 54,minor number 就用 0。接著(zhù)下一個(gè)命令.

mknod /dev/buf c 54 0

mknod 是用來(lái)產(chǎn)生 special file 的 command。/dev/buf 表示要產(chǎn)生叫 buf 的檔案,
位于 /dev 下。 c 表示它是一個(gè) character device。54 為其 major number,0 則是
它的 minor number。有關(guān) character device driver 的寫(xiě)法。有機會(huì )我再跟各位介
紹,由于這次是講 wait_queue,所以,就不再多提 driver 方面的東西.

第二步,我們要寫(xiě)一個(gè) module,底下是這個(gè) module 的程序代碼:

buf.c
#define MODULE
#include
#include
#include
#include
#include
#define BUF_LEN 8192

int flag; /* when rp = wp,flag = 0 for empty,flag = 1 for
non-empty */
char *wp,*rp;
char buffer[BUF_LEN];
EXPORT_NO_SYMBOLS; /* don‘t export anything */

static ssize_t buf_read( struct file *filp,char *buf,size_t count,
loff_t *ppos )

return count; 


static ssize_t buf_write( struct file *filp,const char *buf,size_t count, 
loff_t *ppos ) 

return count; 


static int buf_open( struct inode *inode,struct file *filp ) 

MOD_INC_USE_COUNT; 
return 0; 


static int buf_release( struct inode *inode,struct file *filp ) 

MOD_DEC_USE_COUNT; 
return 0; 


static struct file_operations buf_fops = { 
NULL, /* lseek */ 
buf_read, 
buf_write, 
NULL, /* readdir */ 
NULL, /* 
poll */
NULL, /* ioctl */
NULL, /* mmap */
buf_open, /* open */
NULL, /* flush */
buf_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL /* lock */
};

static int buf_init()

int result; 

flag = 0; 
wp = rp = buf; 

result = register_chrdev( 54,"buf",&buf_fops ); 
if ( result < 0 ) { 
printk( "<5>buf: cannot get major 54\n" ); 
return result; 


return 0; 


static void buf_clean() 

if ( unregister_chrdev( 54,"buf" ) ) { 
printk( "<5>buf: unregister_chrdev error\n" ); 



int init_module( void ) 

return buf_init(); 


void cleanup_module( void ) 

buf_clean(); 


有關(guān) module 的寫(xiě)法,請各位自行參考其它的文件,最重要的是要有 init_module()和 
cleanup_module() 這兩個(gè) function。我在這兩個(gè) function 里分別做 initialize 和 
finalize 的動(dòng)作?,F在分別解釋一下。在 init_module() 里,只有呼叫 buf_init() 
而己。其實(shí),也可以將 buf_init() 的 code 寫(xiě)到 init_module() 里。只是我覺(jué)得這
樣比較好而已。 

flag = 0; 
wp = rp = buf; 
result = register_chrdev( 54,"buf",&buf_fops ); 
if ( result < 0 ) { 
printk( "<5>buf: cannot get major 54\n" ); 
return result; 

return 0; 

init_buf() 做的事就是去注冊一個(gè) character device driver。在注冊一個(gè) 
character device driver 之前,必須要先準備一個(gè)型別為 file_operations 結構的
變量,file_operations 里包含了一些 function pointer。driver 的作者必須自己寫(xiě)
這些 function。并將 function address 放到這個(gè)結構里。如此一來(lái),當 user 去讀
取這個(gè) device 時(shí),kernel 才有辦法去呼叫對應這個(gè) driver 的 function。其實(shí),簡(jiǎn)
要來(lái)講。character device driver 就是這么一個(gè) file_operations 結構的變
量。file_operations 定義在 這個(gè)檔案里。它的 prototype 在 kernel 2.2.1 與以前
的版本有些微的差異,這點(diǎn)是需要注意的地方。 

register_chrdev() 看名字就大概知道是要注冊 character device driver。第一個(gè)參
數是此 device 的 major number。第二個(gè)是它的名字。名字你可以隨便取。第三個(gè)的
參數就是一個(gè) file_operations 變量的地址。init_module() 必須要傳回 0,module 
才會(huì )被加載。 

在 cleanup_module() 的部分,我們也是只呼叫 buf_clean() 而已。它做的事是 
unregister 的動(dòng)作。 

if ( unregister_chrdev( 54,"buf" ) ) { 
printk( "<5>buf: unregister_chrdev error\n" ); 


也就是將原本記錄在 device driver table 上的資料洗掉。第一個(gè)參數是 major 
number。第二個(gè)則是此 driver 的名稱(chēng),這個(gè)名字必須要跟 register_chrdev() 中所
給的名字一樣才行。 

現在我們來(lái)看看此 driver 所提供的 file_operatio
ns 是那些。

static struct file_operations buf_fops =

NULL, /* lseek */
buf_read,
buf_write,
NULL, /* readdir */
NULL, /* poll */
NULL, /* ioctl */
NULL, /* mmap */
buf_open, /* open */
NULL, /* flush */
buf_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL /* lock */
};

在此,我們只打算 implement buf_read(),buf_write(),buf_open,和
buf_release()等 function 而已。當 user 對這個(gè) device 呼叫 open() 的時(shí)候,
buf_open() 會(huì )在最后被 kernel 呼叫。相同的,當呼叫 close(),read(),和
write() 時(shí),buf_release(),buf_read(),和 buf_write() 也都會(huì )分別被呼叫。首
先,我們先來(lái)看看 buf_open()。

static int buf_open( struct inode *inode,struct file *filp )
MOD_INC_USE_COUNT;
return 0;
}

buf_open() 做的事很簡(jiǎn)單。就是將此 module 的 use count 加一。這是為了避免當此
module 正被使用時(shí)不會(huì )被從 kernel 移除掉。相對應的,在 buf_release() 中,我們
應該要將 use count 減一。就像開(kāi)啟檔案一樣。有 open(),就應該要有對應的
close() 才行。如果 module 的 use count 在不為 0 的話(huà),那此 module 就無(wú)法從
kernel 中移除了。

static int buf_release( struct inode *inode,struct file *filp )

MOD_DEC_USE_COUNT; 
return 0; 


接下來(lái),我們要看一下buf_read()和buf_write()。 

static ssize_t buf_read( struct file *filp,char *buf,size_t count, 
loff_t *ppos ) 

return count; 


static ssize_t buf_write( struct file *filp,const char *buf, 
size_t count,loff_t *ppos ) 

return count; 


在此,我們都只是回傳 user 要求讀取或寫(xiě)入的字符數目而已。在此,我要說(shuō)明一下這
些參數的意義。filp 是一個(gè) file 結構的 pointer。也就是指我們在 /dev 下所產(chǎn)生
的 buf 檔案的 file 結構。當我們呼叫 read() 或 write() 時(shí),必須要給一個(gè) 
buffer 以及要讀寫(xiě)的長(cháng)度。Buf 指的就是這個(gè) buffer,而 count 指的就是長(cháng)度。至
于 ppos 是表示目前這個(gè)檔案的 offset 在那里。這個(gè)值對普通檔案是有用的。也就是
跟 lseek() 有關(guān)系。由于在這里是一個(gè) drvice。所以 ppos 在此并不會(huì )用到。有一點(diǎn)
要小心的是,上面參數 buf 是一個(gè)地址,而且還是一個(gè) user space 的地址,當 
kernel 呼叫 buf_read() 時(shí),程序在位于 kernel space。所以你不能直接讀寫(xiě)資料到 
buf 里。必須先切換 FS 這個(gè) register 才行。 

Makefile 
P = buf 
OBJ = buf.o 
INCLUDE = -I/usr/src/linux/include/linux 
CFLAGS = -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -O $(INCLUDE) \ 
-include /usr/src/linux/include/linux/modversions.h 
CC = gcc 

$(P): $(OBJ) 
ld -r $(OBJ) -o $(P).o 

c.o: 
$(CC) -c $(CFLAGS) $< 

clean
:
rm -f *.o *~ $(P)

加入上面這個(gè) Makefile,打入 make 之后,就會(huì )產(chǎn)生一個(gè) buf.o 的檔案。利用
insmod 將 buf.o 載到 kernel 里。相信大家應該都用過(guò) /dev/zero 這個(gè) device。去
讀取這個(gè) device,只會(huì )得到空的內容。寫(xiě)資料到這個(gè) device 里也只會(huì )石沈大?!,F
在你可以去比較 buf 和 zero 這兩個(gè) device。兩者的行為應該很類(lèi)似才是。

第三步,我們在第二步中 implement 一個(gè)像 zero 的 device driver。我們現在要經(jīng)
由修改它來(lái)使用 wait_queue。首先,我們先加入一個(gè) global variable,write_wq,
并把它設為 NULL。

struct wait_queue *write_wq = NULL;

然后,在 buf_read() 里,我們要改寫(xiě)成這個(gè)樣子。

static ssize_t buf_read( struct file *filp,char *buf,size_t count,
loff_t *ppos )

int num,nRead; 
nRead = 0; 
while ( ( wp == rp ) && !flag ) { /* buffer is empty */ 
return 0; 


repeate_reading: 
if ( rp < wp ) { 
num = min( count,( int ) ( wp-rp ) ); 

else { 
num = min( count,( int ) ( buffer+BUF_LEN-rp ) ); 

copy_to_user( buf,rp,num ); 
rp += num; 
count -= num; 
nRead += num; 
if ( rp == ( buffer + BUF_LEN ) ) 
rp = buffer; 
if ( ( rp != wp ) && ( count > 0 ) ) 
goto repeate_reading; 
flag = 0; 
wake_up_interruptible( &write_wq ); 
return nRead; 


在前頭我有提到,buf 的地址是屬于 user space 的。在 kernel space 中,你不能像
普通寫(xiě)到 buffer 里一樣直接將資料寫(xiě)到 buf 里,或直接從 buf 里讀資料。Linux 里
使用 FS 這個(gè) register 來(lái)當作 kernel space 和 user space 的切換。所以,如果你
想手動(dòng)的話(huà),可以這樣做: 

mm_segment_t fs; 
fs = get_fs(); 
set_fs( USER_DS ); 
write_data_to_buf( buf ); 
set_fs( fs ); 

也就是先切換到 user space,再寫(xiě)資料到 buf 里。之后記得要切換回來(lái) kernel 
space。這種自己動(dòng)手的方法比較麻煩,所以 Linux 提供了幾個(gè) function,可以讓我
們直接在不同的 space 之間做資料的搬移。誠如各位所見(jiàn),copy_to_user() 就是其中
一個(gè)。 

copy_to_user( to,from,n ); 
copy_from_user( to,from,n ); 

顧名思義,copy_to_user() 就是將資料 copy 到 user space 的 buffer 里,也就是
從 to 寫(xiě)到 from,n 為要 copy 的 byte 數。相同的,copy_from_user() 就是將資料
從 user space 的 from copy 到位于 kernel 的 to 里,長(cháng)度是 n bytes。在以前的 
kernel 里,這兩個(gè) function 的前身是 memcpy_tofs() 和 memcpy_fromfs(),不知道
為什么到了 kernel 2.2.1之后,名字就被改掉了。至于它們的程序代碼有沒(méi)有更改就
不太清楚了。至于到那一版才改的。我沒(méi)有仔細去查,只知道在 2.0.36 時(shí)還沒(méi)改,到
了 2.2.1 就改了。這兩個(gè) function 是 macro,都定義在 里。要使用前記得先 
include 進(jìn)來(lái)。 

相信 buf_read() 的程序代碼應當不難了解才對。不知道各位有沒(méi)有看到,在buf_read
() 的后面有一行的程序,就是 

wake_up_interruptible( &write_wq ); 

write_wq 是我們用來(lái)放那些想要寫(xiě)資料到 buffer
,但 buffer 已滿(mǎn)的 process。這一行的程序會(huì )將掛在此 queue 上的 process 叫醒。
當 queue 是空的時(shí),也就是當 write_wq 為 NULL 時(shí),wake_up_interruptible() 并
不會(huì )造成任何的錯誤。接下來(lái),我們來(lái)看看更改后的 buf_write()。

static ssize_t buf_write( struct file *filp,const char *buf,size_t count,
loff_t *ppos )

int num,nWrite; 
nWrite = 0; 
while ( ( wp == rp ) && flag ) { 
interruptible_sleep_on( &write_wq ); 


repeate_writing: 
if ( rp > wp ) { 
num = min( count,( int ) ( rp - wp ) ); 

else { 
num = min( count,( int ) ( buffer + BUF_LEN - wp ) ); 

copy_from_user( wp,buf,num ); 
wp += num; 
count -= num; 
nWrite += num; 
if ( wp == ( buffer + BUF_LEN ) ) { 
wp = buffer; 

if ( ( wp != rp ) && ( count > 0 ) ) { 
goto repeate_writing; 

flag = 1; 
return nWrite; 


我們把 process 丟到 write_wq 的動(dòng)作放在 buf_write() 里。當 buffer 已滿(mǎn)時(shí),就
直接將 process 丟到 write_wq 里. 

while ( ( wp == rp ) && flag ) { 
interruptible_sleep_on( &write_wq ); 


好了?,F在程序已經(jīng)做了一些修改。再重新 make 一次,利用 insmod 將 buf.o 載到 
kernel 里就行了。接著(zhù),我們就來(lái)試驗一下是不是真正做到 block IO. 

# cd /dev 
# ls -l ~/WWW-HOWTO 
-rw-r--r-- 1 root root 23910 Apr 14 16:50 /root/WWW-HOWTO 
# cat ~/WWW-HOWTO > buf 

執行到這里,應該會(huì )被 block 住?,F在,我們再開(kāi)一個(gè) shell 出來(lái). 

# cd /dev 
# cat buf 
.。( contents of WWW-HOWTO ) ..。skip ... 

此時(shí),WWW-HOWTO 的內容就會(huì )出現了。而且之前 block 住的 shell 也已經(jīng)回來(lái)了。最
后,試驗結束,可以下 

# rmmod buf 

將 buf 這個(gè) module 從 kernel 中移除。以上跟各位介紹的就是 wait_queue 的使
用。希望能對各位有所助益。 

我想對某些人來(lái)講,會(huì )使用一個(gè)東西就夠了。然而對某些人來(lái)講,可能也很希望知道這
項東西是如何做出來(lái)的。至少我就是這種人。在下面,我將為各位介紹 wait_queue 的 
implementation。如果對其 implementation 沒(méi)興趣,以下這一段就可以略過(guò)不用看
了。 

wait_queue 是定義在 里,我們可以先看看它的數據結構是怎么樣: 

struct wait_queue { 
struct task_struct * task; 
struct wait_queue * next; 
}; 

很簡(jiǎn)單是吧。這個(gè)結構里面只有二個(gè)字段,一個(gè)是 task_struct 的 pointer,另一個(gè)
則是 wait_queue 的 pointer。很明顯的,我們可以看出 wait_queue 其實(shí)就是一個(gè) 
linked list,而且它還是一個(gè) circular linked list。 其中 task_struct 就是用來(lái)
指呼叫 sleep_on 等 function的 process。在 Linux 里,每一個(gè) process 是由一個(gè) 
task_struct 來(lái)描敘。task_struct 是一個(gè)很大的的結構,在此我們不會(huì )討論。Linux 
里有一個(gè) global variable,叫 current,它會(huì )指到目前正在執行的 process 的 
task_struct 結構。這也就是為什么當 process 呼叫 system call,
切換到 kernel 時(shí),kernel 會(huì )知道是那個(gè) process 呼叫的。

好,我們現在來(lái)看看 interruptible_sleep_on() 和 sleep_on() 是如何做的。這兩個(gè)
function 都是位于 /usr/src/linux/kernel/sched.c 里。

void interruptible_sleep_on(struct wait_queue **p)

SLEEP_ON_VAR 
current->state = TASK_INTERRUPTIBLE; 
SLEEP_ON_HEAD 
schedule(); 
SLEEP_ON_TAIL 


void sleep_on(struct wait_queue **p) 

SLEEP_ON_VAR 
current->state = TASK_UNINTERRUPTIBLE; 
SLEEP_ON_HEAD 
schedule(); 
SLEEP_ON_TAIL 


各位有沒(méi)有發(fā)現這兩個(gè) function 很類(lèi)似。是的,它們唯一的差別就在于 

current->state = ... 

這一行而已。之前,我們有說(shuō)過(guò),interruptible_sleep_on() 可以被 signal 中斷,
所以,其 current->state 被設為 TASK_INTERRUPTIBLE。而 sleep_on() 沒(méi)辦法被中
斷,所以 current->state 設為 TASK_UNINTERRUPTIBLE。接下來(lái),我們只看 
interruptible_sleep_on() 就好了。畢竟它們兩的差異只在那一行而已。 

在 sched.c 里,SLEEP_ON_VAR 是一個(gè) macro,其實(shí)它只是定義兩個(gè)區域變量出來(lái)而
已。 

#defineSLEEP_ON_VAR\ 
unsigned long flags;\ 
struct wait_queue wait; 

剛才我也說(shuō)過(guò),current 這個(gè)變量是指到目前正在執行的 process 的 task_struct 結
構。所以 current->state = TASK_INTERRUPTIBLE 會(huì )設定在呼叫 
interruptible_sleep_on() 的 process 身上。至于 SLEEP_ON_HEAD 做的事,則是將 
current 的值放到 SLEEP_ON_VAR 宣告的 wait 變量里,并把 wait 放到 
interruptible_sleep_on() 的參數所屬的 wait_queue list 中。 

#defineSLEEP_ON_HEAD\ 
wait.task = current;\ 
write_lock_irqsave(&waitqueue_lock,flags);\ 
__add_wait_queue(p,&wait);\ 
write_unlock(&waitqueue_lock); 

wait 是在 SLEEP_ON_VAR 中宣告的區域變量。其 task 字段被設成呼叫 
interruptible_sleep_on() 的 process。至于 waitqueue_lock 這個(gè)變量是一個(gè) spin 
lock。 waitqueue_lock 是用來(lái)確保同一時(shí)間只能有一個(gè) writer。但同一時(shí)間則可以
有好幾個(gè) reader。也就是說(shuō) waitqueue_lock 是用來(lái)保證 critical section 的 
mutual exclusive access。 

unsigned long flags; 
write_lock_irqsave(&waitqueue_lock,flags); 
..critical section ... 
write_unlock(&waitqueue_lock) 

學(xué)過(guò) OS 的人應該知道 critical section 的作用是什么,如有需要,請自行參考 OS 
參考書(shū)。在 critical section 里只做一件事,就是將 wait 這個(gè)區域變量放到 p 這
個(gè) wait_queue list 中。 p 是 user 在呼叫 interruptible_sleep_on() 時(shí)傳進(jìn)來(lái)
的,它的型別是 struct wait_queue **。在此, critical section 只呼叫 
__add_wait_queue()。 

extern inline void __add_wait_queue(struct wait_queue ** p, 
struct wait_queue * wait) 

wait->next = *p ? : WA
IT_QUEUE_HEAD(p);
*p = wait;
}

__add_wait_queue() 是一個(gè)inline function,定義在 中。WAIT_QUEUE_HEAD()是個(gè)很
有趣的 macro,待會(huì )我們再討論?,F在只要知道它會(huì )傳回這個(gè) wait_queue 的開(kāi)頭就可
以了。所以,__add_wait_queue() 的意思就是要把 wait 放到 p 所屬的 wait_queue
list 的開(kāi)頭。但是,大家還記得嗎? 在上面的例子里,一開(kāi)始我們是把 write_wq 設
為 NULL。也就是說(shuō) *p 是 NULL。所以,當 *p 是 NULL 時(shí),


wait->next = WAIT_QUEUE_HEAD(p)

是什么意思呢?

所以,現在,我們來(lái)看一下 WAIT_QUEUE_HEAD() 是怎么樣的一個(gè) macro,它是定義在
里。

#define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1))

x 型別是 struct wait_queue **,因為是一個(gè) pointer,所以大小是 4 byte。因此,
若 x 為 100 的話(huà),那 ((x)-1) 就變成 96。如下圖所示。 WAIT_QUEUE_HEAD(x) 其實(shí)
會(huì )傳回 96,而且將其轉型為 struct wait_queue*,各位可以看看。原本的
wait_queue* 只配制在 100-104 之間?,F在 WAIT_QUEUE_HEAD(x) 卻直接傳回96,但
是 96-100 這塊位置根本沒(méi)有被我們配置起來(lái)。更妙的事。由于 x 是一個(gè) wait_queue
list 的開(kāi)頭,我們始終不會(huì )用到 96-100 這塊,我們只會(huì )直接使用到 100-104 這塊內
存。這也算是 wait_queue 一項比較奇怪的 implementation 方式吧。下面有三張圖,
第一張表示我們宣告了一個(gè) wait_queue* 的變量,地址在 100。另外還有一個(gè)
wait_queue 的變量,名叫 wait。第二張圖是我們呼叫 interruptible_sleep_on() 之
后得到的結果。第三張則是我們又宣告一個(gè) wait_queue,名叫 ano_wait,將
ano_wait 放到 wait_queue list 后的結果就第三張圖所顯示的。
http://linuxfab.cx/Columns/10/wqq.GIF

在 interruptible_sleep_on() 中,當呼叫完 SLEEP_ON_HEAD 之后,目前的 process
就已經(jīng)被放到 wait_queue 中了。接下來(lái)會(huì )直接呼叫 schedule(),這個(gè) function 是
用來(lái)做 scheduling 用的。current 所指到的 process 會(huì )被放到 scheduling queue
中等待被挑出來(lái)執行。執行完 schedule() 之后,current 就沒(méi)辦法繼續執行了。而當
current 以后被 wake up 時(shí),就會(huì )從 schedule() 之后,也就是從 SLEEP_ON_TAIL 開(kāi)
始執行。SLEEP_ON_TAIL 做的事剛好跟 SLEEP_ON_HEAD 相反,它會(huì )將此 process 從
wait_queue 中移除。

#defineSLEEP_ON_TAIL\
write_lock_irq(&waitqueue_lock);\
__remove_wait_queue(p,&wait);\
write_unlock_irqrestore(&waitqueue_lock,flags);

跟 SLEEP_ON_HEAD 一樣。SLEEP_ON_TAIL 也是利用 spin lock 包住一個(gè) critical
section。

extern inline void __remove_wait_queue(struct wait_queue ** p,struct
wait_queue * wait)

struct wait_queue * next = wait->next; 
struct wait_queue * head = next; 
struct wait_queue * tmp; 
while ((tmp = head->next) != wait) { 
head = tmp; 

head->next = next; 


__remove_wait_queue() 是一個(gè) inline function,也是同樣定義在 里。是用來(lái)將 
wait 從 p 這個(gè) wait_queue list 中移除掉。 

現在,大家應該已經(jīng)清楚了 int
erruptible_sleep_on() 和 sleep_on() 的做法,也應該比較清楚 wait_queue 是如何
的做到 block IO。接下來(lái),我們繼續看 wake_up_interruptible() 和 wake_up() 是
如何 implement 的。wake_up_interruptible() 和 wake_up() 其實(shí)是兩個(gè) macro,都
定義在 里。

#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | \
TASK_INTERRUPTIBLE)
#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE)

從這里可以看出,兩個(gè) macro 幾乎是一樣的,差別只在于傳給 __wake_up() 中的一個(gè)
flag 有所差異而已。其實(shí),wake_up() 傳給 __wake_up() 的是
TASK_UNINTERRUPTIBLE|TASK_INTERRUPTIBLE,意思是說(shuō)它會(huì )將 wait_queue list 中
process->state 是 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 的所有 process
叫醒。而 wake_up_interruptible() 則只將 state是 TASK_INTERRUPTIBLE 的叫醒.

void __wake_up(struct wait_queue **q,unsigned int mode)

struct wait_queue *next; 
read_lock(&waitqueue_lock); 
if (q && (next = *q)) { 
struct wait_queue *head; 
head = WAIT_QUEUE_HEAD(q); 
while (next != head) { 
struct task_struct *p = next->task; 
next = next->next; 
if (p->state & mode) 
wake_up_process(p); 


read_unlock(&waitqueue_lock); 


在 wake up 的過(guò)程中,我們不需要設定 write lock,但是仍要設定 read lock,這是
為了避免有人在我們讀取 wait_queue 時(shí)去寫(xiě) wait_queue list 的內容,造成 
inconsistent。在這段程序代碼中,是去 transverse 整個(gè) list,如果 process 的 
state 跟 mode 有吻合,則呼叫 wake_up_process() 將它叫醒。 

void wake_up_process(struct task_struct * p) 

unsigned long flags; 
spin_lock_irqsave(&runqueue_lock,flags); 
p->state = TASK_RUNNING; 
if (!p->next_run) { 
add_to_runqueue(p); 
reschedule_idle(p); 

spin_unlock_irqrestore(&runqueue_lock,flags); 


在此,runqueue_lock 也是一個(gè) spin lock,kernel 依然在此設一個(gè) critical 
section 以方便更改 run queue。Run queue 是用來(lái)放可以執行的 process 用的。在
放入 run queue 之前,會(huì )先將 process 的 state 設為 TASK_RUNNING。 

wait_queue 其實(shí)是一個(gè)蠻好用的東西。相信只要各位有機會(huì )去修改 kernel 的話(huà),都
應該有機會(huì )用到它才對。希望對大家有點(diǎn)幫助。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
阻塞與非阻塞I/O
linux中的阻塞機制及等待隊列
Linux驅動(dòng)阻塞的實(shí)現
全面解析Linux內核的同步與互斥機制
Linux內核機制之等待隊列
Linux內核的同步機制(三):等待隊列
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久