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