Linux Driver函式透過syscall被呼叫時,虛擬記憶體User Mode部分是映射到呼叫syscall者的Process記憶體內容,因此像是copy_from_user之類的函式可以直接複製呼叫者的記憶體資料。
但是在這種情況下如果要寫入資料到其他Process的記憶體,就必須切換User Mode的映射。一般CPU的虛擬記憶體與物理記憶體映射對應是透過記憶體映射表,由CPU的CR3暫存器指向當前的記憶體映射表。
因此透過更改CR3暫存器,我們就可以暫時將User Mode的映射切換到其他Process,並透過copy_from_user或copy_to_user讀取或寫入到該Process。
部份Code(將Process A的資料複製到當前Process):
因此透過更改CR3暫存器,我們就可以暫時將User Mode的映射切換到其他Process,並透過copy_from_user或copy_to_user讀取或寫入到該Process。
部份Code(將Process A的資料複製到當前Process):
void *src; //指向Process A的User Mode資料記憶體位置 void *dst; //指向要寫入到當前(current)process的User Mode記憶體位置 void *buf; //臨時緩衝區 int size; //要複製的大小 struct task_struct *a_task; //Process A的task_struct,a_task = pid_task(find_vpid(Process A的PID),PIDTYPE_PID) struct mm_struct *oldmm; oldmm = current->mm; //保存當前mm_struct current->mm = a_task->mm; current->active_mm = current->mm; //如果當前Process不是一個Kernel Thread,active_mm = current->mm load_cr3(current->mm->pgd); //更新CR3 copy_from_user(buf,src,size); //從Process A複製資料到緩衝區 current->mm = oldmm; //恢復當前mm_struct current->active_mm = current->mm; load_cr3(current->mm->pgd); //恢復CR3 copy_to_user(dst,buf,size); //將緩衝區資料寫到當前Process的目標位置
一開始,我沒對current->mm和current->active_mm作替換,但是由於Linux在進程調度時,會根據current->mm載入CR3,為了避免呼叫完load_cr3後發生進程調度,導致CR3被改回去(經過測試,確定會發生此狀況),用preempt_disable()暫時關閉搶佔,保證從load_cr3到copy_to_user結束都不會發生進程調度。
但是後來測試時,發現對於size比較大時,會發生copy_to_user失敗的情形。追究原因,應該是因為copy_from_user和copy_to_user皆會發生Page Fault(複製時剛好該記憶體頁被置換出物理記憶體,這時會發生Page Fault,然後Linux去把該記憶體頁重新映射回物理記憶體中),但是關閉搶佔的情況下,是無法處理Page Fault,導致讀寫失敗。
寫些比較親民的啦...寫這我都有看沒有懂..
ReplyDelete