システムデザイン

概要

  • なぜ?への探究心、そして、再発防止のために何をすべきか?
  • ガシャガシャやらずに、常にロジカルに戦略的に。何個かバックアッププランを持っておくように。
  • RFC=仕様書読め

参考

目次

勉強すべきこと

  • 必ずやること

    • システムデザイン
    • 各種用語の説明練習
    • ブロードキャストとかに関してもっときちんと
    • ネットワークアドレス、ブロードキャストアドレス、SMTP、ARP、ローカルホスト
    • SQLかるく復習
    • ネットワーク系はこれが良さそう
    • TCP/IPでどうやって次のMACアドレスを得るの?誰が管理している?
    • IPv6について
    • backupの種類
    • 具体的なデバッグモニタが動かないんですけdお
    • OSIの全列挙
    • スイッチとハブとルータって何。何するの。
    • MACアドレスの構造は?
    • MACアドレステーブルって何?スイッチにあるらしい
    • IP RangeでClass A-E全部教えて。じつは、IP RangeとPrivate IP Range(A-C)は別らしい。
    • FTPは何のために使うのか?
  • これめっちゃ良さそう

  • 優先度つけて(system designがかなり大きい割合を持っていそう)

    • Design a database to support a valet parking system.
  • シェルスクリプトをちゃんと学ぶ

    • "", ''の違いなど
  • 名前解決するレゾルバがDNSよりも優先するローカルにあるhostsファイル

  • BigQuery

  • GSuiteのサーベイ

  • フラッピングの対応は?

  • ナイス値とプロセスの優先度の違い

  • syslogdとは

  • OAuth, basic auth

  • 子が親殺したらどうなる?何のためにやる?

  • httpsのローカルのチェックってなにやるの?

  • buffered IOとunbuffered IOの違い

  • プロセステーブル

  • ゾンビ多いと何が問題?どう検知?どう直す?

  • なぜUDPは1対多でOKと言えるのか

  • パケットのルーティングの仕組み(トップレベルルータに流すのではなく、知らなかったら上流に流している。MACアドレスとの関係は?)

  • バーチャルホスト

  • VPSとは?

  • NASとは?

  • TCPって具体的にデータをどうやって送ってるの?

  • パケットのルーティングは、トップレベルルータに流すのではなく、知らなかったら上流に流す。(どうやって勉強するんだ)

  • 孤児プロセスってどうやってinitに引き取られるの?引き取られたあとはどうなるの?

  • curlでどうやってHTTPリクエストを送るの

  • localhosts

  • ポート

    • ポートはTransport (L4)で初めて出てくる概念なので、pingにはポート指定も何もない
    • PingはICMPと呼ばれるインターネット層で動作するプロトコル
  • systemctl start dockerのsystemctlとは

  • psコマンドのもう少し細かい使い方

  • デーモンとは

  • ストリームとは

  • IPの逆引きについて

    • .in-addr.arpa:
  • topとかいろいろ復習

  • TCPのリスニングってなに

  • tomcatとは https://knowledge.sakura.ad.jp/14427/

  • ポートフォワードとは

  • クッキーのプライバシー・セキュリティの問題

  • SSHのデバッグ

    • どうやってHTTPメソッドを送るの?
  • どうやってSMTPは繋いだパソコンを認識しているの?

  • DNSのやり取り

  • routeの解析方法

  • RESTAPIの使い方

  • POST通信とは?

  • vmstat https://qiita.com/saikoro-steak/items/0ae51b4f5de520b37a44#_reference-126384302dd0632c1402 https://qiita.com/saikoro-steak/items/244362bcceec4df19343

  • stack, heap領域

  • nginxとは

  • どうやってHTTPの内容を盗み見るの?

  • Webサーバの挙動というか順番は?

  • HTTPの標準化についてW3C以外のものとは?そもそもW3Cとは?

  • ゾンビプロセス、どうやって検知・排除するの?

  • ICMP

  • DHCPの設定はどこ?-

  • プロキシのキャッシュを逃れてサーバにアクセスする必要があるときはどうする?

  • SSl http://wa3.i-3-i.info/word116.html (リンク切れ)

  • SSL系のデバッグどうするん

  • killした後の話

  • Unix hanbookをよめ

  • システムコール

  • docker

  • アパッチ

  • systemd

  • ポート

  • どうやってHDDを変えるの?ディスクフルの対応方法は?

  • トラブルシューティング系のシミュレーションしとく

  • OOP

  • ネットワークの可視化ツールを具体的に試す

  • VLANって何

参考

OS

  • ソフトとハードのinterpreterとなり、UIとして動くもの。

データベース

  • RDBMS ・・・複雑なデータに向いている。データの肥大化による性能劣化が起きることがある。
  • NoSQL・・・単純なデータに向いている。データが肥大化しても性能劣化が起こりづらいが、複雑なデータは苦手。

Unix

仮想メモリ

  • 仮想メモリと物理メモリはページテーブルによって割り当てられる。

    • これによって物理メモリの断片化を気にすることなく仮想メモリを使える。
    • デマンドページングと言って、参照が発生した仮想メモリに対して実メモリを割り当てていく。
    • さらに、forkした時に仮想メモリのページは共有されて、オンデマンドに子のページが作られる(コピーオンライト)。
  • ユーザ空間では、テキスト・データ・ヒープ・スタック域が割り当てられる。

    • heapはbrk(2)とかをした時に使われる
    • bssにグローバル変数を置く
    • stackは関数の引数やローカル変数や関数の呼び出し順(これは要するに関数がDFS的だからstackで表されるねという話)
  • 動的リンクと静的リンク

    • 静的リンクはめっちゃ仮想メモリの機能を使ったうまいやつ
    • 動的リンクでは、オブジェクトファイルを予めリンクしてしまうので
      • オブジェクトが実行可能ファイル毎に複製されるので、 ディスク空間を多量に浪費してしまう。
      • 複数のプロセスが、 同一の関数を異なるテキスト・セグメントにロードして実行するので、 メモリを不必要に浪費してしまう。
      • ライブラリ関数を変更した場合、 それを使うすべての実行可能ファイルは、再リンクしなければならない。
      • 一方、ダイナミック・ローダは、 ライブラリがマップされたときにリンケージ・テーブルのエントリを、 共有ライブラリの関数のアドレスで埋めます。 このリンケージ・テーブルは、関数呼び出しのジャンプ・テーブルとして働きます。
    • 一方共有ライブラリは、共有ライブラリから出力ファイルへはテキスト (コード) またはデータをコピーしない
      • ダイナミック・ローダが、 実行時にそのライブラリをプロセスのアドレス空間にマップします。
      • リンカは、プログラムから共有ライブラリ・ルーチンおよびデータへの参照を、 リンケージ・テーブルのエントリにバインドするだけ

デバイスドライバ

  • ioctl

シェル

  • 全てのシェルは新たにプログラムを起動するときに0, 1, 2のファイルディスクリプタをオープン

    • それぞれSTDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
  • stty -aで、Ctrl-Cみたいなものでどんなシグナルを送れるかを一覧できる

    • Ctrl-S, Qのように、シグナルを発行しないものもある
  • ttyコマンドで自分の端末名がわかる

    • 端末名がわかると、psコマンドでどの端末から立てられたプロセスなのかがわかる
    • linuxに直接接続している場合はtty, そうでない場合はptsとなる
    • デーモンは?というやつで、標準入出力・エラー出力が全部/dev/nullに繋がれる
    • ちなみに、GUIから起動した普通のターミナルはtty…ではなくpts。多分本当に黒画面のやつがttyなんやね
  • デーモンをC言語レベルで作る場合は、以下を行う

    • initプロセス(プロセス番号1)を親プロセスとしていて
    • 制御端末を持たないプロセスをデーモンと認識する

リダイレクト

カーネル

  • プログラムの流れ

    • カーネル→シェル→アプリケーション
    • カーネル→アプリケーション
    • カーネル→システムコール→アプリケーション
  • /etc/passwd

    • ユーザ名・パスワード・ユーザID・グループID・コメント・ホームディレクトリ・シェル
    • ところでシェルのbashはbourne again shell, ちなみに元のbashはbourne shell

システムコール

  • システムコール一覧
  • よく使うやつに限ったもの (リンク切れ)
  • カーネルの昨日を使うためのC言語の関数(manで見るときは2)
  • 例えば、sbrk(2)はメモリ割り当てをする。malloc(3)を実装するのに必要
  • 他にも、kill(1)はkill(2)のために必要
  • どれくらいのどんな種類のシステムコールが使われているかは、strace /bin/echo helloなどで確認できる
read(3, "", 4096)                       = 0
close(3)                                = 0
open("/usr/share/locale/ja_JP.eucJP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/ja_JP.eucjp/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/ja_JP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/ja.eucJP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/ja.eucjp/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/ja/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja_JP.eucJP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja_JP.eucjp/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja_JP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja.eucJP/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja.eucjp/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/ja/LC_MESSAGES/procps-ng.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
kill(14031, SIGTERM)                    = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?

+++ exited with 0 +++

名前何の略?やること備考
brkbreakヒープ領域のブレーク値(=ヒープ上の未割り当てメモリの先頭アドレス)を変更する。現在のブレーク値との大小によって割当・開放を切り替えられるsbrkという差分指定のbrkもある。mallocと併用不可
killkillプロセスIDやプロセスグループを指定して、シグナルを送る
dupduplicateファイルディスクリプタを複製して最小未割当ファイルディスクリプタに割り当てる複製と言っているが、実際に使うのはファイルディスクリプタの付け替えに使いがち。dup2というファイルディスクリプタを指定できる奴もあって、こっちのほうが便利。

入出力

  • unbuffered I/O
    • open, read, write, lseek, closeの5つ(標準入出力ルーティンではない、ファイル書き込みたち)
    • open, openat ファイルをオープンする。openatはfdからの相対パスでのオープン
    • creat 新規ファイルの作成(open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)と同値。WRONLYなのでつらいね)
    • lseek openされたファイルは、ファイルオフセットという位置を管理する
      • これをセットしたりいじったりする。シーク不能ならば-1を返す(パイプ、FIFO、ソケットに対してはシーク不能)。
      • ファイルサイズを超えた位置にシークすることは許されていて、writeするとシークしていない部分はちゃんと0で読めて、しかもディスクブロック数は抑えられるらしい(hole, 間隙)。
    • read 要求バイト数を指定して、ファイル/ネットワーク/パイプ/FIFO/レコード型装置から「読む」。30文字しか入っていない状態で100バイトを読もうとすると、readはまず30文字を返し、次に読むときに次に0文字を返すらしい。
    • 同じファイルを複数のプロセスからいじる場合、lseek->read, writeがアトミックである必要があるが、それはpread, pwriteで保証できるらしい(ちなみに、appendは自動的にatmicなので気軽に使って良い)
    • fcntl 初めにopenした時にO_WRONLYといったが、あとでappendに変えたい、みたいなことができる

プロセス

構造

  • プロセスはinitプロセスを根とした木構造

    • initプロセスの番号は常に1
    • pstreeを使うとプロセスの木構造を見ることができる!
    • プロセスグループIDとは、initの直接の子cの部分木をグループcと名づけている
  • ゾンビプロセス

    • プロセステーブルのエントリでしかない(4kb-8kbくらい)ので、あまりリソースは食わない。
    • 原因は二点
      • 親が先に死んだプロセスは孤児プロセス
      • 子プロセスが終わったのに親プロセスがwait()してくれなかったプロセスはゾンビプロセス(その意味では、全ての正常終了するプロセスはゾンビプロセスの状態を一瞬介す。これ簡潔で良い: A zombie process is simply a process that has exited but its parent has not yet read the processes exit code. )
    • 対処方
      • 基本的にはゾンビプロセスそのものはkillでは殺せない
      • 親を殺しても、親の親にすげ替えられるだけ
      • ゾンビプロセスを終了させるためには「kill -9 」コマンドを実行しますが、ゾンビプロセスの親プロセスが CentOS7 の場合「systemd」、CentOS6 の場合「init」の場合は OS 再起動しかありません。
      • また、kill -9 コマンドでもゾンビプロセスが消えない場合は、やはり OS 再起動しかありません。
    • 起きうる問題
      • 基本、1個や2個ある分には問題ない
      • ただし、ゾンビプロセスが継続的に発生すると、プロセステーブルが一杯になり、場合は新規でプロセスを生成することができなくなります。(forkが失敗します)

システムコール

  • プロセス制御はfork, exec, waitpid

    • spawn=fork+exec
    • execは7種類ある
  • fork(2)

    • 自分のプロセスコピーを作る
    • プログラムポインタの位置まで同じプロセスがその時点で作成される。キモい。
    • forkの返り値によって親か子かがわかるという仕様
  • sched*(2)はタスクの優先度を指定する

    • niceとかpriorityとか

タスクの種類と優先度

  • https://linuxjm.osdn.jp/html/LDP_man-pages/man7/sched.7.html

  • SCHEDはschedulingのsched

  • 優先度

    • 静的優先度=120+nice in [-20, 19]。niceと静的優先度は同値
    • 動的優先度=CPUを使いまくっていると優先度が下がってしまう
  • タスクにはいくつかの種類がある

  • スケジューリングポリシー

    • SCHED_OTHER, SCHED_BATCHは、niceにより割当時間が変わる(1違うと1.25倍伸びる, 5変わると3倍伸びる。nice値はthreadごとに割り当てられる!!)
    • SCHED_IDLE(超優先度低い)
    • SCHED_FIFO, RR (FIFOは入力待ちまで他に制御が映らない、と思いきや1秒に一回移る(入力待ちしないプログラムだったらずっと俺のターンだと思っていたのだけど…)。RRは他にFFにタイムアウトが付いたやつ。リアルタイムで、priority = 1-99)
    • SCHED_DEADLINE(散発タスク)
  • 優先度の分類

    • 優先度は1-129まである
    • 1-100がrtprioで、101-129が普通のpriority
    • rtprioにはnice値は無いし、otherのプロセスにはrtprio値はない(完全に一対一)
    • rtprioはFIFOかRR
    • psでこれを確認するには、ps -C stress-ng -o comm,pid,ppid,cls,rtprio,nice,%cpuなどとする
  • スケジューリングポリシーを変えるにはsched*(2)を使う

  • 優先度とスケジューリングポリシースケジューリングされる

パイプ

  • 入力したものを出力するだけの存在、まさに「パイプ」
  • これクソわかりやすいパイプの説明
    • 要するにdupは開いてるファイルディスクリプタを入力して、閉じてるファイルディスクリプタのうち最小のものに帰る

シグナル

  • killコマンドで送れる

  • プロセス番号とシグナル番号を指定すると、プロセスがシグナルをコールバック的にtrapできる(trap=シグナルのコールバック設定)。

    • C言語では、signal(3)関数(デフォルトで無視する、みたいなsig = signal( SIGTSTP , SIG_IGN );という書き方もある)
    • シェルスクリプトでは、trap 'command' [signal id...](これでEXITシグナルをtrapすると、プログラム終了時にメッセージを出すとかtmpファイルを消すとかができる)
    • SIGKILLだけはtrap, 無視不能!(強制終了なので)
  • シグナルは何と言うか実装指針であって、別にsigintを受け取って終了しないような実装をしてもよい(sigkillだけは特別!!)

    • 例えば、ddコマンドはSIGUSR1を受け取って進捗状況を表示する
  • kill -lで全シグナルをチェックできる。よく使うのは以下。

番号名前説明
0EXITプロセス終了時に、プロセスが自分自身に対して送出する
1HUPハングアップシグナル(SIGnal HUng uP)。XWindow のクローズで発行される。また、慣例的にデーモンのリセットに使用される。端末で実行しているコマンドは、SIGHUPを受け取ると終了します。従って、X(X Window System)環境でコマンドを実行している最中に端末アプリケーションを終了すると、そのコマンドは終了します。また、ssh接続でコマンドを実行している最中に回線が切断された場合も、そのコマンドは終了します。これを防ぐのが「nohup」コマンドです。「nohup コマンド &」のように、コマンドnohupでコマンドをバックグラウンド実行させると、端末を終了させてもコマンドがSIGHUPを受け取らないようになり、その結果、コマンドは終了しなくなります。サーバプロセスの多くはSIGHUPを受け取るとプロセスを終了して再起動します。このため、SIGHUPシグナルは「設定ファイルの再読込」をさせたいときにも使われます。
2INTCtrl+C や Del キーを押したときに発生する割り込みシグナル(Ctrl-Cとkill 2は実はちがくて、Ctrl-Cはその子にも全部SIGINTを送る)
3QUITCtrl+\ を押したときに発生するクイットシグナル。デフォルトはコアダンプする。pingなど処理して殺せないものもある
9KILLプロセスを強制終了するためのキルシグナル。強制終了なので当然、trap はできない。
15TERMプロセスを終了させるための終了シグナル。kill コマンドはデフォルトでこのシグナルを使用する。INTとTERMの違いは、ターミナルのCtrl-Cで発行できるかどうかくらいの違いで、たいして変わらない
18CONTプロセスに再開を通知(fg)
19STOPプロセスに中断を通知, ハンドルできない!!
20TSTPプロセスにサスペンドを通知する。(Ctrl+Z)
  • STOPとKILLは無視、ハンドルはできないので注意

  • 基本Ctrl-CはSIGINTだが、全ての子プロセスに対してSIGINTを送っている。つまり、親だけではなく子も全部死ぬ。

    • 一方、kill -SIGINTをすると、親だけ死ぬ。子は孤児となってinitプロセスに引き取られる
  • 子プロセスがSTOP, CONT, EXITした時には、親プロセスにSIGCHLDが送られて、waitpidがreturnするらしい。

  • プロセスの状態 状態遷移

    • R TASK_RUNNING: TASK_RUNNING(実行中)
    • S TASK_INTERRUPTIBLE: sleep, cin待ちなど
    • D TASK_UNINTERRUPTIBLE: シグナルを処理することすらできないような大事なタスクをやってる。Dは非常に短い時間のみであることが期待されている。
    • T TASK_STOPPED: SIGSTOPやSIGTSTPで止まった
    • TASK_TRACED: デバッガで別プロセスに監視されているとこの状態になる
    • Z EXIT_ZONBIE: EXITなことに注意。ゾンビ。プロセスに関する統計情報を親プロセスが収集できるようにするためだけに残されている。
    • EXIT_DEAD: もう終わり。
    • おまけ: TASK_KILLABLE killは重要なのでカーネル2.6.25からはこの状態が加わった。
  • D Stateが長いとマジでヤバい

    • 本当にリブートしか対処のしようがない。kill -KILLも効かない(uninterruptableなので)
  • kill -KILLで殺せない場合

    • 基本的にSIGKILLはinitプロセス以外を「いずれ」殺す(すぐに殺すとは言っていない)
    • initプロセスは殺せない
    • テープのようなデバイス待ち(ps D stateの時)→デバイスの処理を終了させてから殺す。デバイスの処理が終わるのを待つしか無い、終わらなそうなら殺せない。
    • プロセスが、使えないリソースを待っている場合(NFS=ネットワークファイルシステムなどでハードリンクしようとした場合)→kill -QUITなら殺せるという情報が会ったが、そんなはずなくない?
    • プロセスがゾンビ→rebootしか方法がない
  • topコマンドでkを押すとkillコマンドみたいな感じでシグナルを送れる

ファイルシステム

  • 実は/とnull文字以外の文字は全て使えるが印字可能な文字だけを使いましょうね

  • i-node

    • i-node
      • ファイルの属性とデータの場所を管理するデータ型
    • ディレクトリは、ファイル名→iNodeを管理
      • 1つのinodeに対して複数のファイル名を割り当てることができる(ハードリンク、lnの-sオプションなし版)
      • シンボリックリンクはパスが書かれてるだけのただのファイル
    • i-nodeの個数制限がある!
      • ディスクは有り余っていても、i-nodeが枯渇してリソースフルになる可能性もある
    • リテインカウントみたいなことができる
  • ハードリンクとソフトリンク