はじめに
最近 コンピュータはなぜ動くのか 第2版 知っておきたいハードウエア&ソフトウエアの基礎知識 を読みました。
特に面白かったのは以下です。
-
アセンブラ
メモリの動きはこんななんだ〜と思って面白かったです。高水準言語では短く書ける処理も、内部では色々やってると知って、もうif文もfor文も書けません😂 -
ネットワーク
自宅のルータは、一般的なWEBページを名前解決できないけど、他のDNSサーバに聞きに行くってどうやってるんだろう?というのがちょっとわかって嬉しかったです。これまでずっと ISP のルータに聞きに行ってくれてたんだね、ありがとうルーターちゃん🥲
コンピュータ
コンピュータの3大原則
- コンピュータは、入力、演算、出力を行う機械である(ハードウェア)
- プログラムは、命令とデータの集合体である(ソフトウェア)
- コンピュータの都合は、人間の感覚と異なる場合がある(コンピュータが取り扱うものは何でも数値)
ハードウェア
IC(集積回路)から構成される。個々のICには数多くのピンがついている。ピンは、入力用または出力用のいずれか
いくつかのICが連携して、外部から入力された情報を内部で演算し、その結果を外部に出力する
ソフトウェア
命令(関数)とデータ(変数)の集合体
命令とは、入力、演算、出力をコンピュータに指示するもの
どんなプログラムでも中身は数字の羅列になっていて、個々の数値は命令またはデータのいずれか
CPU
コンピュータの頭脳。プログラムを解釈・実行し、内部で演算を行い、メモリとI/Oを制御する
クロック信号に合わせてメモリから命令を読み出し、順番に解釈・実行する
クロック信号
クロックジェネレータが発する、時計のようにカチカチと電圧の高低を繰り返す電気信号
クロック信号の周波数は、CPUの動作速度を示す尺度となる。現在主流のCPUなら3GHz~4GHz程度
クロックジェネレータ
内蔵した水晶の周波数(振動数)に応じてクロック信号を生成
レジスタ
| レジスタ | 説明 |
|---|---|
| 汎用レジスタ | 任意の用途で使える |
| フラグレジスタ | 命令の実行によって、データがどのような状態になったかを示す。データがゼロになった、マイナスになった、桁あふれになったことを0と1で示す |
| プログラムレジスタ | 次に実行する命令のアドレスが格納される。1つの命令を実行すると、プログラムレジスタの値が、次に実行する命令のアドレスに自動で更新される |
| スタックポインタ | メモリの中のスタック領域のアドレスが格納される |
メモリ
命令とデータを記憶する
内部が8ビットごとのデータ格納領域に区切られていて、それぞれの領域を区別するための番号(アドレス)がつけられている
I/O
パソコン本体と、HDD、ディスプレイ、キーボードなどの周辺装置を接続して、データの受け渡しをする
入力装置と出力装置のどちらをつなぐかなどの設定や、受け渡すデータの格納を行う
接続
CPU、メモリ、およびI/Oが持つピンを電線で相互に接続し、個々のICに電源を与え、CPUにクロック信号を供給すれば、ハードウェアとしてのコンピュータが完成する
各ICの内部では、膨大な数のトランジスタが集積されている
電線
接続する電線は、1本で1ビットの2進数を伝える
低いときは0(0V)、高いときは1(+5V)
| 線 | 説明 |
|---|---|
| データ線 | データを受け渡す |
| アドレス線 | アドレスを知らせる |
| 制御線 | CPUがメモリとI/Oのどちらを相手にするのか、読み出しなのか書き込みなのかを区別する |
| 電気信号 | 相手に何かの意思表示をする |
|---|---|
| 正論理 | 通常時に電圧を低くしておいて、意思表示をするときに電圧を高くする |
| 負論理 | 通常時に電圧を高くしておいて、意思表示をするときに電圧を低くする |
ワンチップマイコン
CPU、メモリ、I/O、クロックジェネレータを1つのICにまとめたもの
電化製品や自動車などで使われている
ソフトウェア
プログラムの流れ
- 順次
- 分岐
- 繰り返し
フローチャート
プログラムの流れを表す図
ジャンプ命令
プログラムの任意の位置に、処理の流れを変える命令
プログラムレジスタに、ジャンプ先のメモリアドレスが設定される
構造化プログラミング
高水準言語では、プログラムの流れを順次、分岐、繰り返しだけで表し、ジャンプ命令は使わないという考え
イベントドリブン
イベントの種類に応じてプログラムの流れが決定する
状態遷移図を使うと表しやすい
アセンブラ
C言語のソースコードをアセンブラに直してみる
#include <stdio.h>
int main(void) {
int total = 0;
for (int i = 0; i < 11; i++) { // 0〜10
total += i;
}
printf("合計: %d\n", total);
return 0;
}
SUM START
LD GR1, ZERO ; 変数 i に 0 を代入(初期化)
LD GR2, ZERO ; 合計用の変数 total に 0 を代入
LD GR3, LIMIT ; 上限(11)を GR3 に読み込む
LOOP CPA GR1, GR3 ; i と 11 を比べる
JZE END_SUM ; i が 11 になったらループを終える
ADDA GR2, GR1 ; total に i を足す(total = total + i)
ADDA GR1, ONE ; i を 1 増やす(i = i + 1)
JUMP LOOP ; もう一度ループの最初に戻る
END_SUM ST GR2, ANS ; 合計結果をメモリに保存する
RET ; プログラムを終了(呼び出し元に戻る)
; ===== データ定義部 =====
ZERO DC 0 ; 定数 0
ONE DC 1 ; 定数 1
LIMIT DC 11 ; 定数 11(ループの終了条件)
ANS DS 1 ; 合計を格納するための領域
END
アルゴリズム
明確に定義された有限個の規則の集まりであって、有限回適用することにより問題を解くもの。例えば、sin(X)を決められた精度で求める算術的な手順を、もれなく記述した文
番兵のアルゴリズムを体感してみる
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
const int N = 10000000; // 1000万
const int TRIALS = 100; // 試行回数
int target = N - 1; // 最後の要素を探す
int *list = malloc(sizeof(int) * (N + 1)); // 番兵用に +1
if (!list) {
printf("メモリ確保失敗\n");
return 1;
}
// 配列を初期化
for (int i = 0; i < N; i++) {
list[i] = i;
}
double total_time_no_sentinel = 0.0;
double total_time_with_sentinel = 0.0;
clock_t start, end;
// ===== 番兵なし版 =====
for (int t = 0; t < TRIALS; t++) {
int ansIdx = 0, found = 0;
start = clock();
while (ansIdx < N) {
if (list[ansIdx] == target) {
found = 1;
break;
}
ansIdx++;
}
end = clock();
total_time_no_sentinel += (double)(end - start) / CLOCKS_PER_SEC;
}
// ===== 番兵あり版 =====
list[N] = target; // 番兵
for (int t = 0; t < TRIALS; t++) {
int ansIdx = 0;
start = clock();
while (list[ansIdx] != target) {
ansIdx++;
}
end = clock();
total_time_with_sentinel += (double)(end - start) / CLOCKS_PER_SEC;
}
printf("平均実行時間(%d回)\n", TRIALS);
printf("【番兵なし】%.6f秒\n", total_time_no_sentinel / TRIALS);
printf("【番兵あり】%.6f秒\n", total_time_with_sentinel / TRIALS);
free(list);
return 0;
}
↑ 平均実行時間(100回)
【番兵なし】0.021079秒
【番兵あり】0.019883秒
Cだと誤差の範囲か。最近のCPUはパイプライン、投機実行などの高速化が色々あるらしく、もうプログラマの考えるところは少ないのではとか思ったり(少なくとも私程度の知識量ならおとなしくCPUにお任せでいいのかしらとか😅
import time
import numpy as np
N = 10_000_000 # 要素数 1000万
TRIALS = 10
target = N - 1 # 最後の要素を探す
arr = np.arange(N + 1) # 番兵用に +1
# ===== 番兵なし =====
total_time_no_sentinel = 0.0
for trial in range(TRIALS):
start = time.perf_counter()
ansIdx = 0
found = False
while ansIdx < N:
if arr[ansIdx] == target:
found = True
break
ansIdx += 1
end = time.perf_counter()
total_time_no_sentinel += end - start
print('番兵なし', trial, '回目')
# ===== 番兵あり =====
arr[N] = target # 番兵
total_time_with_sentinel = 0.0
for trial in range(TRIALS):
start = time.perf_counter()
ansIdx = 0
while arr[ansIdx] != target:
ansIdx += 1
found = (ansIdx < N)
end = time.perf_counter()
total_time_with_sentinel += end - start
print('番兵あり', trial, '回目')
print(f"平均実行時間({TRIALS}回)")
print(f"【番兵なし】{total_time_no_sentinel / TRIALS:.6f}秒")
print(f"【番兵あり】{total_time_with_sentinel / TRIALS:.6f}秒")
↑ 平均実行時間(10回)
【番兵なし】1.981564秒
【番兵あり】1.667981秒
これwhileとfor変えたら、番兵ありの方が時間遅くなったりしました🤔 python は裏で処理色々やってるから、プログラマにはわからないオーバーヘッドとかがあるのだとか、?
配列
複数のデータを格納するためのメモリ領域をまとめて確保し、全体に1つの名前をつけたもの
主なデータ構造
スタック
データを山のように積み上げる
↓ 必要なもの
- スタックのサイズを要素数とした配列
- スタックの最上部に格納されたデータのインデックスを表す変数(スタックポインタ)
- プッシュ関数
- ポップ関数
キュー
データを行列のように並ばせる
↓ 必要なもの
- 任意のサイズの配列
- キューの先頭のデータのインデックスを表す変数
- キューの末尾のデータのインデックスを表す変数
- エンキュー関数
- デキュー関数
配列の末尾までデータを格納してしまったら、次の格納位置は配列の先頭に戻る
リスト
データの並びを任意に変更できる
配列の他の要素のアドレス(次にどの要素につながるかという情報、次の要素のアドレス)をポインタとして保持する(自己参照構造体)
2分木
データの並びを二またに分ける。2分探索木という形式でよく使う
繋がり情報のメンバを2つ持った自己参照構造体にする
オブジェクト指向プログラミング
オブジェクトそのものに重点をおき、対象の振る舞いや操作が、対象の属性として備わるという考え方に基づいてプログラミングすること。プログラムの再利用が容易になり、ソフトウェアの生産性を高められる。
クラス
関数と変数のグループ
クラス内の関数と変数はメンバと呼ぶ
オブジェクト
クラスはクッキーの型であり、くり抜かれたクッキーがオブジェクトである
オブジェクトの持つ関数を呼び出すことを、メッセージパッシングという
主なプログラミングテクニック
- 継承:既存のクラスの持つメンバを引き継いで新たなクラスを作成すること
- カプセル化:クラスの持つメンバの中で、クラスの利用者に見せる必要のないものを隠すこと
- 多態性:同じメッセージに対してオブジェクトごとに様々な動作を行うこと
クラスの使い方
- クラスが持つメンバを個別に利用する
- クラスの定義の中に他のクラスを含める(集約)
- 既存のクラスを継承して新しいクラスを定義する
クラスライブラリ
再利用可能なクラス群
インタフェース
クラスという部品を使う人にとって、クラスがどのように見えるかという仕様
モデリング
- 部品化(現実世界を複数のオブジェクトの集合体として分割すること)
- 省略化
UML
| 名称 | 主な用途 |
|---|---|
| ユースケース図 | プログラムの使われ方を示す |
| クラス図 | クラスおよび複数クラスの関連を示す |
| オブジェクト図 | オブジェクトおよび複数オブジェクトの関連を示す |
| シーケンス図 | 複数オブジェクトの関連を時間に注目して示す |
| コミュニケーション図 | 複数オブジェクトの関連を協調関係に注目して示す |
| ステートマシン図 | オブジェクトの状態変化を示す |
| アクティビティ図 | 処理の流れを示す |
| コンポーネント図 | ファイルおよび複数ファイルの関連を示す |
| デプロイメント図 | コンピュータやプログラムの配置方法を示す |
データベース
DBシステムの構成要素
- スタンドアロン型
- ファイル共有型
- クライアントサーバ型
- WEBシステム
構造化プログラミング
レコードのリレーションシップの形態は、1対1、多対多、1対多のいずれかになる。多対多になる場合は、リンクテーブルが必要
インデックス
データの検索速度を向上させる内部的な仕組み。フィールドにインデックスを設定すると、そのフィールドのためのインデックステーブルが自動的に作成される。検索の速度が向上する代償として登録の速度が低下する
インデックステーブル
フィールドの値と、そのフィールドを持つレコードの位置を示すもの。インデックステーブルは、もとのテーブルに比べてフィールド数が少ないので、高速に検索が行える
DBMS
簡単にデータファイルを読み書きできるようにし、データを矛盾なく安全に保つ機能を持つ。参照整合性のチェックや、トランザクション制御を行なう
ネットワーク
| LAN | |
|---|---|
| 用法 | 自宅や企業内のネットワーク |
| プロトコル | イーサネット |
| 識別番号 | MACアドレス |
| WAN | |
|---|---|
| 用法 | インターネットのように企業と企業の間を結ぶネットワーク |
| プロトコル | IP |
| 識別番号 | IPアドレス |
LAN と WAN
ルータがLANをWANにつなぐ
ルータの先はプロバイダ(インターネット接続事業者)のルータにつながっている
プロバイダのルータから先は、また別のルータで、他のプロバイダや他の企業につながっている
| MACアドレス | |
|---|---|
| 識別方法 | ハードウェア的。基本的に後から変更できない |
| ビット数 | 48ビット。8ビットずつ6つにハイフンで区切り、それぞれを16進数で表す |
| 表し方 | 上位24ビットがハードウェアメーカーの識別番号で、下位24ビットが製品の機種とシリアル番号 |
| 特徴 | 世界で一意 |
| IPアドレス | |
|---|---|
| 識別方法 | ソフトウェア的。状況に合わせて任意に設定できる |
| ビット数 | 32ビット。8ビットずつドットで区切理、それぞれを10進数で表す |
| 表し方 | 上位桁はネットワークの識別番号(LANの識別番号、ネットワークアドレス)で、下位桁はホストの識別番号(ホストアドレス) |
| 特徴 | サブネットマスクで、ネットワークアドレスとホストアドレスの区切りを示す |
DHCP
動的にホストを設定するプロトコル
DHCPサーバ
DHCPのプロトコルを使い、ホストにIPアドレスとサブネットマスクなどを自動的に設定する。LAN内のコンピュータに割り当てられるIPアドレスの範囲とサブネットマスクの値などを記憶している。新たなホストをLANに接続すると、DHCPサーバから他のホストに割り当てられていないIPアドレスが知らされる。
デフォルトゲートウェイ
LANとインターネットを繋ぐ最初のルータ
DNSサーバ
ドメイン名に対応するIPアドレスを記憶して知らせるサーバ。1つのDNSサーバで名前解決できない場合は、他のDNSサーバへ問い合わせる。pingだけでなくwebブラウザでドメイン名のサイトを閲覧するときも、DNSサーバへの問い合わせが行われている
接続
自宅のLANでは、デフォルトゲートウェイ、DHCPサーバ、DNSサーバのIPアドレスが全て同じになっている
デフォルトゲートウェイであるルータがDHCPサーバとDNSサーバの機能を持っているから
小規模なLANであれば問題ないが、大規模なLANではルータ以外にDHCPサーバとDNSサーバの役割を持つコンピュータを用意する
TTL
インターネットに送り出されたデータは、いくつかのルータを経由して、受信者にたどり着く。そのルータ上限数がTTL。ルータを経由することでTTLは減る。
ARP
WANからLANに入ったら識別番号をIPアドレスからMACアドレスに変換する
LAN内のすべてのホストに向かってMACアドレスを使って問い合わせて(ブロードキャスト)、なんらかのホストが応答してきたらIPアドレスをMACアドレスに変換できる。
インターネットから返された応答をLAN内のホストに渡すときは、ルータがARPを使う。LAN内のホスト同士が通信するときは、送信元がARPを使う。
ホストは一度得たMACアドレスとIPアドレスの対応情報を記憶する(ARPテーブル)。あらかじめ設定されている時間が経過すると、動的な対応が削除され、再度ARPによる問い合わせが行われる。
TCP
データの送信者と受信者がお互いに相手の確認を取り合いながら確実にデータを受け渡すためのプロトコル。ハンドシェイクという。
データは、プロトコルを守るためにTCP(ポート番号)、IP(IPアドレス)、イーサネット(MACアドレス)のヘッダ情報が付加されて送信される。
ポート番号
TCPの識別番号。WEBブラウザがプログラムを識別するためのもの。
コマンド
| コマンド | 説明 |
|---|---|
| ifconfig | ネットワークインタフェースの確認を行う |
| ping | 通信相手に応答を要求する。応答が返って来れば、通信相手が動作中であることを確認できる |
| traceroute | 通信経路(どのルーターを通っているか)を調べる |
| nslookup | DNSサーバへの問い合わせができる |
| netstat | 通信状態やポートの使用状況を確認する |
| arp | PC内のARPテーブルを見ることができる |
- 紫:MACアドレス
- 黄:リンクローカル?
- 赤:ローカルIPアドレス
- 青:わたしの名前
- サブネットマスク:255.255.255.0

↑ ping www.kantei.go.jp の実行結果
ttl=250 なので、5台のルータを経由して到達

↑ 負荷分散しているので複数IPアドレスが出てくる。全てAWSのCloudFrontとのこと…

↑ 私のお気に入りコマンド、arp -a。私のホストが誰と通信したかわかる
暗号化
| 手法 | 説明 |
|---|---|
| 共通鍵暗号方式 | 暗号化と復号で同じ値の鍵を使う |
| 公開鍵暗号方式 | 暗号化と復号で異なる鍵を使う。公開鍵で暗号化して秘密鍵で復号できるし、秘密鍵で暗号化して公開鍵で復号もできる |
| ハイブリッド方式 | 通信の最初に、処理が遅い公開鍵暗号方式を使って、共通鍵を暗号化して送る。それ以降は、処理の速い共通鍵を使って、必要なデータを暗号化して送る |
ディジタル署名
本人であることを証明するサインや印鑑に相当するもの。文書の内容に改ざんがないことも証明できる
送信者
- 平文と公開鍵と秘密鍵とハッシュ値の計算方法を用意する
- 平文のハッシュ値を求める
- ハッシュ値を秘密鍵で暗号化する(=ディジタル署名のこと)
受信者
- 平文、公開鍵、ハッシュ値、ハッシュ値の計算方法、ディジタル署名を受け取る
- 平文のハッシュ値を求める
- ディジタル署名を公開鍵で復号する
- ハッシュ値が一致するので、送信者が本人であることと、文書に改ざんがないことを証明できる
認証局
実用的なディジタル署名では、公開鍵を送るのではなく、信頼できる認証局が発行した公開鍵証明書を送る
公開鍵証明書は、送信者の公開鍵を認証局の秘密鍵で暗号化したもの。受信者は認証局の公開鍵を使って公開鍵証明書を復号し、送信者の公開鍵を得る
マークアップ言語
XML
拡張可能なマークアップ言語
- XSL
- MathML
- SMIL
- SVG
- XHTML
マークアップ言語
タグによってデータの意味づけをする行為を「マークアップ」といい、この意味づけのための約束事を取り決めた言語を「マークアップ言語」という
- HTML など
XMLとHTML
HTMLの利用方法は、あくまでWebページを表示するというビジュアルな用途に限定しておく
それとは別にXMLを使って、特定の用途ごとに自由にマークアップ言語を作る
その他知らなかった知識
-
コンパイル
- ソースコード(プログラミング言語のファイル)
- コンパイル
- ネイティブコード(マシン語のファイル)
-
コンポーネントベースプログラミング
コンポーネントを組み合わせてプログラムを完成させる -
オブジェクト指向プログラミング
現実世界の物や生き物をプログラムにモデル化して置き換える
-
Hz
クロック信号の周波数の単位。1秒間に1回のクロック信号を生成することが1Hz
3GHzは1秒間に30億回
- 電源供給
| 接続先 | V |
|---|---|
| Vccピン | +5Vの直流電源 |
| GNDピン | 0Vの直流電源 |
-
低水準言語
コンピュータのハードウェアを直接操作する命令を記述する言語
例)マシン語、アセンブラ -
高水準言語
コンピュータのハードウェアを意識せずに、数式や日常の英語に近い表現で命令を記述できる言語
例)C言語、Python、Javaなど
-
オペコード
命令 -
オペランド
目的語
-
正規化
テーブルを複数に分け、個々のテーブルのリレーションシップを設定して、データベースの構造を整理すること
-
主キー
その値がわかればレコードを特定できるキー。複数のフィールドの値を組み合わせて主キーとすることもできる -
外部キー
他のテーブルの主キー。複数のテーブルを結びつける
-
ホスト
通信機能を持った機器のこと
-
W3C勧告
W3Cが取り決めた、特定のメーカーに依存しない汎用的な仕様 -
DTD
XMLインスタンスの内容が適切な表現であるかどうかを厳しくチェックできる -
XMLスキーマ
データ型や桁数などを厳しくチェックできる -
DOM
XML文書を処理できる
プログラムのモジュール化
-
プロセス指向
プロセス(処理)をモジュールとする(手続き型プログラミング) -
オブジェクト指向
オブジェクト(物)をモジュールとする
おわりに
日々の業務とダイレクトに関係があるわけではないのですが、教養として知っておくべき知識がたくさん記載されており、とても勉強になりました。
次は「ネットワークはなぜつながるのか」とか読めたらなぁと思っています。


