《現代操作系統》第2章 進程與線程

1.  進程

一個正在執行的程序實例

2.  進程模型

概念上每個進程擁有自己的虛擬CPU,實際上真正的CPU在各進程之間來回切換(多道程序設計)

3.  創建進程

1) 系統初始化

2) 執行了正在運行的進程所調用的進程創建系統調用fork()

3) 用戶請求創建一個新進程

4) 一個批處理作業的初始化

4.  進程終止

1) 正常退出(自願的)

2) 出錯退出(自願的)

3) 嚴重錯誤(非自願)

4) 被其他進程殺死(非自願)

5.  進程的層次結構

UNIX:進程與其子進程構成一個進程組

WINDOWS:沒有層次概念,父進程用句柄控制子進程

6.  進程狀態

運行、阻塞、就緒

1) 運行->阻塞:進程爲等待輸入而阻塞

2) 運行->就緒:調度程序選擇另一個進程

3) 就緒->運行:調度程序選擇這個進程

4) 阻塞->就緒:出現有效輸入

其他狀態:掛起、終止

7.  進程的實現

進程表表項(進程控制塊)包括與進程管理、存儲管理、文件管理有關的信息。

中斷向量:中斷髮送時,保存狀態後,計算機跳轉到中斷向量所指示位置完成操作。

多道程序設計中的CPU利用率=1-p^n

(n進程數,p用於I/O操作的時間與等待時間的時間比)

8.  線程(迷你進程)
9.  進程與線程內容的比較

進程:

地址空間

全局變量

打開文件清單

子進程

即將發生的報警

信號與信號處理程序

賬號信息

線程:

程序計數器

寄存器

堆棧

狀態

 

一組線程共享資源

兩種主要方法實現進程包:在用戶空間中和在內核中(另有混合實現)

10. 內核級線程的調度程序激活機制

目的:模擬內核級線程的功能,提供(在用戶空間才能實現的)更好的性能和更大的靈活性,避免了不必要的用戶空間/內核空間轉換,提高效率。

內核給每個進程安排一定數量的虛擬處理器,將線程分配的處理器上

11. 彈出式線程

分佈式系統中另一種處理消息到來的方式,一個消息的到達導致系統創建一個處理該消息的程序。

12. 進程間通信

競爭條件:兩個或多個進程讀寫共享數據,最後結果取決於進程運行的精確時序

臨界區:對共享內存進行訪問的程序片段

避免競爭條件需滿足以下4個條件:

1)    任何兩個進程不能同時處於臨界區

2)    不應對CPU數量和速度做任何假設

3)    臨界區外運行的進程不得阻塞其他進程

4)    不得使進程無限期等待進入臨界區

13. 實現忙等待的互斥的方案

1)    屏蔽中斷

2)    鎖變量

3)    嚴格輪換法(忙等待)

4)    Peterson解法

5)    TSL指令(需要硬件支持,“測試並加鎖”指令)

14. 信號量

用一個整型變量累計喚醒次數

P/down:大於0,減1;爲0,將進程睡眠

V/up:增1。

整個操作是不可分割的原子操作

15. 使用信號量解決“生產者-消費者問題”

(1)含有嚴重競爭條件的生產者-消費者問題(下面代碼)

對count的訪問未加限制。

有可能出現以下情況:緩衝區爲空,消費者剛剛讀取count的值發現它爲0。此時調度程序把 CPU切換給了生產者。生產者想緩衝區中加入一個數據項,count加1,並且它推斷由於剛纔count爲0,所以消費者一定在睡眠,所以調用 wakeup來喚醒消費者。但消費者並未睡眠,所以wakeup信號丟失。當消費者下次運行時,它將測試先前讀到的count值,發現爲0,於是睡眠。生 產者遲早會填滿整個緩衝區,然後睡眠。最後兩個進程都將永遠睡眠下去。

#define N 100           /*緩衝區中的槽數目*/
int count = 0;            /*緩衝區中的數據項數目*/
void producer(void)
{
     int item;
     while(TRUE)
     {
       item=produce_item();
       if(count==N) sleep();
       insert_item(item);
       count=count+1;
       if(count==1) wakeup(consumer);   /*count==1說明生產之前count==0,consumer進程應該在睡眠,遂喚醒它*/
     }
}
 
void consumer(void)
{
    int item;
    while(TRUE)
     {
       if(count==0) sleep();
       item=remove_item();
       count=count-1;
       if(count==N-1) wakeup(producer); /*count==N-1說明消費之前緩衝區滿了,生產者應該在睡眠,遂喚醒*/
       consume_item(item);
     }
}

 

(2)使用信號量解決“生產者-消費者問題”

信號量mutex用於互斥,保證任意時刻只有一個進程讀寫緩衝區和相關變量。互斥是避免混亂所必需的操作。

信號量full和empty用於實現同步,保證某種事件的順序發生或不發生。它們保證當緩衝區滿的時候生產者停止運行,以及當緩衝區空的時候消費者停止運行。

#define N 100           /*緩衝區中的槽數目*/
typedef int semaphore;      /*信號量是一種特殊的整型數據*/
semaphore mutex = 1;       /*控制對臨界區的訪問*/
semaphore empty = N;      /*計數緩衝區的空槽數目*/
semaphore full=0;               /*計數緩衝區的滿槽數目*/
 
void producer(void)
{
     int item;
     while(TRUE)
     {
       item=produce_item();
       down(&empty);     /*空槽數目減一*/
       down(&mutex);     /*進入臨界區*/
       insert_item(item);
       up(&mutex);        /*離開臨界區*/
       up(&full);            /*將滿槽的數目加1*/
     }
}
 
void consumer(void)
{
    int item;
    while(TRUE)
     {
       down(&full);
       down(&mutex);
       item=remove_item();
       up(&mutex);
       up(&empty);
       consume_item(item);
     }
}


16. 互斥量

一個二進制位(解鎖和加鎖),是信號量的簡化版本。

17. 利用線程解決“生產者-消費者問題”

#include <stdio.h>
#include <pthread.h>
#define MAX 10 //需要生產的數量
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer = 0;//生產者、消費者使用的緩衝區
 
void *producer(void *ptr)
{
   int i;
   for(i=1; i<=MAX; i++)
    {
       pthread_mutex_lock(&the_mutex); //互斥使用緩衝區
       while(buffer !=0) pthread_cond_wait(&condp, &the_mutex);
       printf("procucer produce item %d\n",i);
       buffer = i; //將數據插入緩衝區
       pthread_cond_signal(&condc);//喚醒消費者
       pthread_mutex_unlock(&the_mutex);//釋放緩衝區
    }
   
   pthread_exit(0);
 
}
 
void *consumer(void *ptr)
{
 
   int i;
   for(i=1; i<=MAX; i++)
    {
       pthread_mutex_lock(&the_mutex);//互斥使用緩衝區
       while(buffer ==0) pthread_cond_wait(&condc, &the_mutex);
       printf("consumer consume item %d\n",i);
       buffer = 0;//從緩衝區中取出數據
       pthread_cond_signal(&condp);//喚醒生產者
       pthread_mutex_unlock(&the_mutex);//釋放緩衝區
    }
   pthread_exit(0);
 
}
 
int main(int argc, char *argv[])
{
   pthread_t pro, con;
   pthread_mutex_init(&the_mutex, 0);
   pthread_cond_init(&condc,0);
   pthread_cond_init(&condp,0);
   pthread_create(&con, 0, consumer, 0);
   pthread_create(&pro, 0, producer, 0);
   pthread_join(pro,0);
   pthread_join(con,0);
   pthread_cond_destroy(&condc);
   pthread_cond_destroy(&condp);
   pthread_mutex_destroy(&the_mutex);
   return 0;   
}


18. 其它同步機制

(1)管程

高級同步原語,一個管程是一個有過程、變量及數據結構等組成的一個集合,組成一個特殊模塊或軟件包。

任一時刻管程中只能有一個進程。

(2)消息傳遞

(3)屏障:除非所有的進程都就緒準備下一階段,否則任何進程都不能進入下一階段。

 

19. 調度

選擇下一個佔用資源的進程。

何時調度:

1) 創建新進程後

2) 一個進程退出時

3) 一個進程阻塞時

4) 發生I/O中斷時

5) 時鐘中斷時

調度算法的目標:公平、策略強制性、平衡

調度算法的分類:搶佔式/非搶佔式

三種環境:

1) 批處理(吞吐量、週轉時間、CPU利用率)

2) 交互式(響應時間、均衡性)

3) 實時(滿足截止時間、可預測性)

20. 調度算法

批處理:

1)先來先服務

2)最短作業優先

3)最短剩餘時間優先

交互式:

1)輪轉調度(分時間片,每個進程在一個時間片內運行)

2)優先級調度

3)多級隊列(前兩種結合)

4)最短進程優先(老化算法估計運行時間aT0+(1-a)T1,如a=1/2,時間序列爲T0,T0/2+T1/2,T0/4+T1/4+T2/2,三輪過後T0在新的估計值中所佔的比重下降到1/8)

5)保證調度

6)彩票調度

7)公平分享調度(保證用戶分配到的CPU時間的公平)

實時:

硬實時/軟實時

週期性/非週期性

21. 哲學家就餐問題的一種解法

#define N 5                                      /*哲學家數目*/
#define LEFT (i-1+N)%N                     /*i的左鄰號碼*/
#define RIGHT (i+1)%N               /*i的右鄰號碼*/
#define THINKING 0                          /*哲學家正在思考*/
#define HUNGRY 1                           /*哲學家想取得叉子*/
#define EATING 2                      /*哲學家正在吃麪*/
typedef int semaphore;              /*信號量是一個特殊的整型變量*/
int state[N];                                /*記錄每個人狀態的數組*/
semaphore mutex = 1;               /*臨界區互斥*/
semaphore s[N];                                /*每個哲學家一個信號量*/
 
void philosopher(int i) {       /*i:哲學家號碼,從0到N-1*/
       while(TURE){                      /*無限循環*/
              think();                         /*哲學家正在思考*/
              take_forks(i);                /*需要兩隻叉子,或者阻塞*/
              eat();                                   /*進餐*/
              put_forks(i);                  /*把兩把叉子同時放回桌子*/
       }
}
 
void take_forks(int i) {          /*i:哲學家號碼,從0到N-1*/
       down(&mutex);                          /*進入臨界區*/
       state[i]= HUNGRY;                    /*記錄下哲學家i飢餓的事實*/
       test(i);                                 /*試圖得到兩隻叉子*/
       up(&mutex);                              /*離開臨界區*/
       down(&s[i]);                        /*如果得不到叉子就阻塞*/
}
 
void put_forks(int i) {                  /*i:哲學家號碼,從0到N-1*/
       down(&mutex);                          /*進入臨界區*/
       state[i]= THINKING;            /*哲學家進餐結束*/
       test(LEFT);                                  /*看一下左鄰居現在是否能進餐*/
       test(RIGHT);                         /*看一下右鄰居現在是否能進餐*/
       up(&mutex);                              /*離開臨界區*/
}
 
void test(i) {                               /*i:哲學家號碼,從0到N-1*/
       if(state[i]== HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
              state[i]= EATING;
              up(&s[i]);
       }
}
22. 讀者寫者問題的一種解法

typedef int semaphore;        /*定義整型的信號量*/
semaphore mutex = 1;          /*爲控制對全局量rc的訪問而設置的信號量*/
semaphore db = 1;             /*爲控制對數據庫的訪問而設置的信號量*/
int rc = 0;                   /*rc是正在讀或者等待讀的進程數,初值爲0*/
 
void reader(void)
{
       while(TRUE) {           /*設置無限循環方式*/
              down(&mutex);        /*獲取對rc變量的互斥訪問權*/
              rc= rc + 1;         /*這時又多了一個讀進程*/
              if(rc == 1) down(&db); /*若是第一個讀者,則允許訪問*/
              up(&mutex);          /*釋放對rc的互斥操作*/
              reader_data_dase();  /*完成數據訪問操作*/
              down(&mutex);        /*獲取對rc變量的互斥訪問權*/
              rc= rc - 1;         /*這時減少了一個讀者進程*/
              if(rc == 0) up(&db);/*若是最後一個讀者,可允許寫*/
              up(&mutex);          /*釋放對rc的互斥訪問*/
              use_data_read();     /*非臨界區操作,使用讀出數據*/
       }
}
void writer(void)
{
       while(TRUE) {           /*設置無限循環方式*/
              think_up_data();     /*非臨界區操作,準備希望修改的數據*/
              down(&db);           /*獲取數據庫互斥訪問權*/
              writer_data_dase;    /*更新數據庫消息*/
              up(&db);             /*釋放數據庫互斥訪問權*/
       }
}


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