1.1、Linux下多任務機制的介紹


  多任務處理是指用戶可以在同一時間內運行多個應用程序,每個正在執行的應用程序被稱為一個任務。Linux就是一個支持多任務的操作系統,比起單任務系統它的功能增強了許多。

  多任務操作系統使用某種調度策略(可以查看操作系統來了解)支持多個任務的並發執行。事實上,(單核)處理器在某一時刻只能執行一個任務。每個任務創建時被分配時間片(幾十到上百毫秒),任務執行(占用CPU)時,時間片遞減。操作系統會在當前任務的時間片用完時調度執行其他的任務,由於任務會頻繁的切換執行,且分配的時間片都是幾十到上百毫秒,因此給用戶多個任務同時運行的錯覺。

  多任務操作系統支持多個任務並發執行。每個任務創建時被分配時間片,任務執行時,時間片遞減,操作系統調度進程是隨機的。

  多任務操作系統中通常有三個概念:任務、進程和線程。

1、任務

  任務是一個邏輯概念,指由一個軟件完成的活動,或者是實現某個目的的一系列操作。通常一個程序的一次運行就代表一個任務,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務就是進程或線程。例如QQ的一次運行就是一個任務。一個殺毒軟件的一次運行是一個任務,目的是在各種病毒的侵害中保護計算機系統,這個任務包含多個獨立功能的子任務(進程或線程),包括實時監控功能、定時查殺功能、防火牆功能以及用戶交互功能等。

  任務、進程和線程之間的關系如下圖所示:

2、進程

  2.1、進程的基本概念

  進程是指一個具有獨立功能的程序在某個數據集合上的一次動態執行過程,它是操作系統進行資源分配和調度的基本單位。一個任務的運行可以激活若干個進程,這些進程來互相合作完成該任務的功能。

  進程是一個獨立的可調度的任務,是一個抽象實體。當系統在執行某個程序時,分配和釋放的各種資源。

  進程是一個程序的一次動態執行過程。

  進程是程序執行和資源管理的最小單位。

  進程具有並發性、動態性、交互性和獨立性等主要特征:

  (1)並發性:指的是系統中多個進程可以同時並發執行,相互之間互不受干擾。

  (2)動態性:指的是進程具有完整的生命周期,而且在進程的生命周期內,進程的狀態是不斷變化的,另外,進程具有動態的地址空間(包括代碼、數據和進程控制塊等)。

  (3)交互性:指的是進程在執行過程中可能會與其他進程發生直接或間接的通信(IPC),如進程同步或進程互斥等,需要為此添加一定的進程處理機制。

  (4)獨立性:指的是進程是一個相對完整的資源分配和調度的基本單位,各個進程的地址空間是相互獨立的,因此,只有采取某些特定的通信機制才能實現進程之間的通信。

  進程與程序的區別:

  程序是一段靜態的代碼,是保存在非易失性存儲器上的指令和數據的有序集合,沒有任何執行的概念;

  進程是一個動態的概念,它是程序的一次執行過程,進程具有生命周期,包括了動態創建、調度、執行和消亡的整個過程,它是程序執行和資源管理的最小單位;

  從操作系統的角度來看,進程是程序執行時相關資源的總稱。當進程結束時,所有資源被操作系統自動回收。

  Linux系統中主要包括下面幾種類型的進程:

  (1)交互式進程:這類進程經常與用戶進行交互,需要等待用戶的輸入(鍵盤和鼠標操作等)。當接收到用戶的輸入之后,這類進程能夠立刻響應。交互式進程可以在前台運行也可以在后台運行。典型的交互式進程有shell命令進程、vim文本編輯器、圖形應用程序運行等。

  (2)批處理進程:這類進程不必與用戶進行交互,它不屬於某個終端,它被提交到一個隊列中以便順序執行。因此通常在后台運行。因為這類進程不必很快的響應,因此往往不會優先調度。典型的批處理器是編譯器的編譯操作、數據庫搜索引擎等。

  (3)守護進程(精靈進程、后退進程、Daemon進程):這類進程一直在后台運行,和任何終端都無關聯。一般在系統啟動時開始執行,系統關閉時結束。很多系統進程(各種服務)都是以守護進程的形式存在。在Linux中,init是0號進程,它是所有進程的父進程。

  2.2、Linux下的進程結構

  Linux中的進程包含三個段:

  (1)數據段:存放的全局變量、常數以及動態數據分配的數據空間(如malloc函數取得的空間)等。

  (2)正文段:存放的是程序的代碼。

  (3)堆棧段:存放的是函數的返回地址、函數的參數以及程序中的局部變量。

 
  進程不僅包括程序的指令和數據,而且包括程序計數器值、CPU的所有寄存器值以及存儲臨時數據的進程堆棧。

  因為Linux是一個多任務的操作系統,所以其它的進程必須等到操作系統將處理器使用權分配給自己之后才能運行。當正在運行的進程需要等待其它的系統資源時,Linux內核將取得處理器的控制權,按照某種調度算法將處理器分配給某個正在等待執行的進程。

  內核將所有進程存放在雙向循環鏈表(進程鏈表)中,鏈表的每一項都是task_struct,稱為進程控制塊的結構。該結構包含了與一個進程相關的所有信息,在<include/Linux/sched.h>文件中定義。task_struct內核結構比較大,它能完整的描述一個進程,如進程的狀態、進程的基本信息、進程標識符、內存相關信息、父進程相關信息、與進程相關的終端信息、當前工作目錄、打開的文件信息、所接收的信號信息等。

  下面講述task_stuct結構中最為重要的兩個域:state(進程狀態)和pid(進程標識符)。

  (1)進程運行狀態

  Linux中的進程運行狀態有以下幾種主要狀態:

  A、 運行狀態(TASK_RUNNING):程序當前正在運行,或者在運行隊列中等待調度。

  B、可中斷的阻塞狀態(等待狀態)(TASK_INTERRUPTIBLE):進程處於阻塞(睡眠)狀態,正在等待某些事件的發生或者能夠占用某些資源。處在這種狀態下的進程可以被信號中斷。接收到信號或被顯示的喚醒呼叫(如調用wake_up系列宏:wake_up、wake_up_interruptible等)喚醒之后,進程將轉變為運行(TASK_RUNNING)狀態。

  C、不可中斷的阻塞狀態(等待狀態)(TASK_UNINTERRUPTIBLE):該狀態類似於可中斷的阻塞狀態(TASK_INTERRUPTIBLE),但是它不會處理信號,把信號傳遞到這種狀態下的進程不能改變它的狀態。在一些特定的情況下(進程必須等待,知道某些不能被中斷的事件發生),這種狀態是很有用的。它不能被信號喚醒,只有它所等待的事件發生時,進程才能被顯示喚醒呼叫喚醒。

  D、暫停狀態(TASK_STOPPED):進程的執行被暫停,當進程收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信號,就會進入暫停狀態。

  E、僵屍狀態(TASK_ZOMBLE):子進程運行結束,父進程未退出,並且未使用wair函數族(如使用waitpid()函數)等系統調用來回收子進程的退出狀態。處在該狀態下的子進程已經放棄了幾乎所有的內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等信息供其父進程收集。

  F、消亡狀態(死亡狀態)(TASK_DEAD):這是最終狀態,這是一個已終止的進程,但還在進程向量數組中占有一個task_struct結構。父進程調用wait函數族回收之后,子進程由系統徹底刪除,不可見。

  它們之間的轉換關系如下圖所示:

 

 

  內核可以使用set_task_state和set_current_state宏來改變指定進程的狀態和當前執行進程的狀態。

  (2)進程標識符

  Linux內核通過唯一的進程標識符PID來標識每個進程。PID存放在進程描述符的pid字段中,新創建的PID通常是前一個進程的PID加1,不過PID的值有上限(大值 = PID_MAX_DEFAULT – 1,通常為32767),讀者可以查看/proc/sys/kernel/pid_max來確定該系統的進程數上限。

  當系統啟動后,內核通常作為某一個進程的代表。一個指向task_struct的宏current用來記錄正在運行的進程。current經常作為進程描述符結構指針的形式出現在內核代碼中,例如,current->pid表示處理器正在執行的進程的PID。當系統需要查看所有的進程時,則調用for_each_process()宏,這將比系統搜索數組的速度要快得多。

  Linux中主要的進程標識為進程號(process identity number,PID)和父進程號(parent process ID,PPID),PID唯一的標識一個進程。在Linux中獲得當前進程的進程號(PID)和父進程號(PPID)的系統調用函數分別為getpid()和getppid()。

  2.3、進程的模式

  進程的執行模式分為用戶模式和內核模式:

  用戶模式與內核模式切換:

  2.4、進程的創建、執行和終止

  (1)進程的創建和執行

  許多操作系統提供的都是產生進程的機制,也就是說,首先在新的地址空間里創建進程、讀入可執行文件,后再開始執行。Linux中進程的創建很特別,它把上述步驟分解到兩個單獨的函數中去執行:fork()和exec函數族。首先,fork()函數通過復制當前進程創建一個子進程,子進程與父進程的區別僅僅在於不同的PID、PPID和某些資源及統計量。exec函數族負責讀取可執行文件並將其載入地址空間開始運行。

  要注意的是,Linux中的fork()函數使用的是寫時復制頁的技術,也就是內核在創建進程時,其資源並沒有被復制過來,資源的賦值僅僅只有在需要寫入數據時才發生,在此之前只是以只讀的方式共享數據。寫時復制技術可以使Linux擁有快速執行的能力,因此這個優化是非常重要的。

  (2)進程的終止

  進程銷毀和進程創建同等重要,如果未認真對待進程銷毀,它們將變成僵屍進程困擾各位。進程終結也需要做很多煩瑣的收尾工作,系統必須保證回收進程所占用的資源,並通知父進程。Linux首先把終止的進程設置為僵屍狀態,這時,進程無法投入運行,它的存在只為父進程提供信息,申請死亡。父進程得到信息后,開始調用wait函數族,后終止子進程,子進程占用的所有資源被全部釋放。

進程和僵屍進程

文件操作中,關閉文件和打開文件同等重要。同樣,進程銷毀也和進程創建同等重要。如果未認真對待進程銷毀,它們將變成僵屍進程困擾各位。

  2.5、僵屍進程

  進程完成工作后(執行完main函數中的程序后)應被銷毀,但有時這些進程變成僵屍進程,占用系統中的重要資源。這種狀態下的進程稱作“僵屍進程”,這也是給系統帶來負擔的原因之一。因此,我們應該消滅這種進程。

  (1)產生僵屍進程的原因

  為了防止僵屍進程的產生,先解釋產生僵屍進程的原因。利用如下兩個示例展示調用fork函數產生子進程的終止方式:

  • 傳遞參數並調用exit函數
  • main函數中執行return並返回值

  向exit函數傳遞的參數值和main函數的return語句返回的值都會傳遞給操作系統,而操作系統不會銷毀子進程,直到把這些值傳遞給產生該子進程的父進程,處在這種狀態下的進程就是僵屍進程。也就是說,將子進程變成僵屍進程的正是操作系統。既然如此,僵屍進程何時被銷毀呢?其實之前已給出答案:當子進程將返回值傳遞給父進程的時候。那么,如何向父進程傳遞返回值呢?操作系統不會主動把這些值傳遞給父進程,只有父進程主動發起請求(函數調用)時,操作系統才會傳遞該值。換言之,如果父進程未主動要求獲得子進程的結束狀態值,操作系統將一直保存,並讓子進程長時間處於僵屍進程狀態。接下來的示例將創建僵屍進程:

#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
    pid_t pid = fork();
    if (pid == 0) // if Child Process
    {
        puts("Hi I'am a child process");
    }
    else
    {
        printf("Child Process ID: %d \n", pid);   //輸出子進程ID,可以通過該值查看子進程狀態(是否為僵屍進程)
        sleep(30); // Sleep 30 sec.  父進程暫停30秒,如果父進程終止,處於僵屍進程狀態的子進程將同時銷毀。因此,延緩父進程的執行以驗證僵屍進程
    }
 
    if (pid == 0)
        puts("End child process");
    else
        puts("End parent process");
    return 0;
}


# ./zombie
Child Process ID: 5507
Hi I'am a child process
End child process
End parent process 

 程序開始運行,在打印出子進程的進程ID后,會停歇30秒,這個時候我們可以趁機看一下5507進程號所對應的進程狀態。

# ps -ef | grep 5507
root      5507  5506  0 11:44 pts/32   00:00:00 [zombie] <defunct>
root      5509 23062  0 11:45 pts/31   00:00:00 grep --color=auto 5507
可以看到,5507對應的進程號的狀態為defunct,即為僵屍進程。經過30秒后,隨着父進程的終止,子進程也將銷毀

  (2)銷毀僵屍進程1:利用wait函數

  如前所述,為了銷毀子進程,父進程應主動請求獲取子進程的返回值,接下來討論下發起請求的具體方法,共有兩種,其中之一就是調用wait函數:

#include <sys/wait.h>
pid_t wait(int *statloc);//成功時返回終止的子進程ID,失敗時返回-1

  調用次函數時如果已有子進程終止,那么子進程終止時傳遞的返回值(exit函數的參數值、main函數的return返回值)將保存到該函數的參數所指的內存空間。但函數參數指向的單元中還包含其他信息,因此需要通過下列宏進行分離

  • WIFEXITED子進程正常終止時返回真(true)
  • WEXITSTATUS返回子進程的返回值

  也就是說,向wait函數傳遞變量status的地址時,調用wait函數后應編寫如下代碼 :

if (WIFEXITED(status))
{
    puts("Normal termination!");
    printf("Child pass num: %d \n", WEXITSTATUS(status)); //返回值是多少
}

根據上述內容編寫示例,此示例中不會再讓子進程編程僵屍進程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(int argc, char *argv[])
{
    int status;
    pid_t pid = fork();
    if (pid == 0)
    {
        return 3;
    }
    else
    {
        printf("Child PID: %d \n", pid);
        pid = fork();
        if (pid == 0)
        {
            exit(7);
        }
        else
        {
            printf("Child PID: %d \n", pid);
            wait(&status);
            if (WIFEXITED(status))
                printf("Child send one: %d \n", WEXITSTATUS(status));
 
            wait(&status);
            if(WIFEXITED(status))
                printf("Child send two: %d \n", WEXITSTATUS(status));
            sleep(30); // Sleep 30 sec.
        }
    }
    return 0;
}
  • 第9、13行:第9行創建的子進程將在第13行通過main函數中的return語句終止
  • 第18、21行:第18行中創建的子進程將在第21行通過調用exit函數終止
  • 第26行:調用wait函數,之前終止的子進程相關信息將保存到status變量,同時相關子進程被完全銷毀
  • 第27、28行:第27行中通過WIFEXITED宏驗證子進程是否正常終止,如果正常退出,則調用WEXITSTATUS宏輸出子進程的返回值
  • 第30~32行:因為之前創建了兩個進程,所以再次調用wait函數和宏
  • 第33行:為暫停父進程終止而插入的代碼,此時可以查看子進程狀態 

#gcc wait.c -o wait
# ./wait
Child PID: 6862
Child PID: 6863
Child send one: 3
Child send two: 7 

  在系統中執行ps命令可以發現,並沒有上一個示例中對應PID的進程。這是因為調用了wait函數,完全銷毀了子進程,另外兩個子進程終止時返回3和7傳遞給父進程。這就是通過調用wait函數消滅僵屍進程的方法,調用wait函數時,如果沒有已終止的子進程,那么程序將阻塞直到有子進程終止,因此需謹慎調用該函數

  (3)銷毀僵屍進程2:使用waitpid函數

  wait函數會引起程序的阻塞,還可以考慮調用waitpid函數,這是防止僵屍進程的第二種方法,也是防止阻塞的方法:

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);//成功時返回終止的子進程ID(或0),失敗時返回-1
  • pid:等待終止的目標子進程的ID,若傳遞-1,則與wait函數相同,可以等待任意子進程終止
  • statloc:與wait函數的statloc具有相同意義
  • options:傳遞頭文件sys/wait.h中聲明的常量WNOHANG,即使沒有終止的子進程也不會進入阻塞狀態,而是返回0並退出函數

下面介紹用上述函數的示例,調用waitpid函數,程序不會阻塞。 

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(int argc, char *argv[])
{
    int status;
    pid_t pid = fork();
 
    if (pid == 0)
    {
        sleep(15);   //調用sleep函數推遲子進程的執行,這會導致程序延遲15秒
        return 24;
    }
    else
    {
        while (!waitpid(-1, &status, WNOHANG))   //while循環調用waitpid函數,向第三個參數傳遞WNOHANG,因此,若之前沒有終止的子進程將返回0
        {
            sleep(3);
            puts("sleep 3sec.");
        }
 
        if (WIFEXITED(status))
            printf("Child send %d \n", WEXITSTATUS(status));
    }
    return 0;
}


# gcc waitpid.c -o waitpid
# ./waitpid
sleep 3sec.
sleep 3sec.
sleep 3sec.
sleep 3sec.
sleep 3sec.
Child send 24

  2.6、進程的內存結構

  Linux操作系統采用虛擬內存管理技術,這樣每個進程都有獨立的地址空間,使得每個進程都有各自互不干涉的進程地址空間。該地址空間是大小為4GB的線性虛擬空間,用戶所看到和接觸到的都是該虛擬地址,無法看到實際的物理內存地址。利用這種虛擬地址不但能起到保護操作系統的效果(用戶不能直接訪問物理內存),而且更重要的是,用戶程序可以使用比實際物理內存更大的地址空間。

  4GB的進程地址空間會被分成兩個部分:用戶空間與內核空間。用戶地址空間是從0到3GB(0xC0000000),內核地址空間占據3GB到4GB。用戶進程在通常情況下只能訪問用戶空間的虛擬地址,不能訪問內核空間的虛擬地址。只有用戶進程使用系統調用(代表用戶進程在內核態執行)時可以訪問到內核空間。每當進程切換時,用戶空間就會跟着變化;而內核空間由內核負責映射,它並不會跟着進程改變,是固定的。內核空間地址有自己對應的頁表,用戶進程各自有不同的頁表。每個進程的用戶空間都是完全獨立、互不相干的。進程的虛擬內存地址空間如下圖所示:

  其中用戶空間包括以下幾個功能區域(通常稱之為“段”):

  只讀段:包含程序代碼(.init和.text)和只讀數據(.rodata)。
  數據段:存放的是全局變量和靜態變量。其中可讀可寫數據段(.data)存放已初始化的全局變量和靜態變量,BSS數據段(.bss)存放未初始化的全局變量和靜態變量。
  堆:由系統自動分配釋放,存放函數的參數值、局部變量的值、返回地址等。
  堆棧:存放動態分配的數據,一般由程序員動態分配和釋放。若程序員不釋放,程序結束時可能由操作系統回收。
  共享庫的內存映射區域:這是Linux動態鏈接器和其他共享庫代碼的映射區域。

  由於在Linux系統中每一個進程都會有/proc文件系統下與之對應的一個目錄(如將init進程的相關信息在/proc/1目錄下的文件中描述),因此通過proc文件系統可以查看某個進程的地址空間的映射情況。例如,運行一個應用程序(示例中的可運行程序是在/home/david/project/目錄下的test文件),如果它的進程號為13703,則輸入“cat /proc/13703/maps”命令,可以查看該進程的內存映射情況,其結果如下:

$ cat /proc/13703/maps
    /* 只讀段:代碼段、只讀數據段 */
    08048000-08049000 r-xp 00000000 08:01 876817 /home/david/project/test
    08049000-0804a000 r--p 00000000 08:01 876817 /home/david/project/test
    /* 可讀寫數據段 */
    0804a000-0804b000 rw-p 00001000 08:01 876817 /home/david/project/test
    0804b000-0804c000 rw-p 0804b000 00:00 0 
    08502000-08523000 rw-p 08502000 00:00 0 [heap] /**/
    b7dec000-b7ded000 rw-p b7dec000 00:00 0 
    /* 動態共享庫 */
    b7ded000-b7f45000 r-xp 00000000 08:01 541691
    /lib/tls/i686/cmov/libc-2.8.90.so
    b7f45000-b7f47000 r--p 00158000 08:01 541691 
    /lib/tls/i686/cmov/libc-2.8.90.so
    b7f47000-b7f48000 rw-p 0015a000 08:01 541691
    /lib/tls/i686/cmov/libc-2.8.90.so
    b7f48000-b7f4b000 rw-p b7f48000 00:00 0 
    b7f57000-b7f5a000 rw-p b7f57000 00:00 0 
    /* 動態鏈接器 */
    b7f5a000-b7f74000 r-xp 00000000 08:01 524307 /lib/ld-2.8.90.so
    b7f74000-b7f75000 r-xp b7f74000 00:00 0 [vdso]
    b7f75000-b7f76000 r--p 0001a000 08:01 524307 /lib/ld-2.8.90.so 
    b7f76000-b7f77000 rw-p 0001b000 08:01 524307 /lib/ld-2.8.90.so
    bff61000-bff76000 rw-p bffeb000 00:00 0 [stack] /* 堆棧 */

  2.7、Linux下的進程管理

  (1)啟動進程

  A、手動啟動

  由用戶輸入命令直接啟動進程,前台運行和后台運行

  B、調度啟動

  系統根據用戶事先的設定自行啟動進程

  at在指定時刻執行相關的進程

  cron周期性執行相關進程

  (2)調度進程

  ps:查看系統的進程

  top:動態顯示系統中的進程

  nice:按用戶指定的優先級運行進程

  renice:改變正在運行進程的優先級

  kill:結束進程(包括后台進程)

  bg:將掛起的進程在后台執行

  fg:把后台運行的進程放到前台運行

3、線程(輕量級進程)

  前面已經提到,進程是系統中程序執行和資源分配的基本單位。每個進程都擁有自己的數據段、代碼段和堆棧段,這就造成了進程在進行切換時操作系統的開銷比較大。為了提高效率,操作系統又引入了另外一個概念-----線程,也稱為輕量級進程。線程可以對進程的內存空間和資源進行訪問,並與同一進程中的其它線程共享。因此,線程的上下文切換的開銷比進小的多。

  系統為每個用戶進程創建一個task_struct來描述該進程,該結構體中包含了一個指針指向該進程的虛擬地址空間映射表。實際上task_struct和地址空間映射表一起用來表示一個進程。

  由於進程的地址空間是私有的,因此在進程間上下文切換時,系統開銷比較大。為了提高系統的性能,許多操作系統規范里引入了輕量級進程的概念,也被稱為線程。在同一個進程中創建的線程共享該進程的地址空間。Linux中同樣用task_struct來描述一個線程,線程和進程都參與統一的調度。

  通常線程指的是共享相同地址空間的多個任務,使用多線程的好處是:大大提高了任務切換的效率,避免了額外的TLB&cache的刷新。

  為了進一步減少處理器的空轉時間,支持多處理器以及減少上下文切換開銷,進程在演化中出現了另一個概念-----線程。它是進程內獨立的一條運行路線,是內核調度的最小單元,也被稱為輕量級進程。線程由於具有高效性和可操作性,在嵌入式系統開發中運用的非常廣泛,希望讀者能夠很好地掌握。  

  這里要講的線程相關操作都是用戶空間中的線程的操作。在Linux中,一般pthread線程庫是一套通用的線程庫,是由POSIX提出的,因此具有很好的可移植性。

  pthread線程庫是第三方庫,不是標准的C庫。代碼中有第三方庫是,在編譯時要在后面加上庫名(-lpthread或-pthread),以顯示連接該庫,如gcc main.c -pthread。

  線程通過全局變量和堆區來進行通信。

  一個進程可以擁有多個線程,其中每個線程共享該進程所擁有的的資源。要注意的是,由於線程共享了進程的資源和地址空間,因此,任何線程對系統資源的操作都會給其它線程帶來影響。由此可知,多線程中的同步時非常重要的問題。在多線程系統中,進程與線程的關系如下圖所示:

  一個進程中的多個線程共享的資源有:可執行的命令、靜態數據、進程中打開的文件描述符、信號處理函數、當前工作目錄、用戶ID、用戶組ID。

  每個線程私有的資源有:線程ID(TID)、PC(程序計數器)和相關寄存器、堆棧(局部變量、返回地址)、錯誤號、信號掩碼和優先級、執行狀態和屬性

  進程擁有自己的代碼段,數據段,堆棧段,因此在進程切換時操作系統開銷大,為了解決這個問題,因此引入了線程這一概念,線程也稱為輕量級進程。線程可以對進程的空間和資源進行訪問,也可以與同一進程中的其他線程共享進程的資源,由於共享進程的資源,也引出了共享資源的同步問題。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM