自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

揭秘操作系統(tǒng)最核心技術(shù):進(jìn)程與線程是如何一步步發(fā)明出來(lái)的?

系統(tǒng)
今天我想跟你聊聊一個(gè)計(jì)算機(jī)發(fā)展史上的精彩故事——進(jìn)程與線程是如何一步步被發(fā)明出來(lái)的。

今天我想跟你聊聊一個(gè)計(jì)算機(jī)發(fā)展史上的精彩故事——進(jìn)程與線程是如何一步步被發(fā)明出來(lái)的?

讓我?guī)愦┰綍r(shí)光隧道,看看這些關(guān)鍵概念是如何從無(wú)到有,最終成為現(xiàn)代操作系統(tǒng)基石的。

一、"一人獨(dú)占"的早期計(jì)算機(jī)時(shí)代

想象一下 20 世紀(jì) 50 年代的計(jì)算機(jī)實(shí)驗(yàn)室:一臺(tái)體積龐大、價(jià)格昂貴的 UNIVAC 或 IBM 704 計(jì)算機(jī)占據(jù)了整個(gè)房間。這些早期計(jì)算機(jī)每次只能執(zhí)行一個(gè)程序,用戶必須排隊(duì)等候。

更令人沮喪的是,當(dāng)程序在等待磁帶或打印機(jī)等慢速設(shè)備時(shí),這些價(jià)值不菲的計(jì)算機(jī)就會(huì)閑置著——就像一輛豪華跑車被迫停在路邊等待紅燈一樣浪費(fèi)!

這種運(yùn)行方式被稱為"批處理系統(tǒng)"。用戶把程序和數(shù)據(jù)打在卡片上交給操作員,然后等待——可能是幾個(gè)小時(shí),甚至是整整一天!如果前面的程序出了 bug 導(dǎo)致死循環(huán),后面所有人都只能干等。

有一天,麻省理工學(xué)院的計(jì)算機(jī)科學(xué)家 Fernando Corbató 盯著實(shí)驗(yàn)室里的計(jì)算機(jī)發(fā)呆,突然靈光一閃:

"為什么要讓昂貴的計(jì)算機(jī)資源在等待慢速 I/O 操作時(shí)空閑呢?我們應(yīng)該讓它能同時(shí)處理多個(gè)任務(wù)!"

二、分時(shí)系統(tǒng):打破"獨(dú)占"的第一步

于是,Corbató 和他的團(tuán)隊(duì)開始研發(fā)全新的系統(tǒng)——CTSS(Compatible Time-Sharing System,兼容分時(shí)系統(tǒng)),這被認(rèn)為是現(xiàn)代多任務(wù)操作系統(tǒng)的雛形。

CTSS 的核心理念很簡(jiǎn)單:在內(nèi)存中同時(shí)加載多個(gè)程序,當(dāng)一個(gè)程序等待 I/O 操作時(shí),CPU 可以切換去執(zhí)行另一個(gè)程序。這就是"多道程序設(shè)計(jì)"的開端。

但問(wèn)題來(lái)了:如何讓一個(gè)正在運(yùn)行的程序暫停,然后在未來(lái)某個(gè)時(shí)刻恢復(fù)執(zhí)行呢?

答案是:必須保存程序的"上下文"信息!就像你在讀一本書時(shí),如果要暫時(shí)去做別的事,你需要記住當(dāng)前讀到第幾頁(yè)第幾行,這樣回來(lái)才能繼續(xù)讀。

于是,調(diào)查研究后,Corbató 和他的團(tuán)隊(duì)設(shè)計(jì)出了一個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)來(lái)保存程序的"狀態(tài)快照":

struct cpu_state {
    uint32_t r0, r1, r2, r3;         // 通用寄存器
    uint32_t pc;                     // 程序計(jì)數(shù)器
    uint32_t sp;                     // 棧指針
    uint32_t status;                 // 狀態(tài)寄存器
    // 其他必要的寄存器狀態(tài)...
};

有了這個(gè)結(jié)構(gòu),當(dāng)系統(tǒng)需要切換任務(wù)時(shí),就可以保存當(dāng)前程序的所有寄存器狀態(tài),然后加載另一個(gè)程序的狀態(tài),讓 CPU 繼續(xù)執(zhí)行新的程序。

三、內(nèi)存保護(hù):解決程序"打架"問(wèn)題

但實(shí)施分時(shí)系統(tǒng)后,一個(gè)新問(wèn)題很快浮出水面。在貝爾實(shí)驗(yàn)室工作的 Dennis Ritchie(C 語(yǔ)言和 UNIX 的創(chuàng)始人之一)回憶道,他們的早期系統(tǒng)經(jīng)常崩潰,而且數(shù)據(jù)會(huì)莫名其妙地被修改。

調(diào)查后,他們發(fā)現(xiàn)了根本原因:

不同的程序在同一內(nèi)存空間中互相干擾!

原因很簡(jiǎn)單:在早期系統(tǒng)中,所有程序共享同一塊內(nèi)存空間,沒(méi)有任何隔離機(jī)制。就像幾個(gè)小孩在同一張紙上畫畫,互相涂抹對(duì)方的作品一樣混亂。

看看這個(gè)災(zāi)難性的例子:

// 計(jì)費(fèi)程序 : interest.c  
struct account {
    char name[50];
    double balance;
} customer = {"Zhang San", 1000.0};

void update_balance() {
    while(1) {
        customer.balance *= 1.05;  // 計(jì)算利息
        sleep(1);
    }
}

// 同時(shí)運(yùn)行的取款程序 : withdraw.c
struct account {
    char name[50];
    double balance;
} customer = {"Zhang San", 1000.0};

void withdraw() {
    while(1) {
        customer.balance -= 100;  // 定期取款
        sleep(1);
    }
}

在早期系統(tǒng)中,由于缺乏地址空間隔離機(jī)制,這兩個(gè)獨(dú)立編譯的程序使用了相同的內(nèi)存地址范圍,導(dǎo)致它們的 customer 變量實(shí)際上重疊在物理內(nèi)存的同一區(qū)域,結(jié)果就是一個(gè)程序計(jì)算利息增加余額,另一個(gè)程序同時(shí)在取款減少余額,導(dǎo)致完全不可預(yù)測(cè)的結(jié)果。

系統(tǒng)科學(xué)家們意識(shí)到,必須從根本上解決這個(gè)問(wèn)題——需要在操作系統(tǒng)層面提供內(nèi)存隔離機(jī)制!

四、進(jìn)程的誕生:給每個(gè)程序一個(gè)"隔離艙"

1964年,MIT、貝爾實(shí)驗(yàn)室和通用電氣公司聯(lián)合開發(fā)了 MULTICS 系統(tǒng)。在這個(gè)過(guò)程中,一個(gè)革命性的概念誕生了——"進(jìn)程"。

什么是進(jìn)程?簡(jiǎn)單說(shuō),進(jìn)程就是一個(gè)運(yùn)行中的程序?qū)嵗?,擁有?dú)立的內(nèi)存空間和系統(tǒng)資源。

MULTICS 的設(shè)計(jì)者創(chuàng)建了一個(gè)內(nèi)存映射系統(tǒng),確保每個(gè)進(jìn)程都有自己的獨(dú)立內(nèi)存區(qū)域:

struct mem_layout {
    void* text_begin;        // 代碼區(qū)起始地址
    size_t text_size;        // 代碼區(qū)大小
    void* heap_begin;        // 堆區(qū)起始地址
    size_t heap_size;        // 堆區(qū)大小
    // 其他內(nèi)存區(qū)域...
};

將這個(gè)結(jié)構(gòu)與前面的狀態(tài)快照結(jié)構(gòu)結(jié)合起來(lái),就形成了完整的進(jìn)程定義:

struct task_control {
    struct cpu_state registers;  // CPU狀態(tài)
    struct mem_layout memory;    // 內(nèi)存布局
    pid_t id;                    // 任務(wù)標(biāo)識(shí)符
    uint8_t state;               // 任務(wù)狀態(tài)
    resource_list_t resources;   // 資源列表
    // ... 其他控制信息
};

這就是操作系統(tǒng)中"進(jìn)程控制塊"(PCB)的雛形!有了它,操作系統(tǒng)就能管理多個(gè)進(jìn)程的執(zhí)行,實(shí)現(xiàn)真正的多任務(wù)處理。

但這又帶來(lái)了新問(wèn)題:如何公平地分配 CPU 時(shí)間給多個(gè)進(jìn)程?

五、時(shí)間片輪轉(zhuǎn)調(diào)度:讓每個(gè)人都有發(fā)言權(quán)

在多進(jìn)程系統(tǒng)中,如果讓一個(gè)進(jìn)程一直運(yùn)行到結(jié)束,其他進(jìn)程就得一直等待,這顯然不合理。特別是對(duì)于交互式程序,用戶會(huì)感覺(jué)系統(tǒng)反應(yīng)遲鈍。

為解決這個(gè)問(wèn)題,MIT 的研究人員設(shè)計(jì)了一種全新的調(diào)度算法——"時(shí)間片輪轉(zhuǎn)調(diào)度"(Round-Robin Scheduling)。

這個(gè)概念非常簡(jiǎn)單:給每個(gè)進(jìn)程分配一個(gè)固定長(zhǎng)度的 CPU 使用時(shí)間(稱為"時(shí)間片",通常為幾十毫秒),當(dāng)一個(gè)進(jìn)程用完自己的時(shí)間片,操作系統(tǒng)就強(qiáng)制將 CPU 分配給下一個(gè)等待的進(jìn)程。

// 時(shí)間片輪轉(zhuǎn)調(diào)度的偽代碼
void scheduler() {
    while(true) {
        process = get_next_process_from_queue();
        set_timer(TIME_SLICE);  // 設(shè)置時(shí)鐘中斷
        context_switch(process); // 切換到該進(jìn)程
        // 時(shí)間片用完后,時(shí)鐘中斷處理程序會(huì)調(diào)用scheduler()
    }
}

這就像是一場(chǎng)公平的會(huì)議,每個(gè)人都有固定的發(fā)言時(shí)間。即使有人滔滔不絕,到時(shí)間也必須讓下一位發(fā)言。

時(shí)間片的長(zhǎng)度設(shè)置很有講究:

  • 太短:進(jìn)程切換開銷占比太大,系統(tǒng)效率降低
  • 太長(zhǎng):響應(yīng)時(shí)間變長(zhǎng),用戶體驗(yàn)差

典型的時(shí)間片長(zhǎng)度是 10-100 毫秒,這對(duì)人類感知來(lái)說(shuō)足夠短,讓用戶感覺(jué)多個(gè)程序在"同時(shí)"運(yùn)行,這就是我們熟悉的"并發(fā)"。

進(jìn)程的誕生和時(shí)間片輪轉(zhuǎn)調(diào)度帶來(lái)了巨大的好處:

  • 不同程序之間徹底隔離,不會(huì)相互干擾
  • 系統(tǒng)可以同時(shí)運(yùn)行多個(gè)程序,大大提高了效率
  • 即使一個(gè)程序崩潰,也不會(huì)影響其他程序的運(yùn)行
  • 所有進(jìn)程都能獲得公平的 CPU 使用時(shí)間

就像給每個(gè)畫畫的小孩分發(fā)獨(dú)立的畫紙,再也不用擔(dān)心誰(shuí)會(huì)涂抹誰(shuí)的作品了!同時(shí),每個(gè)小孩都能輪流使用那支珍貴的金色畫筆(CPU)。

六、進(jìn)程間通信與切換瓶頸

隨著進(jìn)程模型的普及,新的需求出現(xiàn)了——進(jìn)程之間需要交換數(shù)據(jù)和協(xié)調(diào)行動(dòng)。UNIX的創(chuàng)始人們發(fā)明了各種"進(jìn)程間通信"(IPC)機(jī)制,如 管道、消息隊(duì)列和共享內(nèi)存等。

但隨著系統(tǒng)中運(yùn)行的進(jìn)程越來(lái)越多,一個(gè)嚴(yán)重的問(wèn)題浮出水面——進(jìn)程切換的開銷實(shí)在太大了!每次切換進(jìn)程,系統(tǒng)都需要:

  • 保存當(dāng)前進(jìn)程的struct cpu_state(所有寄存器狀態(tài))
  • 更新struct mem_layout(切換完整的內(nèi)存映射)—— 最耗時(shí)
  • 刷新 TLB 緩存和其他處理器緩存
  • 恢復(fù)新進(jìn)程的struct cpu_state

這個(gè)過(guò)程就像一名攝影師在拍攝多個(gè)場(chǎng)景之間切換:不僅要記住每個(gè)場(chǎng)景的拍攝角度和相機(jī)設(shè)置(CPU 狀態(tài)),還要搬運(yùn)和重新布置整套燈光裝備、背景和道具(內(nèi)存映射)。即使攝影師技術(shù)再好,這種場(chǎng)景切換的時(shí)間成本也是無(wú)法避免的!

在加州大學(xué)伯克利分校,研究人員還觀察到一個(gè)有趣的現(xiàn)象:服務(wù)器應(yīng)用程序創(chuàng)建了大量進(jìn)程來(lái)處理不同的請(qǐng)求,這些進(jìn)程運(yùn)行完全相同的代碼,卻各自占用獨(dú)立的內(nèi)存空間。這簡(jiǎn)直是在浪費(fèi)資源!

面對(duì)這個(gè)問(wèn)題,他們提出了一個(gè)革命性的疑問(wèn):"如果多個(gè)任務(wù)需要執(zhí)行相同的程序代碼,為何不創(chuàng)造一種新機(jī)制,讓它們共享代碼和數(shù)據(jù),只在需要時(shí)保持獨(dú)立?"

七、線程的誕生:輕量級(jí)的執(zhí)行單元

1979年,Xerox PARC 的研究人員 David Boggs 和 Butler Lampson 在開發(fā) Alto 操作系統(tǒng)時(shí),提出了一個(gè)革命性的想法——"線程"。

線程被設(shè)計(jì)為進(jìn)程內(nèi)的"輕量級(jí)執(zhí)行單元",它們:

  • 共享所屬進(jìn)程的代碼段、數(shù)據(jù)段和系統(tǒng)資源
  • 擁有自己的執(zhí)行棧和寄存器狀態(tài)
  • 可以獨(dú)立調(diào)度執(zhí)行
struct execution_flow {
    uint32_t flow_id;         // 流ID
    uint8_t status;           // 運(yùn)行狀態(tài)
    struct cpu_state regs;    // 寄存器狀態(tài)
    void *stack;              // ??臻g
    struct task_control *owner;  // 所屬任務(wù)
};

線程相比進(jìn)程有哪些優(yōu)勢(shì)?太多了!

  • 創(chuàng)建和銷毀更快:不需要分配新的地址空間,只需要一個(gè)新的棧
  • 切換開銷?。壕€程間切換不需要切換地址空間,速度提升好多倍!
  • 資源共享自然:同一進(jìn)程的線程共享內(nèi)存,可以直接讀寫共享變量
  • 通信簡(jiǎn)單高效:線程間通信不需要特殊的IPC機(jī)制

就像網(wǎng)絡(luò)游戲中的"組隊(duì)"功能——同一隊(duì)伍的玩家可以共享地圖信息,直接交流,而不同隊(duì)伍之間則需要特殊渠道才能通信。

八、實(shí)戰(zhàn)對(duì)比:進(jìn)程vs線程

理論講完了,來(lái)看個(gè)簡(jiǎn)單例子,直觀感受進(jìn)程與線程的區(qū)別:

多進(jìn)程版web服務(wù)器:

import os
from http.server import HTTPServer, BaseHTTPRequestHandler

def run_server(port):
    server = HTTPServer(('', port), BaseHTTPRequestHandler)
    server.serve_forever()

# 創(chuàng)建5個(gè)進(jìn)程,每個(gè)監(jiān)聽不同端口
for i in range(5):
    pid = os.fork()  # 創(chuàng)建新進(jìn)程
    if pid == 0:  # 子進(jìn)程
        run_server(8000 + i)
        break# 子進(jìn)程不再繼續(xù)循環(huán)

# 父進(jìn)程需要等待子進(jìn)程(簡(jiǎn)化處理)
# ...

多線程版web服務(wù)器:

import threading
from http.server import HTTPServer, BaseHTTPRequestHandler

def run_server(port):
    server = HTTPServer(('', port), BaseHTTPRequestHandler)
    server.serve_forever()

# 創(chuàng)建5個(gè)線程,每個(gè)監(jiān)聽不同端口
threads = []
for i in range(5):
    t = threading.Thread(target=run_server, args=(8000 + i,))
    threads.append(t)
    t.start()

# 等待所有線程結(jié)束
for t in threads:
    t.join()

# ...

代碼看起來(lái)很相似,但背后的區(qū)別巨大:

  • 多進(jìn)程版本中,每個(gè)服務(wù)器進(jìn)程有完全獨(dú)立的內(nèi)存空間,資源消耗更大
  • 多線程版本中,所有線程共享同一個(gè)進(jìn)程的內(nèi)存空間,資源利用更高效
  • 多進(jìn)程版更適合需要隔離的任務(wù),多線程版更適合共享數(shù)據(jù)的任務(wù)

九、線程安全與未來(lái)趨勢(shì)

線程雖然解決了很多問(wèn)題,但也帶來(lái)了新的挑戰(zhàn),最大的就是"線程安全"問(wèn)題。由于多個(gè)線程共享同一地址空間,并發(fā)訪問(wèn)共享數(shù)據(jù)會(huì)導(dǎo)致"競(jìng)態(tài)條件"。

看一個(gè)經(jīng)典的計(jì)數(shù)器問(wèn)題:

// 全局共享變量
int counter = 0;

// 線程函數(shù)
void* increment_counter(void* arg) {
    for (int i = 0; i < 100000; i++) {
        counter++;  // 實(shí)際上是: 讀取counter, +1, 寫回counter
    }
    return NULL;
}

當(dāng)兩個(gè)線程同時(shí)執(zhí)行時(shí),最終 counter 值可能小于期望的 200000,因?yàn)榫€程可能同時(shí)讀取相同的值,各自加 1 后寫回,導(dǎo)致一個(gè)增量丟失。

為了解決這個(gè)問(wèn)題,操作系統(tǒng)引入了各種同步原語(yǔ),如互斥鎖、條件變量和信號(hào)量等。

技術(shù)永遠(yuǎn)在進(jìn)化。如今,新一代的并發(fā)模型已經(jīng)出現(xiàn):協(xié)程(Coroutine)比線程更輕量,由程序自己控制切換,不需要操作系統(tǒng)介入。

歷史回顧與結(jié)語(yǔ)

回顧這段歷史,我們看到了計(jì)算機(jī)從"一臺(tái)機(jī)器只能做一件事"到"同時(shí)處理成千上萬(wàn)任務(wù)"的驚人進(jìn)化:

  • 批處理系統(tǒng)(1950s):一次只運(yùn)行一個(gè)程序,如早期的 IBM 704
  • 分時(shí)系統(tǒng)(1961):MIT 的 CTSS 首次實(shí)現(xiàn)了多用戶時(shí)間共享
  • 進(jìn)程(1964):MULTICS 系統(tǒng)引入進(jìn)程概念,為程序提供獨(dú)立執(zhí)行環(huán)境
  • 線程(1979):Xerox Alto 操作系統(tǒng)引入輕量級(jí)執(zhí)行單位,共享進(jìn)程資源
  • 協(xié)程(2010s):用戶態(tài)的輕量級(jí)線程,進(jìn)一步降低并發(fā)開銷

這就像交通工具從"一次只載一個(gè)人的獨(dú)木舟",逐步發(fā)展為如今的"高速磁懸浮列車"一樣神奇。

每當(dāng)你打開電腦,運(yùn)行十幾個(gè)應(yīng)用程序,同時(shí)瀏覽網(wǎng)頁(yè)、聽音樂(lè)、下載文件,這一切看似理所當(dāng)然的便利,都是幾代計(jì)算機(jī)科學(xué)家智慧的結(jié)晶。

進(jìn)程和線程,這兩個(gè)看似抽象的概念,讓我們的數(shù)字生活變得如此豐富多彩。下次當(dāng)你的電腦同時(shí)運(yùn)行多個(gè)應(yīng)用時(shí),別忘了感謝那些 60 年代的工程師們——正是他們的創(chuàng)新思維,讓今天的多任務(wù)處理成為可能!

責(zé)任編輯:趙寧寧 來(lái)源: 跟著小康學(xué)編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)