作業系統學習(六)—— 執行緒概念及特點,作業系統的併發機制

weixin_34146805發表於2017-09-19

一、執行緒

執行緒概念

執行緒是程式中的一條執行路徑,有自己私用的堆疊和處理機執行環境,共享父程式的主存,單個程式可以建立許多個執行緒。

程式概念複習

  • 一個可執行程式,定義了初始程式碼和資料。
  • 一個私用地址空間,它是程式可以使用的一組虛擬主存地址。
  • 程式執行時所需的系統資源(如檔案,訊號燈,通訊埠等)是由作業系統分配的。
  • 若系統支援執行緒執行,那麼每個程式至少有一個執行執行緒。

程式是任務排程的單位,也是系統資源的分配單位
執行緒是程式中的一條執行路徑,當系統支援多執行緒處理時,執行緒是任務排程的單位,但不是系統資源的分配單位。執行緒完全繼承父程式佔有的資源,只是當它活動時有自己的執行現場

執行緒運用例項:

網頁伺服器是多執行緒的,需要接收使用者關於網頁,影象,聲音等請求,一個網頁伺服器可能有眾多使用者的併發訪問。伺服器建立一個執行緒以監聽客戶請求,當有請求產生時,伺服器將建立另一個執行緒來處理請求。

執行緒的特點與狀態:

執行緒的特點:
  • 執行緒建立與管理的開銷小的多,因為執行緒可以共享父程式的所有程式和全域性資料,這意味著建立一個新執行緒只涉及最小量的主存分配(執行緒表),也意味著一個程式建立的多個執行緒可以共享地址區域和資料。
  • 在程式內建立多執行緒,可以提高系統的並行處理能力。
執行緒的狀態變遷:

執行緒是處理機排程的最小單位,而不是程式。一個程式可以建立一個執行緒,那麼它具有單一的控制路徑,一個程式也可以建立多個執行緒,那麼它具有多個控制路徑。
這時,執行緒是爭奪CPU的單位
執行緒的過程:建立,執行,等待,就緒或終止

使用者執行緒和核心執行緒

使用者執行緒:在核心支援下,在使用者層通過執行緒庫實現的。執行緒庫提供對執行緒建立、排程和管理等方面的支援。使用者執行緒的建立和排程是在使用者空間進行的,不需要核心干預,因此使用者級執行緒通常能快速地建立和管理。
缺點:如果核心是單執行緒的,那麼任何一個使用者級執行緒執行了一個執行緒等待的系統呼叫,就會引起整個程式的阻塞。

核心執行緒:作業系統直接支援。管理由作業系統完成。核心在其空間內執行執行緒建立,排程和管理。核心執行緒的建立和管理比在使用者級建立和管理使用者執行緒要慢,但正是由於核心管理執行緒,當一個執行緒執行等待的系統呼叫時,核心能排程應用程式內的另一個執行緒去執行。

二、作業系統的併發機制

在UNIX/Linux系統中,建立一個新程式的唯一方法就是呼叫系統呼叫fork。呼叫fork的為父程式,新建立的程式叫做子程式。格式:

pid = fork();

完成以下操作:
1)為新程式分配一個新的PCB結構;
2)為子程式賦一個唯一的程式標識號(PID);
3)做一個父程式上下文的邏輯副本。正文程式碼區無需拷貝,只需增加引用數即可。父子程式將執行相同的程式碼。但資料段和堆疊段屬於程式的私有資料,需要拷貝到新的記憶體區中。
4)增加與該程式相關聯的檔案表和索引節點表的引用數。父程式開啟的檔案子程式可以繼續使用。
5)對父程式返回子程式的程式號,對子程式返回零。

例:設有Linux程式如下:

#include<sys/types.h>
#include<stdio.h>
#include<unistd.h>
main()
{
    pid_t child;
    int i=2;
    if((child=fork()) == -1)
    {
        printf("fork error.\n");
        exit(0);
    }
    if(child == 0)
    {
        i = i + 3;
        printf("i = %d\n",i);
     }
     i = i + 5;
     printf("i = %d\n",i);
}

執行結果有以下四種:
1)fork error
2)i = 5->10->7,即i=2,然後子程式先執行,child==0,i=5,i=10,之後父程式執行,i=7。
3)i = 7->5->10,即i=2,先執行父程式,再排程子程式。
4)i = 5->7->10,先執行子程式的一半,父程式進入,再排程子程式。

由此,我們可以看出程式排程的必要性。

exec()類函式

exec()有一類函式,他們的作用是根據引數指定的檔名找到可執行檔案,並用它來取代呼叫程式的內容。在呼叫程式內部執行一個可執行檔案。

if(fork()==0)
{
    printf("a");
    execlp("./file1",0);
    printf("b");
}

建立執行緒即應用例項

Linux系統下的多執行緒遵循POSIX執行緒介面,稱為pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連線時需要庫libpthread.a(這裡是C語言版本)

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void thread()
{
    int i;
    for(i=0;i<3;i++)
        printf("This is a pthread.\n");
}
int main()
{
    pthread_t id;
    int i,ret;
    ret = pthread_create(&id,NULL,(void* ) thread,NULL);//執行緒建立函式,成功建立返回0,否則返回-1,確定進入執行緒的入口
    if(ret != 0)
    {
        printf("Create pthread error!\n");
        exit(1);
    }
    for(i=0;i<3;i++)
        printf("This is the main process.\n");
    pthread_join(id,NULL);//pthread_join()函式,以阻塞的方式等待thread指定的執行緒結束。
    return (0);
}

等待程式、執行緒的終止及其應用

在UNIX/Linux系統中,一個程式可以通過系統呼叫wait使他的執行與子程式的終止同步,即wait函式
格式:pid = wait(stat_addr);
wait()使父程式暫停執行,直到它的一個子程式結束為止,該函式的返回值是終止執行的子程式的PID。引數status所指向的變數存放子程式的退出碼,即從子程式的main函式返回的值或子程式中exit()函式的引數。如果status不是一個空指標,狀態資訊將被寫入它指向的變數。

相關文章