Rustを用いたシステムプログラミング入門(第1報)


あなたの元のポストを見つけることができますmy personal blog.
近代的なコンピュータは、研究開発の数十年を経て、現在の状態に進化した非常に複雑な創造です.時々、それは黒い魔法のようであるようです.それの魔法はありません、ちょうど科学.しかし、アランチューリング、チャールズバベッジ、エイダLovelace、ジョンフォンノイマン、および他の多くのような心のいくつかは、これは可能な限り魔法です.
OK、それは導入の十分であり、システムプログラミングの基礎に飛び込む.この部分で我々は学びます.
プロセスは何ですか?
-どのようにして作成・実行されますか?
- Rust中のコード例を見てCと比較します.
コードにダイビングする前に、我々はオペレーティングシステムの主要なコンポーネントの最低レベルからビルドを開始します.図1 Aに示すように、どんなコンピュータの最も低いレベルもハードウェアである、次に、裸の金属で動くカーネル・モードは来ます.これはLinuxのようなオペレーティングシステムがある場所です.
図1 A
カーネルモードの上にユーザモードがあります.ユーザーがカーネルと対話することができ、Webブラウザや電子メールリーダーなどの他の上位ソフトウェアを使用するには、ユーザーインターフェイスプログラムが必要です.これはウィンドウ、グラフィカルユーザインタフェース、あるいは端末からコマンドを読み込み、実行するために使われるコマンドであるシェルである

プロセス:親と子

すべてのオペレーティングシステムの主なコンセプトはプロセスです.プロセスは基本的に実行中のプログラムです.あなたはその特定のプログラムに関するすべての情報を含む引き出しと考えることができます.いくつかのプロセスがコンピュータの起動時に起動し、バックグラウンドで実行されたり、シェルを介してユーザによって呼び出されたり、相互に作用したりする.すべてのプロセスにIDがあります.このプロセスはIDの1を持ち、initと呼ばれます.その後、initは他のプロセスなどを呼び出す.OSが実行するためにシェルでコマンドを入力すると、システムはコンパイラを実行する新しいプロセスを作成します.プロセスがコンパイルを終えたら、システムコールを終了します.
Unixシステムでは、すべての新しいプロセスは親プロセスの子プロセスである.プロセスの作成は親プロセスのクローニングによって行われる.各々のプロセスは1つの親を持っていますが、複数の子プロセスを持つことができます.プロセスの構造はツリーに似ており、initはルートで、階層の上部にあることを意味します.
プロセスの作成後、親プロセスと子プロセスは同じであり、親プロセスは任意のID番号を持ち、子プロセスはidを0に等しくなる.次に、子プロセスの実行を新しいプログラムで置き換えます.プロセスがその目的を満たしているとき、それは終了して、通常(自発的に)終了しました.プロセスは、エラーによって終了することもできます.
図1 B
システムはまた、プロセステーブルと呼ばれるもののデータを維持し、すべてのプロセスを追跡します.プロセスID、プロセス所有者、プロセス優先度、プロセスごとの環境変数、親プロセスのような情報を保持します.それに加えて、それはまた、特定のプロセスがどのような状態で情報を保持します.それぞれのプロセスは以下の4つの状態のいずれかである.
実行可能-プロセスは/アクティブにCPUを使用して実行されます.
スリープ-プロセスは実行可能ですが、最初に停止/終了する別のプロセスを待っています.
停止-この状態はプロセスが更なる手続きのために中断されたことを示します.それは再び信号で実行するために再起動することができます.
ゾンビ-プロセスが終了したときに終了する'システムの終了'または他の誰かがプロセスを殺す.しかし、プロセステーブルから削除されていない.
多くの場合、プロセスは互いに相互作用し、状態を変更し、実行中から眠っている状態に移動します.これは通常aSIGSTOP シグナルは、Ctrl + Zによって発行されます(今後の部品で詳細に信号を確認します).停止したプロセスと同様に、そのアクティビティを再起動できます.ゾンビの状態を除いては、一度は、再起動することはできませんが死亡した.
図1 C

C対さび


公式Linuxカーネルプログラミング言語であるCでは、最初にプロセスを作成し、新しいプロセスをフォークして、子プロセスに新しいディレクティブを実行するシステムを明示的に要求します.そうしなければ、親プロセスと子プロセスは同じディレクティブを実行します.実行の例を示します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;
}
ご覧の通り、手動でプロセスを管理し、実行が成功したかどうかを監視する必要があります.また、エラーを処理しなければなりません.子だけで実行されるコマンドが欲しいなら、現在のプロセスが子であるかどうかを手動でチェックしなければなりませんcase 0 . 錆では、同じことができますstandard library’s process module :
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() 子プロセスを生成し処理する責任を持つプロセスビルダーです.Cコードのように、実行するコマンド、環境変数、コマンド引数、および呼び出しを提供しますoutput 方法.出力は子プロセスとしてコマンドを実行し、それを終了させるのを待ち、集められた出力を返す.
の代わりにoutput() また、どちらかを使用するオプションがありますstatus() or spawn() . これらのメソッドのそれぞれは、子プロセスを微妙な違いでforkする責任があります.output() : はプログラムを実行し、Output , 子プロセスが実行を終了した後に限ります.status() : はプログラムを実行し、ExitStatus アフタープロセスコンパイル.これによりコンパイルされたプログラムの状態を確認できます.spawn() : はプログラムを実行し、Child プロセス.これはプログラムのコンパイルを待ちません.このオプションはwait and kill ディレクティブまたはそのプロセスのIDを取得できます.
こちらです.env() コマンドは/binフォルダのパスを探すのに十分スマートです.最後に、エラー処理はexpect() . を返します.Ok , プログラムが正常に実行されたか、Err 何かがうまくいかないならpanic! . プログラムが終了しないようにしたい場合Err このようなことができます.

use std::process::Command;

main() {
    let user_input = get_user_input(); // custom function
    if let Err(_) = Command::new(&user_input)
                            .envs("PATH", "/bin")
                            .status() {
        println!("{}: command not found!", &cmd);
    }
    // the rest of the program...
}
ヒアstatus() ハンディは、それを返す呼び出しOk ユーザによってlegitコマンドが与えられて実行された場合.しかし、利用できないコマンドが供給された場合、取り扱いに興味があります.そういうわけで、我々はチェックするだけですErr が返された場合には、"コマンドが見つからなかった"コマンドを端末に出力し、終了するのではなく現在のプログラムの実行を続ける.
最後にspawn() は、いくつかの子プロセスと親プロセス間で実行順序を管理するために使用されます.含むstdin stdout and stderr Cプログラマによく知られている分野とwait() , kill() and id() メソッド.次の部分のプロセスのこの部分を見てみましょう.また、2つ以上のスレッドが共有データにアクセスして、同時に変更するようにしたときに、RUSTがどのようにしてレースの状態を気にするかを見ることもできます.

概要


この入門部分では、プロセスとは何か、どのように作成され、どのように作成され、プロセスの作成とコマンドの実行のRustの実装を比較したCに私たちは、錆のコードは、人間のエラーに弱い傾向があるが、それはより冗長でより簡潔なことを見た.次の部分では、プロセス実行時間と状態を管理し、システム信号を扱う