亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
12下一頁
最近訪問板塊 發(fā)新帖
查看: 7998 | 回復(fù): 19
打印 上一主題 下一主題

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇) [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2005-07-04 10:33 |只看該作者 |倒序?yàn)g覽
[list=]
本文檔是《Linux2.4 內(nèi)核說明文檔》中的第二部分。以下是整個(gè)文檔大致目錄:
1,啟動(dòng) (http://72891.cn/forum/viewtopic.php?t=557946
2,進(jìn)程和中斷管理
3,虛擬文件系統(tǒng)
4,Linux 頁緩沖
5,IPC機(jī)制

本篇文檔的目錄為:
2.1.        Tack結(jié)構(gòu)和進(jìn)程表       
2.2.        創(chuàng)建和中止任務(wù)與內(nèi)核線程       
2.3.        調(diào)度程序       
2.4.        Linux執(zhí)行鏈表       
2.5.        等待隊(duì)列       
2.6.        內(nèi)核時(shí)鐘       
2.7.        Bottom Halves       
2.8.        任務(wù)隊(duì)列       
2.9.        I386體系中系統(tǒng)調(diào)用實(shí)現(xiàn)       
2.10.        原子操作       
2.11.        旋轉(zhuǎn)鎖、讀寫旋轉(zhuǎn)鎖和Big-Reader旋轉(zhuǎn)鎖
2.12.        信號燈和讀寫信號燈       
2.13.        裝載模塊的內(nèi)核支持

一下是正文:
2.        進(jìn)程和中斷管理

2.1.        Tack結(jié)構(gòu)和進(jìn)程表
  linux下的每個(gè)進(jìn)程都是動(dòng)態(tài)分配一個(gè)task_struct結(jié)構(gòu),整個(gè)系統(tǒng)可以創(chuàng)建的最大進(jìn)程數(shù)僅由當(dāng)前可用物理內(nèi)存總數(shù)限制,并且等于(見kernel/fork.c:fork_init()函數(shù)):
  
   /*
    * The default maximum number of threads is set to a safe
    * value: the thread structures can take up at most half
    * of memory.
    */
    max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2;
   
    這個(gè)式子在IA32體系結(jié)構(gòu)上主要意味著最大數(shù)為物理內(nèi)存頁數(shù)/4,例如:在一個(gè)512M內(nèi)存機(jī)器上你可以創(chuàng)建32K線程。這對于舊版本(2.2或者更老)內(nèi)核的4K限制是一個(gè)可觀的改進(jìn),而且這可以在運(yùn)行時(shí)使用系統(tǒng)調(diào)用sysctl(2)修改KERN_MAX_THREADS,或者簡單使用procfs系統(tǒng)接口來調(diào)整。

# cat /proc/sys/kernel/threads.max
32764
# echo 100000 >; /proc/sys/kernel/threads.max
# cat /proc/sys/kernel/threads.max
100000
# gdb .q vmlinux /proc/kcore
Core was generated by `BOOT_IMAGE=240ac18 ro root=306 video=matrox:vesa:0x118'.
#0 0x0 in ?? ()
(gdb) p max_threads
$1 = 100000

  Linux系統(tǒng)上進(jìn)程的關(guān)聯(lián)表現(xiàn)為一個(gè)以下兩個(gè)方式鏈接的task_struct結(jié)構(gòu)的集合:
1)        以pid為鍵值的hash表。
2)        通過p->;next_task和p->;prev_task指針連接的雙向鏈表。
這個(gè)hash表名為pidhash,并在include/linux/sched.h中定義:

/* PID hashing. (shouldnt this be dynamic?) */
#define PIDHASH_SZ (4096 >;>; 2)
extern struct task_struct *pidhash[PIDHASH_SZ];
#define pid_hashfn(x) ((((x) >;>; ^ (x)) & (PIDHASH_SZ . 1))

   所有的任務(wù)以他們的pid為鍵值存放到hash表中,并假定均勻地從(0 to PID_MAX-1)分布。這個(gè)hash表用來通過指定的pid快速的找到task結(jié)構(gòu),搜索函數(shù)find_task_pid()定義在include/linux/sched.h中:

static inline struct task_struct *find_task_by_pid(int pid)
{
struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
for(p = *htable; p && p.>;pid != pid; p = p.>;pidhash_next)
;
return p;
}

  在每個(gè)hash鏈上的所有任務(wù)都通過p->;pidhash_next和p->;pidhash_pprev連接起來,這在hash_pid函數(shù)和unhash_pid函數(shù)將指定進(jìn)程插入hash表或者移出hash表時(shí)使用。所有的操作都受到tasklist_lock寫同步鎖保護(hù)。
而雙向鏈表則為系統(tǒng)遍歷所有的任務(wù)提供了方便,這個(gè)操作由定義在include/linux/sched.h中的for_each_tack()宏來實(shí)現(xiàn)。

#define for_each_task(p) \
for (p = &init_task ; (p = p.>;next_task) != &init_task ; )

for_each_task()函數(shù)的使用者必須使用tasklist_lock讀同步鎖。注意for_each_task()函數(shù)采用init_task標(biāo)識鏈表的起點(diǎn),這樣才是安全的,因?yàn)榭杖蝿?wù)(pid = 0) 是不在鏈表里的。進(jìn)程hash表和進(jìn)程鏈表的修改操作,特別是fork函數(shù),exit函數(shù)和ptrace函數(shù),必須調(diào)用tasklist_lock寫同步鎖。更有趣的是,所有的寫操作還必須屏蔽當(dāng)前CPU的中斷,這個(gè)原因是顯而易見的:send_sigio函數(shù)遍歷了進(jìn)程表,這樣需要調(diào)用tasklist_lock讀同步鎖,并且該函數(shù)是kill_fasync函數(shù)在中斷環(huán)境下調(diào)用的。
現(xiàn)在我們已經(jīng)知道task_struct結(jié)構(gòu)是怎樣鏈接到一起的,現(xiàn)在讓我們分析一下task_struct結(jié)構(gòu)的成員。這些成員是UNIX系統(tǒng)的proc結(jié)構(gòu)和user結(jié)構(gòu)松散組合到一起的。
其他UNIX版本總是將進(jìn)程狀態(tài)信息作為單獨(dú)一部分常駐內(nèi)存,其他部分則作為進(jìn)程運(yùn)行時(shí)所需信息,如此簡陋的設(shè)計(jì)僅僅因?yàn)閮?nèi)存時(shí)非常寶貴的資源,F(xiàn)代操作系統(tǒng)(如Linux或者FreeBSD)并不做如此區(qū)分,而是在內(nèi)核常駐內(nèi)存的數(shù)據(jù)結(jié)構(gòu)中維護(hù)進(jìn)程狀態(tài)。
include/linux/sched.h中定義了task_struct結(jié)構(gòu),并且通常大寫為1680字節(jié),狀態(tài)宏也定義同一個(gè)頭文件中。

volatile long state; /* .1 unrunnable, 0 runnable, >;0 stopped */
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE 4
#define TASK_STOPPED 8
#define TASK_EXCLUSIVE 32

為什么TASK_EXCLUSIVE宏定義為32而不是16呢?這是由于16被TASK_SWAPPING使用了,并且后來在移出TASK_SWAPPING時(shí)沒有把TASK_EXCLUSIVE的值上調(diào)。
可變量p->;state的定義意味著它自身可以被中斷處理者異步修改。
1)        TASK_RUNNING:含義是假定任務(wù)已經(jīng)處于運(yùn)行隊(duì)列中。至于不是已經(jīng)處于運(yùn)行隊(duì)列的原因是由于將一個(gè)任務(wù)標(biāo)識為TASK_RUNNING和將該任務(wù)移動(dòng)到運(yùn)行隊(duì)列不是一個(gè)原子操作。從運(yùn)行隊(duì)列角度考慮,操作時(shí)需要保持runqueue_lock讀同步鎖。如果這樣操作,你將發(fā)現(xiàn)在運(yùn)行隊(duì)列的每個(gè)任務(wù)都處于TASK_RUNNING狀態(tài)。然后,反過來卻不一定。同樣地,驅(qū)動(dòng)程序可以標(biāo)識他們自身狀態(tài)為TASK_INTERRUPTIBLE,然后調(diào)用schedule()函數(shù),這個(gè)函數(shù)將從運(yùn)行隊(duì)列移出它自己(除非當(dāng)時(shí)有一個(gè)導(dǎo)致它滯留在運(yùn)行隊(duì)列的未處理信號)。
2)        TASK_INTERRUPTIBLE:含義是任務(wù)處于休眠狀態(tài)但可以通過一個(gè)信號或者休眠中止時(shí)鐘喚醒。
3)        TASK_UNINTERRUPTIBLE:含義類似于TASK_INTERRUPTIBL,但任務(wù)不能被喚醒。
4)        TASK_ZOMBIE:含義是任務(wù)已經(jīng)被中止但它的狀態(tài)還沒被父進(jìn)程獲取。
5)        TASK_STOPPED:含義是由于任務(wù)控制信號或者ptrace系統(tǒng)調(diào)用,任務(wù)已經(jīng)被停止。
6)        TASK_EXCLUSIVE:含義是這不是一個(gè)單獨(dú)狀態(tài),但能夠與TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE狀態(tài)并存(OR操作)。這意味著當(dāng)任務(wù)與其他在等待隊(duì)列休眠時(shí),它可以單獨(dú)被喚醒而不需要喚醒整個(gè)等待隊(duì)列的任務(wù)。
任務(wù)標(biāo)記包含了關(guān)于非互相排斥的進(jìn)程狀態(tài)信息。

unsigned long flags; /* per process flags, defined below */
/*
* Per process flags
*/
#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
/* Not implemented yet, only for 486*/
#define PF_STARTING 0x00000002 /* being created */
#define PF_EXITING 0x00000004 /* getting shut down */
#define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */
#define PF_SUPERPRIV 0x00000100 /* used super.user privileges */
#define PF_DUMPCORE 0x00000200 /* dumped core */
#define PF_SIGNALED 0x00000400 /* killed by a signal */
#define PF_MEMALLOC 0x00000800 /* Allocating memory */
#define PF_VFORK 0x00001000 /* Wake up parent in mm_release */
#define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP) */

p->;has_cpu, p->;processor, p->;counter, p->;priority, p->;policy and p->;rt_priority字段和調(diào)度程序關(guān)聯(lián),并將在后面描述。
p->;mm 和p->;active_mm字段分別指向mm_struct結(jié)構(gòu)描述的進(jìn)程地址空間和有效地址空間(如果這個(gè)進(jìn)程不是內(nèi)核進(jìn)程的話)。這使得當(dāng)任務(wù)被調(diào)度離開時(shí)TLB能夠在地址空間自由切換。所以,如果當(dāng)前正在執(zhí)行內(nèi)核任務(wù)(沒有p->;mm),則它的next->;active_mm將被設(shè)置為已經(jīng)被調(diào)度離開的任務(wù)的prev->;active_mm,如果pre-mm != NULL,則這個(gè)地址將和prev->;mm相同。如果CLONE_VM標(biāo)識傳遞到了clone系統(tǒng)調(diào)用或者依靠vfork系統(tǒng)調(diào)用,則地址空間可以在任務(wù)之間共享。
p->;exec_domain和p->;personality字段與任務(wù)的特性相關(guān),也就是為了模仿UNIX特性的唯一系統(tǒng)調(diào)用。
p->;fs字段包含了文件系統(tǒng)信息,在linux下有三個(gè)方面的含義:
1)        root目錄實(shí)體結(jié)構(gòu)和掛載點(diǎn);
2)        預(yù)備的root目錄實(shí)體和掛載點(diǎn);
3)        當(dāng)前工作目錄實(shí)體和掛載點(diǎn);
這個(gè)結(jié)構(gòu)同樣包含了一個(gè)引用計(jì)數(shù),因?yàn)楫?dāng)進(jìn)行帶CLONE_FS標(biāo)識的clone系統(tǒng)調(diào)用時(shí),它是共享的。
p->;files字段包含了文件句柄表,這在進(jìn)行帶CLONE_FILES標(biāo)識clone系統(tǒng)調(diào)用時(shí)也是多任務(wù)共享的。
p->;sig字段包含了信號處理函數(shù)入口,以CLONE_SIGHAND參數(shù)執(zhí)行clone操作后頁可以在進(jìn)程間共享。

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2005-07-04 12:56 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

2.2.        創(chuàng)建和中止任務(wù)與內(nèi)核線程
不同的操作系統(tǒng)書籍,從一個(gè)“正在執(zhí)行的程序的實(shí)例”到“由clone或者fork系統(tǒng)調(diào)用產(chǎn)生的任務(wù)”等不同方式定義了“進(jìn)程”。在linux下,共有三種類型程序:
        空線程;
        內(nèi)核線程;
        用戶任務(wù);
空線程在為第一個(gè)CPU引導(dǎo)時(shí)創(chuàng)建,然后依靠定義在arch/i386/kernel/smpboot.c的fork_by_hand()函數(shù)手工為每個(gè)CPU創(chuàng)建這個(gè)線程。所有的空線程共享一個(gè)init_task結(jié)構(gòu),但都擁有各自的存放在CPU隊(duì)列里的init_tss表示的TSS結(jié)構(gòu)。他們以CLONE_PID方式clone,PID都為零,其他任務(wù)都不能共享這個(gè)PID。
內(nèi)核模式下,kernel_thread函數(shù)調(diào)用clone系統(tǒng)調(diào)用創(chuàng)建了內(nèi)核線程。內(nèi)核線程通常沒有用戶地址空間,也就是p->;mm=NULL,因?yàn)樗麄兠鞔_通過daemonize()函數(shù)執(zhí)行exit_mm()函數(shù)。內(nèi)核線程通?梢灾苯硬僮鲀(nèi)核地址空間,并被分配低范圍的pid號。當(dāng)在處理器模式下運(yùn)行時(shí)意味著內(nèi)核線程將享用所有的I/O特權(quán)并不能被調(diào)用程序預(yù)清空。
用戶任務(wù)通過clone或者fork系統(tǒng)調(diào)用創(chuàng)建,他們都在內(nèi)部調(diào)用了kernel/fork.c的do_fork()函數(shù)。
讓我們分析一下當(dāng)用戶進(jìn)程調(diào)用fork系統(tǒng)調(diào)用時(shí)發(fā)生了什么。雖然fork操作在傳遞用戶堆棧和寄存器時(shí)依賴于體系架構(gòu),但在下面真實(shí)執(zhí)行這個(gè)操作的do_fork()函數(shù)確實(shí)簡潔的,并位于kernel/fork.c文件。
以下步驟將被執(zhí)行:
1)        本地變量被設(shè)置為-ENOMEM,當(dāng)fork創(chuàng)建一個(gè)新任務(wù)結(jié)構(gòu)失敗時(shí)將作為錯(cuò)誤代碼返回。
2)        .如果CLONE_PID標(biāo)識被設(shè)置,則返回-EPERM錯(cuò)誤,除非調(diào)用者是空線程。普通用戶線程clone時(shí)不能傳遞CLONE_PID標(biāo)識并期待操作成功。SIFCHLDclone標(biāo)識,對于fork來說,它被認(rèn)為是不相關(guān)的,僅在sys_clone調(diào)用do_fork時(shí)才被認(rèn)為是相關(guān)的。
3)        初始化current->;vfork_sem。它將在sys_vfork函數(shù)為了休眠父進(jìn)程直到子進(jìn)程執(zhí)行mm_release函數(shù)時(shí)使用,就像執(zhí)行其他程序或者中止其他程序一樣。
4)        通過alloc_task_struct()宏分配一個(gè)新的任務(wù)結(jié)構(gòu)。在x86系統(tǒng)上,它僅是一個(gè)GFP_KERNEL優(yōu)先級的gfp。這酒是為何fork系統(tǒng)調(diào)用可能休眠的第一個(gè)原因。如果分配失敗,返回-ENOMEM錯(cuò)誤。
5)        通過結(jié)構(gòu)拷貝*p = *current,將所有當(dāng)前進(jìn)程結(jié)構(gòu)的數(shù)據(jù)都拷貝到新進(jìn)程,或許這個(gè)操作應(yīng)該被memcpy替換。然后,所有不能被子進(jìn)程修改的字段將被設(shè)置為正確的值。
6)        大范圍的內(nèi)核鎖被采用以防止其他部分執(zhí)行本段代碼。
7)        如果父進(jìn)程擁有用戶資源則校驗(yàn)是否超出了RLIMIT_NPROC限制。如果是這樣,則返回-EAGAIN錯(cuò)誤;如果沒有,則通過指定的uid將計(jì)數(shù)器p->;user->;count進(jìn)程數(shù)刷新。
        如果系統(tǒng)所有的任務(wù)數(shù)目超過了最大線程數(shù),返回-EAGAIN錯(cuò)誤。
9)        如果進(jìn)程是依賴預(yù)模塊執(zhí)行的,則增加依賴模塊的引用計(jì)數(shù)。
10)        如果進(jìn)程是依賴預(yù)模塊二進(jìn)制格式的,也增加依賴模塊的引用計(jì)數(shù)。
11)        子進(jìn)程被標(biāo)識為“沒有被執(zhí)行”(p->;did_exec=0)。
12)        子進(jìn)程被標(biāo)識為'not.swappable' (p->;swappable = 0)。
13)        子進(jìn)程被置為TASK_UNINTERRUPTIBLE狀態(tài),即p->;state = TASK_UNINTERRUPTIBLE。
14)        依照clone_flags的數(shù)值設(shè)置子進(jìn)程的p->;flags,如果是簡單fork,p->;flags= PF_FORKNOEXEC。
15)        通過快速算法kernel/fork.c的get_pid()函數(shù)設(shè)置子進(jìn)程號p->;pid。
16)        初始化子進(jìn)程其他任務(wù)結(jié)構(gòu)。最后子進(jìn)程結(jié)構(gòu)被插入到pidhash表中,并且被喚醒。
這樣任務(wù)就被創(chuàng)建了。停止任務(wù)有很多方式。
1)        通過exit系統(tǒng)調(diào)用;
2)        收到一個(gè)中止信號;
3)        被確定異常強(qiáng)制中止;
4)        以func == 1參數(shù)調(diào)用bdflush。
系統(tǒng)調(diào)用的實(shí)現(xiàn)函數(shù)都有sys_前綴,當(dāng)他們通常僅與參數(shù)檢測或者以細(xì)節(jié)方式傳遞信息,真正的操作是由do_**函數(shù)完成的。所以sys_exit()函數(shù)調(diào)用了do_exit來完成操作。盡管如此,內(nèi)核其他部分有時(shí)也通過調(diào)用sys_exit實(shí)現(xiàn)堆do_exit的調(diào)用。
do_exit函數(shù)定義在kernel/exit.c中,按照以下幾個(gè)步驟執(zhí)行:
        獲取內(nèi)核全局鎖;
        在最后調(diào)用一直循環(huán)的schedule()函數(shù);
        設(shè)置任務(wù)狀態(tài)為TASK_ZOMBIE;
        以current->;pdeath_signa信號通知所有的子進(jìn)程;
        以等同于SIGCHLD的信號current.>;exit_signal通知父進(jìn)程;
        釋放fork函數(shù)分配的資源,關(guān)閉已經(jīng)打開的文件;
        在采用少量FPU切換的體系中,不管硬件設(shè)備要求什么都向FPU的所有者傳遞一個(gè)“none”;

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2005-07-05 14:53 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

贊啊,好東西,謝謝分享!頂!

論壇徽章:
0
4 [報(bào)告]
發(fā)表于 2005-07-07 10:08 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

2.3.        Linux 調(diào)度程序

調(diào)度程序的任務(wù)就是從多個(gè)進(jìn)程中挑選一個(gè)訪問當(dāng)前CPU,它在kernel/sched.c中實(shí)現(xiàn),對應(yīng)的被每個(gè)內(nèi)核源文件都引用的頭文件定義在include/linux/sched.h。

任務(wù)結(jié)構(gòu)中對應(yīng)調(diào)度的字段為:
        p->;need_resched:如果schedule()函數(shù)需要在下次喚醒,則設(shè)置本字段。
        p->;counter:到下次運(yùn)行調(diào)度時(shí)間片剩余的時(shí)間嘀噠,由定時(shí)器遞減。當(dāng)這個(gè)字段的值小于或者等于零時(shí),它被重置為零,同時(shí)設(shè)置p->;need_resched。由于這個(gè)可以被進(jìn)程自身修改,有時(shí)也稱之為“動(dòng)態(tài)優(yōu)先級”。
        p->;priority:進(jìn)程的靜態(tài)優(yōu)先級,僅能被系統(tǒng)調(diào)用如nice函數(shù),POSIX.1b 的sched_setparam函數(shù),4.4BSD/SVR4 的setpriority函數(shù)修改。
        p->;rt_priority:實(shí)時(shí)優(yōu)先權(quán)。
        p->;policy: 調(diào)度策略,指定任務(wù)所屬的調(diào)度種類,任務(wù)可以通過調(diào)用sched_setscheduler函數(shù)修改它。有效的值為SCHED_OTHER (傳統(tǒng)UNIX進(jìn)程),SCHED_FIFO (POSIX.1b的FIFO 實(shí)時(shí)進(jìn)程)和SCHED_RR (POSIX循環(huán)實(shí)時(shí)進(jìn)程)。如果要標(biāo)識進(jìn)程綁定到某個(gè)CPU,則可以將SCHED_YIELD與其他任何值“或”操作。例如通過調(diào)用sched_yield系統(tǒng)調(diào)用,一個(gè)FIFO實(shí)時(shí)進(jìn)程將運(yùn)行于○A處于I/O阻塞,○b明確綁定到某個(gè)CPU或者○c被另一個(gè)高優(yōu)先級的實(shí)時(shí)進(jìn)程占用。SCHED_RR和SCHED_FIFO相同,除非時(shí)間片中止后它回到運(yùn)行隊(duì)列末尾。

盡管看起來schedule()函數(shù)非常復(fù)雜,然而調(diào)度算法是簡單的。該函數(shù)看起來復(fù)雜的原因是由于它在一個(gè)函數(shù)中實(shí)現(xiàn)了三個(gè)調(diào)度算法并和精細(xì)的SMP處理細(xì)節(jié)相關(guān)。

顯然,“空”跳轉(zhuǎn)到schedule()有這個(gè)目的:產(chǎn)生最優(yōu)代碼。同樣,標(biāo)識了調(diào)度程序是為2.4重寫的。因此,下面的討論是適合2.2或者更早的內(nèi)核。
讓我們看看函數(shù)細(xì)節(jié):
1)        如果current.>;active_mm == NULL,則出現(xiàn)了錯(cuò)誤。當(dāng)前進(jìn)程即使是一個(gè)內(nèi)核進(jìn)程,都必須在任何時(shí)候有一個(gè)有效的p->;active_mm。
2)        如果tq_scheduler任務(wù)隊(duì)列需要執(zhí)行,則處理它。任務(wù)隊(duì)列為調(diào)度程序稍后執(zhí)行提供了一個(gè)內(nèi)核機(jī)制。
3)        將本地變量prev 和this_cpu分別初始化為當(dāng)前任務(wù)和當(dāng)前CPU。
4)        檢查schedule()是否由中斷處理函數(shù)調(diào)用的,如果出現(xiàn)這種情況是沒有理由的。
5)        釋放內(nèi)核全局鎖。
6)        如過softirq機(jī)制有任務(wù)執(zhí)行,則立即執(zhí)行。
7)        將本地schedule_data結(jié)構(gòu)指針*sched_data初始化為pre-CPU調(diào)度數(shù)據(jù)區(qū)首指針,這個(gè)包含了last_schedule 的TSC值以及上次調(diào)度任務(wù)結(jié)構(gòu)指針。
        獲取runqueue_lock旋轉(zhuǎn)鎖。注意,采用spin_lock_irq()是由于在調(diào)度函數(shù)中我們保證了中斷可用。所以,當(dāng)釋放runqueue_lock時(shí),可以通過重新使其生效替代保存或者恢復(fù)eflags。
9)        任務(wù)狀態(tài)處理:如果處于TASK_RUNNING狀態(tài),任務(wù)繼續(xù);如果處于TASK_INTERRUPTIBLE狀態(tài)且有未處理信號,則任務(wù)調(diào)整到TASK_RUNNING狀態(tài);處于其他任何狀態(tài),任務(wù)都將從執(zhí)行隊(duì)列中移出。
10)        本CPU的空任務(wù)被設(shè)置為下一步(最優(yōu)候調(diào)度者),可是這個(gè)候選者被設(shè)置為非常低的優(yōu)先級,以期待有比它更好的出現(xiàn)。
11)        如果上一個(gè)任務(wù)處于TASK_RUNNING狀態(tài),則當(dāng)前任務(wù)優(yōu)先級被設(shè)置為該任務(wù)的優(yōu)先級,并被標(biāo)識為比空線程更優(yōu)的候選者。
12)        現(xiàn)在開始處理運(yùn)行隊(duì)列,這個(gè)CPU上可被調(diào)度的每個(gè)進(jìn)程的優(yōu)先級都以當(dāng)前值進(jìn)行比較,高優(yōu)先級的活得執(zhí)行機(jī)會(huì)!斑@個(gè)CPU上可被調(diào)度”的概念必須被理解為:在單CPU機(jī)型上,運(yùn)行隊(duì)列的每個(gè)進(jìn)程是合格的可調(diào)度的;在多CPU機(jī)型上,僅僅在其他CPU非已經(jīng)執(zhí)行的進(jìn)程在當(dāng)前CPU是合格可調(diào)度的。優(yōu)先級由goodness()函數(shù)計(jì)算,這個(gè)函數(shù)為所有的實(shí)時(shí)進(jìn)程標(biāo)識非常高的優(yōu)先級,這個(gè)優(yōu)先級高于1000,以至于超過所有的SCHED_OTHER進(jìn)程;這樣,它就只與其他擁有高優(yōu)先級的實(shí)時(shí)進(jìn)程競爭。如果進(jìn)程時(shí)間片結(jié)束,goodness函數(shù)返回0。對于非實(shí)時(shí)進(jìn)程,goodness的初始值設(shè)置為p->;counter,這樣進(jìn)程就近似于在CPU空閑時(shí)獲得資源,也就是說交互進(jìn)程較CPU范圍計(jì)數(shù)器更有特權(quán)。常量PROC_CHANGE_PENALTY試圖表達(dá)了“cpu affinity”。這也為內(nèi)核進(jìn)程提供了微小的優(yōu)勢。
13)        如果goodness返回值為零,則遍歷進(jìn)程入口列表,并通過簡單的算法重新計(jì)算他們的動(dòng)態(tài)優(yōu)先級:
recalculate:
{
struct task_struct *p;
spin_unlock_irq(&runqueue_lock);
read_lock(&tasklist_lock);
for_each_task(p)
p.>;counter = (p.>;counter >;>; 1) + p.>;priority;
read_unlock(&tasklist_lock);
spin_lock_irq(&runqueue_lock);
}

注意,在重新計(jì)算前停止了runqueue_lock鎖。這個(gè)原因是我們將進(jìn)入到進(jìn)程集合中,這需要較長的時(shí)間,當(dāng)我們在這個(gè)CPU計(jì)算時(shí),schedule函數(shù)可以在另外的CPU上被調(diào)用,并為其挑選一個(gè)合適的進(jìn)程。無可否認(rèn)的,這就是一個(gè)矛盾,因?yàn)楫?dāng)我們在這個(gè)CPU上挑選合適優(yōu)先權(quán)的進(jìn)程時(shí),其他CPU正運(yùn)行schedule計(jì)算動(dòng)態(tài)的優(yōu)先級。
14)        毫無疑問,下一個(gè)任務(wù)將會(huì)被調(diào)度,所以初始化next.>;has_cpu為1,next.>;processor為this_cpu,F(xiàn)在runqueue_lock可以被釋放了。
15)        如果該任務(wù)有一次被調(diào)用(next == prev),那么可以簡單獲取內(nèi)核全局鎖并返回,也就是跳過所有的硬件層(registers, stack 等)和虛擬內(nèi)存相關(guān)操作。
16)        switch_to宏依賴于體系結(jié)構(gòu)。在i386上,它與這些相關(guān):a) FPU 處理; b)LDT 處理; c) 重裝載段注冊器;d) TSS處理 和 e)重裝載調(diào)試注冊器。

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2005-07-07 16:59 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

GOOD!~~~

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2005-07-11 16:07 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

呵呵,謝謝支持,下面還有

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2005-07-11 16:09 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

2.4.        Linux執(zhí)行鏈表

在遍歷等待隊(duì)列之前,首先執(zhí)行Linux標(biāo)準(zhǔn)的雙向執(zhí)行鏈表。等待隊(duì)列用起來繁雜,行話稱之為“l(fā)ist.h 實(shí)現(xiàn)”,因?yàn)樗钕嚓P(guān)的文件是include/linux/list.h。
基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)是list_head結(jié)構(gòu):
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr).>;next = (ptr); (ptr).>;prev = (ptr); \
} while (0)
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr).(unsigned long)(&((type *)0).>;member)))
#define list_for_each(pos, head) \
for (pos = (head).>;next; pos != (head); pos = pos.>;next)
前三個(gè)宏用來初始化一個(gè)next和prev指針皆指向自身的空鏈表,宏可以用到的地方明顯受限于c語法約束。例如,LIST_HEAD_INIT()用來初始化結(jié)構(gòu)元素,第二個(gè)宏用來初始化靜態(tài)變量,第三個(gè)用于函數(shù)內(nèi)部。
list_entry()宏提供了對單獨(dú)鏈表元素的操作,例如(fs/file_table.c:fs_may_remount_ro()函數(shù)):
struct super_block {
...
struct list_head s_files;
...
} *sb = &some_super_block;
struct file {
...
struct list_head f_list;
...
} *file;
struct list_head *p;

for (p = sb.>;s_files.next; p != &sb.>;s_files; p = p.>;next) {
struct file *file = list_entry(p, struct file, f_list);
// do something to 'file'
}
一個(gè)使用list_for_each()宏的最好例子就是任務(wù)調(diào)度函數(shù)中遍歷運(yùn)行隊(duì)列以查找高優(yōu)先級的進(jìn)程部分。
static LIST_HEAD(runqueue_head);
struct list_head *tmp;
struct task_struct *p;

list_for_each(tmp, &runqueue_head) {
p = list_entry(tmp, struct task_struct, run_list);
if (can_schedule(p)) {
int weight = goodness(p, this_cpu, prev.>;active_mm);
if (weight >; c)
c = weight, next = p;
}
}
p.>;run_list 被定義成task_struct結(jié)構(gòu)內(nèi)的list_head結(jié)構(gòu)成員run_list,serves為指向鏈表的指針。向鏈表里面刪除或者添加一個(gè)元素由list_del()/list_add()/list_add_tail()三個(gè)宏完成。下面的例子向運(yùn)行隊(duì)列添加并刪除一個(gè)任務(wù)。
static inline void del_from_runqueue(struct task_struct * p)
{
nr_running..;
list_del(&p.>;run_list);
p.>;run_list.next = NULL;
}
static inline void add_to_runqueue(struct task_struct * p)
{
list_add(&p.>;run_list, &runqueue_head);
nr_running++;
}
static inline void move_last_runqueue(struct task_struct * p)
{
list_del(&p.>;run_list);
list_add_tail(&p.>;run_list, &runqueue_head);
}
static inline void move_first_runqueue(struct task_struct * p)
{
list_del(&p.>;run_list);
list_add(&p.>;run_list, &runqueue_head);
}

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2005-07-11 16:12 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

pf~pf~,精神上嚴(yán)重支持!!

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2005-07-13 13:40 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

2.5.        等待隊(duì)列

當(dāng)進(jìn)程要求內(nèi)核完成一件當(dāng)前不會(huì)發(fā)生但稍后可能發(fā)生的事情時(shí),它就進(jìn)入休眠并在事件條件符合時(shí)被喚醒。內(nèi)核實(shí)現(xiàn)機(jī)制的其中之一就被稱為“等待隊(duì)列”。
Linux實(shí)現(xiàn)允許通過TASK_EXCLUSIVE標(biāo)記來喚醒。對于等待隊(duì)列,你可以采用通用的隊(duì)列,然后簡單地sleep_on /sleep_on_timeout /interruptible_sleep_on /interruptible_sleep_on_timeout;也可以定義自己的隊(duì)列,并通過add/remove_wait_queue向它添加刪除自己,在需要時(shí)通過wake_up/wake_up_interruptible來喚醒。
等待隊(duì)列的第一種用法的例子就是頁分配(mm/page_alloc.c:__alloc_pages()函數(shù))與kswapd內(nèi)核守護(hù)進(jìn)程(mm/vmscan.c:kswap()函數(shù))的交互,也就是定義在mm/vmscan.c里的kswapd_wait等待隊(duì)列。Kswapd守護(hù)進(jìn)程在這個(gè)隊(duì)列休眠,當(dāng)頁分配函數(shù)需要釋放一些內(nèi)存頁的時(shí)候它就被喚醒。
自治等待隊(duì)列用法的例子就是用戶進(jìn)程通過read系統(tǒng)調(diào)用請求數(shù)據(jù)與中斷環(huán)境的內(nèi)核提供數(shù)據(jù)之間的交互。中斷處理可能如下(drivers/char/rtc_interrupt()):

static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
spin_lock(&rtc_lock);
rtc_irq_data = CMOS_READ(RTC_INTR_FLAGS);
spin_unlock(&rtc_lock);
wake_up_interruptible(&rtc_wait);
}

這樣,中斷處理函數(shù)通過讀取一些設(shè)備指定的I/O端口獲得了數(shù)據(jù),然后喚醒rtc_wait等待隊(duì)列中的任務(wù),F(xiàn)在,read系統(tǒng)調(diào)用可以實(shí)現(xiàn)為:

ssize_t rtc_read(struct file file, char *buf, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
ssize_t retval;

add_wait_queue(&rtc_wait, &wait);
current.>;state = TASK_INTERRUPTIBLE;
do {
spin_lock_irq(&rtc_lock);
data = rtc_irq_data;
rtc_irq_data = 0;
spin_unlock_irq(&rtc_lock);

if (data != 0)
break;

if (file.>;f_flags & O_NONBLOCK) {
retval = .EAGAIN;
goto out;
}

if (signal_pending(current)) {
retval = .ERESTARTSYS;
goto out;
}

schedule();
} while(1);
retval = put_user(data, (unsigned long *)buf);
if (!retval)
retval = sizeof(unsigned long);

out:
current.>;state = TASK_RUNNING;
remove_wait_queue(&rtc_wait, &wait);
return retval;
}

rtc_read將執(zhí)行以下步驟操作:
1)        定義一個(gè)指向當(dāng)前進(jìn)程環(huán)境的等待隊(duì)列元素;
2)        將此元素添加倒rtc_wait等待隊(duì)列;
3)        將當(dāng)前環(huán)境標(biāo)識為TASK_INTERRUPTIBLE,這意味著他下次休眠后不再被調(diào)度;
4)        檢查是否有數(shù)據(jù)有效?如果有則跳出,拷貝數(shù)據(jù)到用戶緩沖區(qū),自身任務(wù)狀態(tài)調(diào)整為TASK_RUNNING,并從等待隊(duì)列中移出,然后返回。
5)        如果目前沒有數(shù)據(jù),則檢查用戶是否指定了非阻塞I/O。
6)        同樣檢查是否有信號等待處理。如果是,則通知上層必要時(shí)重啟系統(tǒng)調(diào)用。
7)        最后休眠,直到被中斷處理函數(shù)喚醒。如果任務(wù)自身狀態(tài)不是TASK_INTERRUPTIBLE,則調(diào)度函數(shù)會(huì)在稍后有數(shù)據(jù)可用時(shí)調(diào)度該任務(wù),這會(huì)導(dǎo)致不必要的處理。
這非常有價(jià)值的一點(diǎn)就是,采用等待隊(duì)列就比較容易的實(shí)現(xiàn)poll系統(tǒng)調(diào)用:

static unsigned int rtc_poll(struct file *file, poll_table *wait)
{
unsigned long l;

poll_wait(file, &rtc_wait, wait);

spin_lock_irq(&rtc_lock);
l = rtc_irq_data;
spin_unlock_irq(&rtc_lock);

if (l != 0)
return POLLIN | POLLRDNORM;
return 0;
}

所有的工作都在獨(dú)立于設(shè)備的函數(shù)poll_wait中完成,它完成了必須的等待隊(duì)列操作。我們所要做的就是將其指向一個(gè)由我們設(shè)備相關(guān)的中斷處理函數(shù)喚醒的等待隊(duì)列。

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2005-07-14 13:58 |只看該作者

Linux 2.4 內(nèi)核說明文檔(進(jìn)程與中斷管理篇)

好帖呀,我得存下來看看
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP