Linux核心設計與實現(原書第3版)筆記
志_祥發表於2014-10-31
Linux核心設計與實現(原書第3版)
- p28 do_fork():有意讓child先執行,一般child馬上會exec(),避免了CoW的額外開銷 ...?
- 執行緒建立/fork/vfork 3者呼叫clone()時引數的不同
- wait4
- O(1)排程:根據nice分配時間片
- 排程器類?CFS是normal情況下的?
- CFS:優化互動任務的實時響應,挑選vruntime最小的那個,rbtree中的leftmost
- p47 規劃化:
- if(!sleep) se->vruntime -= cfs_rq->min_vruntime;
- 休眠程式:位於某個wait_queue上(等待佇列使用:inotify_read(),這裡是非同步IO嗎?)
- 可中斷的:如果收到訊號,會提前醒過來
- 使用者搶佔:核心返回user space時,如果need_sched,會導致schedule()呼叫
- 核心搶佔:只要沒有持有鎖,正在執行的程式碼就是可重入的,也是可搶佔的(安全性指的是不會崩潰?但正確性需要使用者自己保證?)
- thread_info#preempt_count 加鎖的次數
- if( need_sched && !preempt_count) schedule();
- 如果程式碼自行呼叫schedule(),它應該保證該搶佔排程是安全的
- 2種實時排程:SCHED_FIFO(一直執行,除非讓出CPU)、SCHED_RR(一直執行,直到時間片用光,帶deadline的執行?)
- 處理器繫結:task_struct#cpus_allowed sched_setaffinity()
- syscall
- *為什麼getpid()返回的是tgid
- 軟中斷:int $0x80,或sysenter(更快地陷入)
- 許可權檢查:suser() --> capable()
- 使用者空間訪問系統呼叫(C庫的實現方式):_syscalln() n為0~6,代表需要傳遞的引數個數
- 確保你需要syscall,否則使用sysfs更好
- 核心資料結構
- list_head:要遍歷的同時刪除,list_for_each_entry_safe(p, next, header, member_name)
- 佇列 kfifo
- idr:對映使用者空間UID
- rbtree
- 中斷
- 註冊:request_irq(irq_number, handler, flags*, name, dev) 由於它最終會呼叫kmalloc(),可能會睡眠!
- 無須可重入
- handler:使用spin_lock以避免SMP上其他CPU同時訪問
- ret_from_intr()
- /proc/interrupts
- local_irq_disable();之前,先local_irq_save(&flags);安全一點,flags不能傳遞給另一函式,必須是同一stack frame
- 狀態判斷:in_interrupt()(中斷處理)、in_irq()(下半部)
- 下半部
- 最早的叫bottom half(BH)
- 任務佇列(task queue):不夠靈活,對於效能要求高的網路子系統,不能勝任
- 2.3+ 軟中斷與tasklet:2個不同型別的tasklet可同時執行
- 2.5+ 徹底丟棄BH、任務佇列 --> 工作佇列
- 軟中斷:softirq_action、softirq_vec[NR_SOFTIRQ=32](實際使用9個,對映為優先順序?)
- tasklets:HI_SOFTIRQ TASKLET_SOFTIRQ(內部用動態的連結串列組織)
- ksoftirqd/n
- work queue:退後到某個核心執行緒執行(!oi see)
- workqueue_struct 工作執行緒worker_thread()
- cpuworkqueue_struct 每CPU的
- work_struct
- 老的任務佇列及keventd
- 核心同步
- atomic_t
- spin lock
- 如果確定中斷在加鎖前是啟用的,則無須儲存irq狀態:spin_lock_irq(&lk);//這個函式名其實挺費解的~~
- spin_lock_bh():加鎖,同時禁止所有下半部的執行
- 讀寫自旋鎖(rwlock):遞迴地獲得同一個讀鎖是安全的
- 訊號量
- 不一致的命名方法:init_MUTEX?
- down_interruptable/up(&mr_sem);
- rw_semaphore
- 互斥體(mutex)指的是任何可以睡眠的強制互斥鎖,比如計數為1的訊號量
- 完成變數(completion)
- 歷史遺留話題:BLK
- 2.6+ 順序鎖(seqlock_t):類似於STM?,seq鎖對寫更優先
- 搶佔:preempt_disable(); 支援巢狀
- barrier:rmb(); wmb(); read_barrier_depends();*(確保屏障前的讀在屏障後的讀之前完成)barrier()(編譯器屏障)
- 定時器和時間管理
- HZ:x86預設為100(10ms單位),其他體系結構為250或1000
- jiffies
- time_before巨集:把unsigned long轉換為long再相減!
- struct timespec xtime; //精確到ns?
- timer_list
- 延遲執行:e.g.重新設定網路卡的以太模式需要2ms
- udelay ndelay mdelay BogoMIPS
- schedule_timeout()
- mm
- struct page: 在於描述實體記憶體本身,而不是包含在裡面的資料
- struct zone:x86-32上normal區域為16~896MB
- kmalloc()
- gfp_mask
- vmalloc()
- slab層
- 每個快取記憶體:p = kmem_cache("task_struct", sizeof(struct task_struct), ARCH_MIN_TASKALIGN, SLAB_PANIC|SLAB_NOTRACK, NULL);
- 核心棧:1或2頁,如果1頁,則使用單獨的中斷棧
- 高階記憶體對映:__GFP_HIGHMEM獲得的頁沒有邏輯地址?
- 永久對映:kmap 有數量限制
- 臨時對映:kmap_atomic
- 老式的每CPU的分配:data[NR_CPUS] --> get_cpu()禁止核心搶佔
- 新的每CPU:DEFINE_PER_CPU(type,name); --> get_cpu_var(name)
- VFS
- 操作物件:{super, inode, dentry, file}_operations
- inode
- permission(inode,mask):允許特定的訪問模式,返回0,支援訪問控制鏈(ACLS)的檔案系統需要
- setxattr:允許key/value與檔案關聯(hiden/invisible?)
- struct file:沒有對應的磁碟資料,通過f_dentry -> 指向相關的inode -> 檔案是否dirty
- 和檔案系統相關的資料結構
- struct file_system_type;
- struct vfsmount;
- 和程式相關資料結構
- struct files_struct;
- 塊I/O層
- struct buffer_head; BH_xxx標誌,弊端:大塊資料的IO分解為多個buffer_head,不必要的負擔和空間浪費
- struct bio; 代表的是I/O操作,而buffer_head僅描述磁碟的一個塊
- struct bio_vec: { page, offset, len } 三元組
- 請求佇列(request_queue)
- I/O排程
- Linus電梯:嘗試合併讀請求;防止飢餓;根據扇區位置調整插入(優化物理訪問效能?);預設到佇列尾部
- 最終期限:寫是非同步的,而讀必然是同步阻塞的!--> 減少讀請求飢餓現象(降低了全域性吞吐量);3個佇列:讀、寫、派發
- 預測I/O(預設排程):請求提交後有意空閒片刻,如果這期間應用有相鄰的其他讀請求,則可提高效能(總感覺這裡的設計考慮因素並不多??)
- CFQ:I/O請求按程式放入不同佇列
- Noop:僅執行合併,用於無尋道開銷的,如快閃記憶體卡
- 程式地址空間
- mm_struct
- vm_area_struct
- VMA標誌,vm_operations_struct
- 可通過記憶體描述符的mmap和mm_rb域訪問記憶體區域(分別使用list和rbtree)
- 頁表:PGD、PMD、PTE
- 頁快取記憶體和回寫
- struct address_space; //physical pages of a file ...
- radix_tree
- flusher執行緒、pdflush
- 裝置與模組
- 字元裝置(只提供流式訪問)、塊裝置(可隨機讀寫)、網路裝置
- kobject ktype kset
- sysfs:把kobject與dentry聯絡起來
- kobject_uevent():核心空間向使用者空間傳送訊號
- 除錯
- 可移植性
- 位元組順序:u32 __cpu_to_be32(u32) 把cpu位元組順序轉換為高位優先