地址映射與共享

這次實驗做的着實好糾結。。想來也不是說有多難,關鍵是給的資料太少了,尤其是移植到0.11下那一步,指導書就沒幾句話,我左看右看也沒搞明白到底要幹什麼。。趙炯同志的那本書又好多要看的,總是看不進去。。不過靜下心去看的話還真的能獲取好多有用的信息,加上各種百度Google,也不算太難,關鍵是查資料實在是好費時間啊。。。由於是第一次直接在代碼中對內存操作,一會兒虛擬,一會線性,一會物理的,遇到了好多問題。。所謂好記性不如爛筆頭,我還是有必要總結一下。。。


先說ubuntu下的那個程序吧,上次都編的差不多了,主要是熟悉了shmget, shmat的操作就好了,廣泛蒐羅之後比較好的貼子就拿來了,以後可能用得着。但是編這次程序的時候居然發現了上次的bug。。。。

1、我開闢了兩塊共享內存,一個num用來存緩衝區中有多少產品,這樣指針直接後移這麼多就好了。但是producer.c中通過判斷num是否爲零來初始化,但是可能出現消費完的情況,這樣就出現了範老師最討厭的問題:有時候對有時候錯,錯了還自己偷偷知道。。因爲不一定什麼時候消費完,也可能一直沒有出現這樣的情況,所以加了個flag用來判斷是否是第一次,再初始化。

2、生產者生產的時候,我是通過讀取最後一個數字,然後++,在寫進去的,所以如果消費完的話,是沒有內容可以讀取出來的,所以把受範老師影響的while循環,改成了for,直接靠循環次數來記錄編號,不用再讀取了。。

這樣看來,上次程序就有這些問題,只不過當時是兩個進程,在一個程序裏,可能切換的比較頻繁?總之沒有發現消費者消費完的情況,純屬僥倖啊。。還是自己考慮問題不全面。。好丟人。。我還去問TA了,結果問着自己悟出來了,有時候自己在那想不如給別人講一下自己的思路呢,講的時候比較詳細,就能縷一遍,可能自己就明白了。。不過暴露了上次的問題。。。希望不要扣分呀~~~委屈


接下來是糾結了好幾天的0.11下的程序了。。看了十三章之後就清楚了許多,但具體實現還是找了好多網上的資料。首先開闢共享內存有那個現成的get_free_page()函數(這個居然上次就用到了好神奇~),但是獲得的是物理地址,而我們編程時纔不關心這些呢,使用的是虛擬地址,所以需要找到程序中一段空閒的線性地址,減去基地址就是虛擬地址啦~同時也要把找到的線性地址映射到get_free_page()獲得的物理地址上去。。。我是在shmget函數中實現找到空閒的物理內存,在shmat函數中找到空閒的線性地址,映射好,並且返回虛擬地址供用戶操作。。但是思路清晰了問題又來了,還是總結一下吧~~

1、怎麼獲得空閒的線性地址呢?我們的程序的線性地址中有代碼段,數據段,堆棧段,也有空閒的部分,參考書最後有一個get_base的函數,看來是返回一個地址的,但裏面的參數我就不明白了。。於是我查了下task_struct的結構,本想知道ldt這個數組是幹嘛的,結果收穫更多啊~看紅色的部分就好,不解釋。。。


struct task_struct {
/*----------------------- these are hardcoded - don't touch -----------------------*/
       long state;       // 進程運行狀態(-1不可運行,0可運行,>0以停止)
       long counter;  // 任務運行時間片,遞減到0是說明時間片用完
       long priority;  // 任務運行優先數,剛開始是counter=priority
       long signal;     // 任務的信號位圖,信號值=偏移+1
       struct sigaction sigaction[32];       //信號執行屬性結構,對應信號將要執行的操作和標誌信息
       long blocked;  // 信號屏蔽碼
/*----------------------------------- various fields--------------------------------- */
       int exit_code;  // 任務退出碼,當任務結束時其父進程會讀取
unsigned long start_code,end_code,end_data,brk,start_stack;
              // start_code   代碼段起始的線性地址
              // end_code     代碼段長度 
              // end_data      代碼段長度+數據段長度
              // brk             代碼段長度+數據段長度+bss段長度
              // start_stack   堆棧段起始線性地址
              // end_data      代碼段長度+數據段長度    

       long pid,father,pgrp,session,leader;      
              // pid       進程號
              // father   父進程號
              // pgrp     父進程組號
              // session 會話號
              // leader 會話首領
       unsigned short uid,euid,suid;
              // uid       用戶標id
              // euid     有效用戶id
              // suid     保存的用戶id
       unsigned short gid,egid,sgid;
              // gid       組id
              // egid     有效組id
             // sgid     保存組id
long alarm; // 報警定時值 long utime,stime,cutime,cstime,start_time; // utime 用戶態運行時間 
              // stime 內核態運行時間 
              // cutime 子進程用戶態運行時間 
              // cstime 子進程內核態運行時間 
              // start_time 進程開始運行時刻 
       unsigned short used_math; // 標誌,是否使用了387協處理器
/* ----------------------------------file system info-------------------------------- */ 
       int tty; // 進程使用tty的子設備號,-1表示沒有使用 
       unsigned short umask; //文件創建屬性屏蔽碼 
       struct m_inode * pwd; // 當前工作目錄的i節點 
       struct m_inode * root; // 根目錄的i節點 
       struct m_inode * executable; // 可執行文件的i節點 
       unsigned long close_on_exec; // 執行時關閉文件句柄位圖標誌 
       struct file * filp[NR_OPEN]; // 進程使用的文件
/*------------------ldt for this task 0 - zero 1 - cs 2 - ds&ss -------------------*/ 
      struct desc_struct ldt[3]; // 本任務的ldt表,0-空,1-代碼段,2-數據和堆棧段
/* ---------------------------------tss for this task ---------------------------------*/ 
       struct tss_struct tss; // 本任務的tss段};


其實不用get_base也可以,直接用current->start_code獲得基地址就行,我試過了,是可以的。但是我有個疑問,就是獲得的基地址能不能直接加上長度呢?獲得的是否就是加上這段長度的地址?答案是肯定的,所以是我考慮複雜了,直接相加就好。。。

2、後來程序運行出現了死循環,又不知道怎麼退出,於是我把consumer也在後臺運行了,然後打印了好多信息在文件中。。原來是sem_open的時候忘記考慮第二次open了。。於是加上了判斷,看名字是否已經存在,但名字是用戶態和內核態的比較,還是要用get_fs_byte來獲取,然後在比較的。。因爲上次在一個程序裏,只open了一次,就沒有這個問題。由此觀之,不要以爲上次運行的對的就萬事大吉了啊!!情況不同了,也不能拋棄人家sem.c啊。。

3、但是,死循環依然存在。。。並且數字特別規律,就是10——0一循環,難道。。。。果然!粗心的我居然兩次都正好獲得了基地址加上current->brk的空閒內存,於是。。。衝突了。。。所以我又往後移了shmid*1024*4的長度,shmid都不一樣,這樣就不會衝突了。。。

4、由於剛開始沒有出現trying to free free page的提示,後來在別人的建議下將生產者生產的數目變成了40,於是就出現了。。經過多次實驗,發現生產者生產數目少的話就還出現panic,大的話就不會。。想了想,在producer.c的最後打印了輸出語句,結果是這樣的:生產數目少就會先把生產者運行完,即文本中輸出的第一行是生產者結束,接下來是消費者的打印內容。。而生產數目多的時候,生產者進程就一直沒有結束了,因爲一直在等待消費才能繼續生產,沒有結束則沒有free_page,也就不會有panic了。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章