使用 Rust 進行系統(tǒng)編程 — 第一部分
今天的文章是關(guān)于系統(tǒng)編程的。Rust 作為系統(tǒng)編程語言,自然是很適合進行系統(tǒng)編程。
現(xiàn)代計算機是一個非常復(fù)雜的創(chuàng)造物,經(jīng)過幾十年的研究和發(fā)展演變成現(xiàn)在的狀態(tài)。有時它看起來像是黑魔法。這里面沒有魔法,只有科學(xué)。然而,一些像 Alan Turing,Charles Babbage,Ada Lovelace,John von Neumann 和許多其他人的頭腦是不可思議的,因為他們使計算機成為可能。
現(xiàn)在,讓我們深入學(xué)習(xí)系統(tǒng)編程的基礎(chǔ)知識:
進程是什么? 它們是如何創(chuàng)建和執(zhí)行的? 查看 Rust 中的一些代碼示例,并將它們與 C 進行比較
在開始編寫代碼之前,我們將從操作系統(tǒng)主要組件的最底層開始構(gòu)建。如圖 1 所示——任何計算機的最低級別是 Hardware,其次是運行在裸機上的 Kernel 模式。這就是像 Linux 這樣的操作系統(tǒng)所在的位置。

在內(nèi)核模式之上,我們有一個用戶模式。為了使用戶能夠與內(nèi)核交互并使用其他更高級別的軟件,如網(wǎng)頁瀏覽器、電子郵件閱讀器等,它需要一個用戶界面程序。這可以是一個窗口,圖形用戶界面,也可以是一個 Shell,它是一個解釋命令的命令,用于從終端讀取命令并執(zhí)行它們。
進程:父和子
所有操作系統(tǒng)的主要概念是進程。一個進程基本上是一個正在運行的程序。你可以把它想象成一個抽屜,里面包含有關(guān)這個特定程序的所有信息。有些進程在計算機啟動時開始運行,有些在后臺運行,有些由用戶通過 Shell 調(diào)用和交互。
所有進程都有一個 id。當系統(tǒng)啟動時,將啟動第一個進程。這個進程的 id 為1,稱為 init。在此之后,init 將調(diào)用其他進程等等。當我們在 shell 中鍵入一個命令供 OS 執(zhí)行時,系統(tǒng)應(yīng)該創(chuàng)建一個新的進程來運行編譯器。當進程完成編譯后,它將進行一個系統(tǒng)調(diào)用來終止自己。
在 UNIX 系統(tǒng)中,每個新進程都是某個父進程的子進程。進程創(chuàng)建是通過克隆父進程來完成的,這被稱為 forking (圖1-b)。每個進程有一個父進程,但可以有多個子進程。進程的結(jié)構(gòu)類似于樹,其中 init 是根,這意味著它位于層次結(jié)構(gòu)的頂部。
在進程創(chuàng)建之后,除了父進程有一個非 0 ID 號,子進程的 ID 等于 0 外,其他方面父進程和子進程是相同的。接下來,系統(tǒng)用一個新程序替換子進程的執(zhí)行。當進程完成其目的時,它將正常地終止并退出(自愿的)。該進程也可以由于一個錯誤退出或殺死另一個進程(非自愿)。

該系統(tǒng)還跟蹤所有的進程,將它們的數(shù)據(jù)保存在所謂的進程表中。它包含諸如進程 id、進程所有者、進程優(yōu)先級、每個進程的環(huán)境變量、父進程等信息。除此之外,它還保存特定進程處于何種狀態(tài)的信息。每個進程可以處于以下四種狀態(tài)之一:
RUNNABLE — 進程正在運行/主動使用 CPU SLEEPING — 該進程是可運行的,但是正在等待另一個進程先停止/完成 STOPPED — 此狀態(tài)表示進程已暫停以便進一步運行。它可以通過信號重新啟動再次運行 ZOMBIE — 當調(diào)用 “system exit” 或其他人終止進程時,進程將終止。但是,該進程尚未從進程表中刪除
通常進程必須相互交互,并且可以改變狀態(tài),從 Running 到 Sleeping,然后回到 Running (圖1-c)。這通常由 SIGSTOP 信號完成,該信號由 Ctrl + Z 發(fā)出(我們將在接下來的部分中深入討論信號)。與停止的進程一樣,它可以重新啟動。一旦被殺死進入 Zombie 狀態(tài)就不能重新啟動或繼續(xù)。

C VS Rust
在 C 語言中(這是目前 Linux 內(nèi)核編程語言),進程創(chuàng)建首先通過 fork 新進程來完成,然后顯式地要求系統(tǒng)在子進程上執(zhí)行一個新指令。如果我們不這樣做,父進程和子進程將執(zhí)行相同的指令。下面是執(zhí)行 ls 命令的一個例子,它列出了給定目錄的文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
switch (pid = fork()) {
case -1:
perror("fork failed");
break;
case 0:
printf("I'm child process and I will execute ls command");
char *argv_list[] = {NULL};
if (execv("ls", argv_list) == -1) {
perror("Error in execve");
exit(EXIT_FAILURE);
}
break;
default:
printf("I'm parent process and I'll just print this");
}
return 0;
}
正如你所看到的,我們必須手動管理進程,并監(jiān)控執(zhí)行是否成功。此外,我們還必須處理錯誤。如果我們希望一個命令只能由一個子進程執(zhí)行,那么我們必須手動檢查當前進程是否是一個子進程,這是按照 case 0 來完成的。在 Rust 中,標準庫的進程模塊[1]也可以做到這一點:
use std::process::Command;
fn main() {
let child = Command::new("ls")
.env("PATH", "/bin")
.output()
.expect("failed to execute process");
// if no error, program will continue..
}這里的 Command::new() 是一個進程構(gòu)建器,負責(zé)生成和處理子進程。就像在 C 代碼中一樣,我們提供要執(zhí)行的命令、環(huán)境變量、命令參數(shù)和調(diào)用輸出方法。輸出將以子進程的形式執(zhí)行該命令,等待它完成,并返回收集到的輸出。
除了 output() 之外,我們還可以使用 status() 或 spawn()。這些方法中的每一個都負責(zé) fork 一個具有細微差異的子進程:
output():只有在子進程完成運行后,才運行程序并返回Output的結(jié)果;status():將運行程序并在進程編譯后返回ExitStatus結(jié)果。這允許檢查編譯程序的狀態(tài);spawn():將運行程序并返回結(jié)果,該結(jié)果是一個子進程。這不需要等待程序編譯。該選項允許wait和kill指令,或者我們可以獲得該進程的 ID。
在這里,env() 是可選的,因為 Command 非常聰明,可以查找 /bin 文件夾的路徑。最后,所有的錯誤處理都由 expect() 完成。如果 Ok 表示程序成功執(zhí)行或者 Err 表示出現(xiàn)錯誤,進而 panic!)。如果遇到 Err,希望程序不要終止,可以這樣做:
use std::process::Command;
main() {
let user_input = get_user_input(); // helper function to get user input
if let Err(_) = Command::new(&user_input)
.envs("PATH", "/bin")
.status() {
println!("{}: command not found!", &cmd);
}
// the rest of the program...
}
這里的 status() 更方便,如果用戶提供合法命令并執(zhí)行,則調(diào)用它將返回 Ok。但我們只對提供不可用命令時的處理感興趣。這就是為什么我們只檢查 Err 是否返回,如果返回,則在終端中打印 “command was not found” 并繼續(xù)當前程序執(zhí)行,而不是終止。
最后,spawn() 用于管理多個子進程和父進程之間的執(zhí)行順序。它包含 stdin stdout 和 stderr 字段,并且具有 c 程序員所熟悉的 wait() , kill() 和 id() 方法。我們將在下一部分中看到進程的這一部分,當兩個或多個線程可以訪問共享數(shù)據(jù)并且它們試圖同時更改這些數(shù)據(jù)時,我們還將看到 Rust 是如何處理競態(tài)條件的。
總結(jié)
在這個介紹性的部分中,我們回顧了什么是進程,它們是如何創(chuàng)建的,并將 Rust 對進程創(chuàng)建和命令執(zhí)行的實現(xiàn)與 C 進行了比較。我們看到,Rust 代碼不僅不容易出現(xiàn)人為錯誤,而且不那么冗長,很簡潔。在接下來的部分中,我們將介紹如何管理進程執(zhí)行時間和狀態(tài),以及處理系統(tǒng)信號。
原文鏈接:https://www.bexxmodd.com/post/systems-programming-with-rust-1
參考資料
進程模塊: https://doc.rust-lang.org/std/process/struct.Command.html
推薦閱讀
覺得不錯,點個贊吧
掃碼關(guān)注「Rust編程指北」
感谢您访问我们的网站,您可能还对以下资源感兴趣:
国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频