【IT/コンピュータ】プロセスとは何ぞや?

IT関連

この記事の対象読者

ITの勉強をしていると出てくる用語に「プロセス」や「スレッド」があります。

一般にプロセスと言えば「過程」や「工程」のことなので、何となく

「コンピュータにやらせている一連の処理のこと」をプロセスと言うのかな?

などと最初は考えることでしょう。確かに概念としてはそうなのですが、ではコンピュータが認識する「プロセス」の実体とは何か?となると案外言葉にできないものではないでしょうか。

この記事では、自分が「プロセス」というもののイメージを掴むにあたって調べたことなどを中心にまとめていきたいと思います。

Linux のソースコードも交えて、その実態の一端に触れていこうと思うので、是非最後まで読んでいただけると嬉しいです♪

では早速本題に入っていきましょう!

教科書的な表現での「プロセス」

まずはIPAの教科書などで見る表現を見ていきます。

自分が手元に持っている応用情報の書籍では、以下のように定義されています。

プロセスとは、CPUから見た処理単位で、「処理に必要なCPU」「主記憶などのシステム資源」を割り当てるときの単位を指す。

正直、初心者にとっては記号の羅列にしか見えないと思います。

いくつか理解のために必要な言葉を補足しておきます。

  • CPU:処理を行う本体。良く分からなければ小さいおじさんと考えると良いです。
  • 主記憶装置:要するにメモリです。良く分からなければ作業用の机とか、計算用紙くらいに捉えておきましょう。

これを念頭に置くと、プロセスとは、ある処理(=プログラム)を行うときに、「小さいおじさんが何時間くらい計算を行うか」「そのおじさんに計算用紙を何枚与えるか」を記録したものと言い換えられます。

人間が仕事をするときと同じですよね。仕事も以下のように分解することができます。

  • 加工対象の「材料」「情報」があって
  • 労働者がそれらを加工し、
  • 何らかの製品・資料が得られる

プロセスとはこのうち「労働者が加工を行う」部分に該当し、その内訳は

  • 作業の手順は何か?
  • 労働者が何時間作業をする必要があるか?
  • 作業をするために、机・メモ・その他道具がどの程度必要か?

といったようになります。コンピュータに置き換えると、上から順に「プログラム」「CPU時間」「メモリなどの計算資源」となる訳です。

プロセスについて一つ注意するべきことは、同じプログラムでも別々に起動したものは別々のプロセスとして認識されるということです。これも実際の仕事に例えると理解しやすいかと思います。

例えばAさんが工場である部品を組み立てる作業をしているとします。このとき、組み立てる部品はB、C、Dの三つのパーツからなるものとし、これらを組上げて一つの部品を作成するのが作業の手順(=プログラム)とします。

Aさんは同じ作業は可能な限りまとめてやりたいので、BとC、そこにDを組付ける作業を同時に2つずつ行うものとします。このとき、「B、C、D」を組み立てるという作業が2つ同時に進行しており、ある時刻における作業の進行状況が異なることがあり得ます。例えば、BとCは両方とも組付けたものの、Dの組付けは片方しか終わっていない、と言った感じですね。

つまり、Aさんは「B、C、Dを組み立てる」というプロセスを2つ同時に行っており、それぞれの進行度は別々に管理されている、ということです。

コンピュータでも同様で、例えばブラウザでWebページを閲覧するというプロセスも、複数のウィンドウを開く場合には「別々のプロセス」として扱われている訳です。

ここから何が分かるかというと、「プログラム」そのものは「プロセス」ではないということです。

ちょっと考えてみれば当たり前のことですが、「実行中のプログラム・および計算資源の状態」がプロセスだということです。今後、「プロセス」という言葉を目にしたら、

  • 実行されているプログラム
  • そのプログラム処理するCPUの割り当て
  • メモリをどれくらい消費するか

がひと固まりになったものを話題にしているんだな、と考えれば大きく認識を外すことはないと思います。

プロセスについての教科書的な話・概要は以上になります。実際のコンピュータの仕組みはこんな単純なものではないのですが、「処理を行う」部分の本質を抜き出すとこんな感じかなと思います。

※ この記事では「プロセス」と表現していますが、「タスク」と表現している書籍もあります。両者は基本的に同じものと考えて差し支えありません。

※ 今回の記事では、計算資源として「メモリ」だけを記載していますが、本当は色々なものがあるのでご注意ください。処理装置もDSPとかGPUとかありますし、記憶装置に関しても「レジスタ」「キャッシュ」「主記憶」とか、仮想記憶を用いて「SSD・HDD」などの外部記憶装置も使用していたりするので厳密に書くとキリがないです。

Linux ソースコードから見る「プロセス」

大層な見出しを付けていますが、要は「task_struct」構造体の中身を見てみようという話です。task_struct自体は、リソースの情報を格納する構造体へのポインタを保持し、全体を管理するものなので細かい部分までは踏み込みません。自分自身、Linuxの全てを把握しているようなプロでは全然ありませんので、重要そうなところだけピックアップして紹介する形になります。

引用元は以下になります。

sched.h - include/linux/sched.h - Linux source code (v2.6.39.4) - Bootlin
Elixir Cross Referencer - Explore source code in your browser - Particularly useful for the Linux kernel and other low-level projects in C/C++ (bootloaders, C l...

上記サイトにおける、1193行目~1543行目が “task_struct”のソースコードになります。

実際に見ると分かるのですが、定数マクロによって何を定義するのかが色々と分岐しているので、全体像は把握できていないです。あくまでこの記事では雰囲気を掴むことを目標にします。

さて、では実際に中身のうち重要な部分を抜き出すと以下のようになります。

task_struct { pid_t pid; /* プロセスの ID */ 
              struct task_struct *real_parent; /* プロセスの親プロセス */
              char comm[TASK_COMM_LEN]; /* プロセスのコマンド名 */
              cputime_t utime, stime; /* プロセスのCPU 使用時間 */
              mm_struct *mm; /* プロセスのメモリ情報 */ 

/* ー  以下略  ー */

}

※ 上記のコードの情報は以下の資料からそのまま持ってきました。

URL:http://web.tuat.ac.jp/~hiroshiy/os/slides/03-process_thread_1.pdf

それぞれの要素は、引用サイトでは以下の場所にあります。

  • pid:1278行目
  • *real_parent:1291行目
  • comm:1337行目
  • utime・stime:1316行目
  • *mm:1256行目

さて、最初に説明した内容と、これらの対応を見ていきます。

まず、pidについては “Process ID”の略ということですね。複数のプロセスが起動しているとき、それらを区別する必要があるのでID番号を振るために変数が割り当てられているということです。

*real_parent については、プロセスの親となるプロセスへのポインタということになります。基本的にどのプロセスも何か別のプロセスから起動される、ということが前提になっている訳ですね。bashなどのシェルからプログラムを実行する際にはbashが親プロセスとなります。

ちなみに、Linuxにおいて1番最初に起動するプロセスは “init プロセス”と呼ばれ、これには1というPIDが割り当てられます。ほかのプロセスは、ここから派生していくという認識を持っておくと良さそうです。init プロセスで何が行われているのかということについては以下を参照すればざっくりと把握できます。

Linux起動の仕組みを理解しよう[init/inittab編]
カーネルが呼び出されてからログインプロンプトが表示されるまでの間に、一体どのような処理が行われているのか。これを理解するには、この部分の全般をつかさどるinitとその設定ファイルであるinittabがカギとなる。

余談ですが、このように、同じ構造体がつながっていくデータ構造は「単方向リスト」と呼ばれ、データ構造の中ではかなり基本的なものになります。IT系の試験でも出てくるので覚えておくとよいでしょう。

さて、次に comm[TASK_COM_LEN]についてですが、これはコマンド名なのでスルーします。コマンドは「ls」とか「cd」とかの、ターミナルで打ち込むものだと思っておけばよいでしょう。

utime, stime は ユーザーCPU時間とシステムCPU時間です。前者はこのプロセスの処理が使用したCPU時間、後者はOS(カーネルのシステムコール)が使用したCPU時間ですね。Linuxのtimeコマンドが参照している変数がおそらくこれなのでしょう(未検証)。

* mm については、プロセスが使用するメモリについての情報が格納されています。プログラムを実行する際には、以下の要素がメモリ領域に必要となります。

  • テキスト領域(プログラムの命令文を格納するエリア)
  • 静的領域(グローバル変数など、プログラム全体を通して使われる変数を記録するエリア)
  • ヒープ領域(プログラムを実行する中で一時的に確保されるメモリのエリア。※mallocなど)
  • スタック領域(ローカル変数や、関数の呼び出し順の情報を格納するエリア)

これらをひっくるめた領域がプロセスの確保するメモリ領域ということになりますが、それを保持しているということですね。

ということで、最初に説明した内容に対応する部分を、task_struct 構造体の一部を眺めることでもう少し細かく見てきました。今回見たソースコードは「プログラムがどのように動作するのか?」というコンピュータの動作原理とも少しかかわりがあるので、ある意味とっつきづらい部分ではあると思います。しかし、最初に説明したイメージを元に見ることで「プロセス」がどのように管理されているのかが見えてくるはずです。

さて、最後に余談ですが、本当にLinuxカーネルを理解したい方は以下のQiita記事などを見ると良いのではないでしょうか。自分もこれで勉強しようかなと思う次第です。。。

【読解入門】Linuxカーネル (概要編) - Qiita
(2/18(月)):スタック退避手順に関して追記しました。Armv7辺りの32bitアーキを対象にしています。はじめにはじめまして。これまでは個人的に興味があったRTOSのZephyrに関す…

まとめ

今回の記事では、IT初心者が引っかかる(と思われる)「プロセス」の実体について例え話とLinuxカーネルのソースコードを交えながら解説してみました。

正直自分も完璧に理解している訳ではないので、不正確な部分もあるかとは思いますが、初心者がイメージを掴むにはこのくらいの解像度で一度概要を掴んでおくのが良いのではないかなと思います。

さて、今回の内容は以上となります。最後までお読みいただき、ありがとうございました!

コメント

タイトルとURLをコピーしました