Open6

『ふつうのLinuxプログラミング』メモ

こなゆうこなゆう

第1章

コンパイラによるコードチェック

-Wall オプション
一般的な警告オプションがすべてONになる。引数の数の違い、型の違いを警告してくれるようになる。

コンパイラによる最適化

ただ「最適化」というと、「実行速度を速くする」という意味になる。
-O1(-O), -O2, -O3 O3に近づくほど強力な最適化になるが3はコンパイラのバグに遭遇することが多いみたいなのでとりあえず-O2をつけておく。

gcc -Wall -O2 -o hello hello.c

manコマンドで情報を見る

manページはいくつかセクションに分類されている。
セクション1: ユーザコマンド
セクション2: システムコール
セクション3: ライブラリ関数
...

ライブラリ関数のprinftを調べたいときはman 3 printfのように指定する

こなゆうこなゆう

第2章

この書籍が話すこと

  • ファイルシステム
  • プロセス
  • ストリーム

OSとは何?

定義が曖昧だが、Linuxに関して言えば下記のようなソフトウェアパッケージが含まれている

  • シェル
  • procps

Linux = カーネル
カーネルは通常1つのプログラムで構成されていてコンピュータを構成するすべてのハードウェアとソフトウェアを管理している

ハードウェアにはどんなものがあるか?

  • CPU
  • メモリ
  • HDD
  • SDD

比較的単位の大きい物理的な部品のことをデバイスと呼ぶ。カーネルは上記に挙げたようなデバイスをすべて統轄している。
実際はHDDにも複数の種類があり、それぞれ操作方法が異なる。なのでそれぞれに対応するコードがカーネルで必要になる。ただ操作するコード以外は共通しているので、デバイスを操作する部分だけ独立させておく。その特定のデバイスを操作するためのソフトウェア部品のことをデバイスドライバと呼ぶ。略してドライバとも呼ばれる。

ハードウェアと直接やり取りができるのはカーネルだけ。プログラムを通りてハードウェアを操作したい場合はカーネルにシステムコールを使用して操作してもらう。
システムコールの一部

  • open
  • read
  • write
  • fork

我々は、プログラミングをする上で、システムコール以外にもライブラリ関数というものを使用できる。libraryに収められている関数をライブラリ関数と呼ぶ。
ライブラリ関数は内部でシステムコールを使っていることもあればライブラリ関数だけで完結していることもある。

Linuxに用意されている、重要なライブラリの1つが標準Cライブラリ。通称libc。
Linuxで通常使われているlibcはGNU libc(glibc)で、GNUプロダクトの一部。

APIはその名の通りプログラミングをするときのインターフェース。システムコールは、カーネルのAPIといえる。

こなゆうこなゆう

第3章

ファイルシステム

ファイル

世の書籍では3つの意味で使用されている

  • 広義のファイル
  • 狭義のファイル(regular file)
  • ストリーム

広義のファイル

テキストファイル、ディレクトリ、シンボリックリンク、これらはすべて「ファイル」。

ファイルの種類

  • ディレクトリ
    • 他のファイル(広義のファイル)を複数入れることができるファイル。
  • シンボリックリンク
    • 他ファイルの名前を格納したファイル。ソフトリンクとも呼ばれる。
  • デバイスファイル
    • デバイス(ハードウェア)をファイルとして表現したもの。
      • たとえばデバイスファイル/dev/sdaは1台目のSSDまたはHDDを表している。「表している」とは、第5章以降で紹介するAPIを使ってこのファイルにアクセスすると、SSDやHDDに記録されているデータを操作できるということだが、非常に危険なので実際には行わないようにする。
  • 名前付きパイプ
    • プロセス間通信に使うファイル。(本書では扱わない)
  • UNIXドメインソケット
      - プロセス間通信で使用するファイル。現在はTCPソケットで代替できるので本書では扱わない。

広義のファイルまとめ

  • 何かしらのデータを保持する
  • 付帯情報(更新日時など)がついている
  • 名前(パス)で指定できる

ファイルシステムとマウント

ファイルシステムはSSD, HDD, USBメモリのような物理的な記憶媒体の上に存在する。
SSD, HDDの場合、ディスクをパーティション(partition)という区画に区切り、それぞれの上にファイルシステムを用意してマウント(mount)することで一本の巨大なディレクトリツリーができる。

mac上でmountコマンドの実行

$ mount
/dev/disk3s1s1 on / (apfs, sealed, local, read-only, journaled)
~

/dev/disk3s1s1はSSD, HDDのパーティションに対応するデバイスファイルの名前。
↑のデバイスファイルがon /にマウントされている。

ファイルシステムの種類

種類 特徴
ext4 Linuxで現在最も一般的なファイルシステム
xfs 旧SGI社が提供したジャーナリングファイルシステム
btrfs Linux向けのコピーオンライトファイルシステム

この他にprocfs, tmpfs, devfsなどのファイルシステムがあるが、これらは疑似ファイルシステムという。

プロセス

動作中のプログラム = プロセス

プロセスには一意に特定できるプロセスIDが割り振られている。プロセスは1つのプログラムからたくさん作成できるので、プロセスIDがなければプロセスをお互いに特定できない。

シグナル

プロセスIDが役立つ代表的な例としてシグナルがある。
実行中のプロセスを止める Ctrl + C は、カーネルが該当プロセスに割り込みシグナル(SIGINT)を送り、受け取ったプロセスが自発的に終了する。

ストリーム

バイト列が流れる通り道。

ファイルに繋がったストリーム

プロセスがファイルの内容にアクセスしたいときは、カーネルにシステムコールを使用してストリームを作ってもらう。

パイプ

バイト列が行き来していればストリームになるので、ストリームの両端にプロセスがあっても問題ない。
上記のようなストリームをパイプという。

何かしらの出力をgrepするときは

  • 各コマンドを独立したプロセスとして同時に実行する
  • プロセス間をストリーム(パイプ)でつなぐ

ネットワーク通信

ストリームの行き先が別のコンピュータになっていれば、ネットワーク通信になる。

プロセス間通信

パイプやネットワーク通信のようにプロセス同士が
ストリームを通じてデータをやりとりしたり意思疎通をはかることを一般にプロセス間通信と言う。

3大重要概念

  1. データに名前をつけて保存する場所としてのファイル
  2. 何かしら活動をする主体のプロセス
  3. プロセスがファイルシステムや他プロセスとデータをやりとりする手段としてのストリーム
こなゆうこなゆう

5章

ストリーム関連のシステムコール

Linux(UNIX)の入出力で出てくるシステムコール

  • ストリームからバイト列を読み込む read
  • ストリームにバイト列を書き込む write
  • ストリームを作る open
  • 用済みのストリームを始末する close

ファイルディスクリプタ

プロセスがファイルを読み書きをしたり他のプロセスとやりとりをするとき、ストリームを使う。
プログラムからストリームを扱うにはファイルディスクリプタを使用する。

プログラムからfdを見ると、ただの整数値(int)。
カーネルの中には実際にストリームを管理するデータ構造があるが、プロセスから隠蔽するためにカーネルが持っているストリームを番号と対応付けている。

カーネルはストリームとファイルディスクリプタを紐づけて管理している。プログラムからはファイルディスクリプタを指定することでストリームを使用することができる。

標準入力・標準出力・標準エラー出力

普通にシェルからプロセスが起動するとき、どのプロセスにも以下3つのストリームが用意されている。またそれぞれ対応するfd番号がある。

  • 標準入力 → fd 0番
  • 標準出力 → fd 1番
  • 標準エラー出力 → fd 2番
grep print < hello.c | head

を実行した場合、hello.cの中身を標準入力としてgrepが受取り、結果を標準出力として出力し、| のパイプが標準出力を標準入力に変換し、headは標準入力を受取り、最後にディスプレイに結果を標準出力として出力する。

上記の例のように標準出力は次のプログラムの標準入力へと繋がれている場合がありため、エラーも標準出力へ出すとユーザーがエラーに気づけない可能性が高い。なのでストリームを余計に用意してユーザーに読ませたいエラーに関しては標準エラー出力へ出すようにしている。

fdf マクロ 意味
0 STDIN_FILENO 標準入力、デフォルトの入力元
1 STDOUT_FILENO 標準出力、デフォルトの出力先
2 STDERR_FILENO 標準エラー出力、ユーザー向けメッセージの出力先

ストリームの定義

本書におけるストリームとは、ファイルディスクリプタで表現され、read()またはwrite()で操作できるものを指す。
ファイルをopen()するとread()またはwrite()を実行できるものが作られるので、ストリームが存在する。
パイプ、ネットワークソケットもまたストリーム。