このテキストは、運営している韓国のブログを日本語に翻訳したものです。
韓国のブログ:https://yunsuu.notion.site/e379eccd32c545419f0651f743c12c13?v=35310b282de24cc5a292fe45cc498a32&pvs=74
この記事は「Operating Systems: Three Easy Pieces」を読んでまとめた本です。
著者が無料で公開しているので、興味のある方はぜひ読んでみてください。
本にある良い問題は解いてgithubにアップロードしています。
github:https://github.com/yunsuu/ostep-homework-solution
本のリンク:https://pages.cs.wisc.edu/~remzi/OSTEP/
プロセスの概念
プロセスは一つの仮想化の単位とみなすことができます。
オペレーティングシステム(OS)の重要な概念の一つは"仮想化"です。これはCPUが一つしかないにも関わらず、各プログラムが実行されるたびに独立したCPUを使用しているかのように錯覚させる概念です。このように独立して使用していると思わせる単位をプロセスと呼びます。
つまり、実行中の各プログラムをプロセスと呼びます。
このようなプロセスを同時に複数動作させる方法としては、タイムシェアリング技術がよく使われます。OSの中で有名なコンテキストスイッチングも、このタイムシェアリング技術の一つです。
プロセスはCPUの各レジスタの情報を保存し、ディスクにアクセスすることもあります。
プロセスAPIの種類
プロセスの動作APIには、ウェブ掲示板を作る際のCRUDのように様々な種類があります。
- CREATE
- プロセスの生成。デスクトップ上のプログラムアイコンをダブルクリックするときなど、このAPIが実行されます。
- DESTROY
- プロセスを削除するAPI。不要になったらこのAPIを実行して削除します。
- WAIT
- プロセスを実行せずに一時停止状態で待機するコマンドです。
- Miscellaneous Control (各種制御)
- プロセスを一時停止したり、再度実行したりする機能を提供します。
- Status
- プロセスの実行時間などの状態を表示します。
OSがプログラムを実行する方法
OSはプログラムをどのように準備し実行しますか?
- ディスク上のプログラム情報をメモリにロードします。
- 特定のサイズのメモリ空間をスタックに割り当て、このスタックは関数の引数やプログラムの内部変数を保存する役割を持ちます。
- また、ヒープ用のメモリを割り当てます。ヒープは動的に割り当てられたデータを保存するために使用されます。例えば、mallocを通じてメモリを割り当てるときにこのヒープを使用します。
- 入出力の初期化作業を行います。ユニックスでは、STDIN、STDOUT、STDERRなどのデバイスに対応する3つのディスクリプタを持っています。
パイソンファイルディスクリプタを利用した例示コード
import os
# ファイルの生成とファイルディスクリプタの取得
file_descriptor = os.open('example.txt', os.O_RDWR | os.O_CREAT)
# ファイルディスクリプタを使用してデータを書き込む
os.write(file_descriptor, b'Hello, File Descriptor!')
# ファイルの開始地点に移動
os.lseek(file_descriptor, 0, os.SEEK_SET)
# ファイルディスクリプタを使用してデータを読み取る
data = os.read(file_descriptor, 100)
# 読み取ったデータを出力
print('Read from file:', data.decode())
# ファイルディスクリプタを閉じる
os.close(file_descriptor)
プロセスの状態
プロセスの状態
プロセスは一般的に四つの基本的な状態を持っています: 実行中
, 待機中
, ブロック中
, および 終了
. しかし、ここでは 終了
状態は除外し、残りの三つの状態について説明します。
1. 実行中
実行中
状態は、プロセスが現在CPUで実行中であることを示します。この状態では、プロセスは実際に命令を実行し、作業を進行しています。
2. 待機中
待機中
状態は、プロセスが実行の準備ができているが、まだCPUを割り当てられていない状態を示します。これらのプロセスは実行待ちのキューにあり、CPUが利用可能になると実行状態に遷移することができます。
3. ブロック中
ブロック中
状態は、プロセスが外部イベント(例: 入出力作業)の完了を待っていて、現在進行できない状態を示します。このような状態のプロセスは、関連イベントが完了するまでCPUで実行することができません。
状態遷移
プロセスの状態は様々な理由で変化することがあります。
実行中
→待機中
: この遷移は、OSが現在実行中のプロセスを一時停止して他のプロセスにCPUを割り当てるときに発生します。このプロセスを’Descheduling’と呼びます。待機中
→実行中
: 'Scheduling’は、待機状態のプロセスがCPUを割り当てられて実行状態に遷移するときに発生します。実行中
→ブロック中
: プロセスがI/O作業を開始するときに発生します。プロセスがデータを読み書きする作業を始め、他の作業を進行できなくなり、この状態に遷移します。ブロック中
→待機中
: 外部イベント(例: I/O作業)が完了すると、プロセスは再び待機状態に遷移し、CPUの割り当てを待つことになります。
次の図は、CPUを使用するプロセス2つがあるときの図を時間実行順序で表示したものです。
次の図は、CPUを使用するプロセス1つ、IOインタラプトがあるプロセス1つがあるときの図を時間実行順序で表示したものです。
プロセス0が停止している間にプロセス1を実行している点がわかります。
OSが使用するデータ構造
プロセスリスト
OSはプロセスリストというデータ構造を使用します。プロセスリスト(Process List)は一般的に、コンピュータで現在実行中のすべてのプロセスのリストを指します。このリストはOSの一部で、ユーザーに次のような情報を提供します:
- プロセス識別子 (PID): 各プロセスにユニークに割り当てられた番号です。
- プロセス名: プロセスを識別する名前です。
- メモリ使用量: プロセスが使用しているメモリの量です。
- プロセス状態: プロセスが実行中か、待機中か、停止したかなどの状態です。
- プロセス優先順位: プロセスがCPU時間を割り当てられる優先順位です。
- CPU使用率: プロセスがどれだけのCPUリソースを使用しているかを示します。
- 所有者: プロセスを開始したユーザーアカウントです。
Linuxのプロセスリストを確認するpsを実行したときの様子
プロセス間の関係を示すpstreeを実行したときの様子
レジスタ
OSは、プロセスを切り替える際に、現在のCPUのレジスタをOSに保存し、準備ができているプロセスのレジスタ情報をロードしてプロセスを切り替えます。
Linuxのデバッグツールであるgdbを通じて、以下の写真のようにレジスタの値を確認することができます。