---
title: ブラウザでbashを動かして理解する「ターミナルの仕組み」
tags: 
author: [murasuke](https://docswell.com/user/4962106)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/4EQYYNM9JP.jpg?width=480
description: ブラウザでbashを動かして理解する「ターミナルの仕組み」 by murasuke
published: May 01, 26
canonical: https://docswell.com/s/4962106/5Y8EWE-2026-05-01-080651
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/4EQYYNM9JP.jpg)

ブラウザでbashを動かして理解す
る「ターミナルの仕組み」
2025/12/17 俺の忘年会2025
#orestudy


# Page. 2

![Page Image](https://bcdn.docswell.com/page/KJ4WWG4R71.jpg)

自己紹介：murasuke
・株式会社 ツールラボ 開発部 所属
PHPで自社Webサービスの開発をやっています
・ボイジャー2号と１号の間にうまれました
・今年、人生で初めてカンファレンスや勉強会に参加
(ちょっとだけ勇気を出して)本当に良かった！！！
・今日も一緒にターミナルに詳しくなりましょう！


# Page. 3

![Page Image](https://bcdn.docswell.com/page/LE1YYD4V7G.jpg)

今年1年のまとめとして、ブラウザでターミナルを動かします！
今年やったこと
● マウスでお絵描きシェルスクリプト
● ターミナル上で動画再生
● (実はJavaScriptで「テトリス」も作ったけど、未発表)


# Page. 4

![Page Image](https://bcdn.docswell.com/page/GEWGGYX3J2.jpg)

今年1年のまとめとして、ブラウザでターミナルを動かします！
ということで
一年の締めくくりとして「ブラウザでターミナルを動かしてみます！」
ターミナルとシェルの間を「つなげる」コードを説明しながら、両者の関係
を理解しましょう
●
「黒い画面＝シェル」なのか？
●
ターミナル / PTY、TTY / シェルって何？
●
入出力の流れをもとに、理解しようと思います


# Page. 5

![Page Image](https://bcdn.docswell.com/page/47ZLLX6GJ3.jpg)

ターミナルとは①
ビデオ端末 (VT100など)：
ディスプレイに文字を表示するようになった端末
・キーを押したらその文字を即時で
表示する仕組みではない
接続先のコンピューターから
エコーバックを受けて、文字を
表示するようになっている
・エスケープシーケンスによる
表示の制御、文字装飾も
できるようになった


# Page. 6

![Page Image](https://bcdn.docswell.com/page/YJ6WW42QJV.jpg)

ターミナルとは②
赤枠：は擬似ターミナル(PTY)と接続しているプロセス
STAT「+(フォワグラウンドプロセス)」ターミナルとつながっているプロセス
青枠：はコンピューターに接続したキーボード入力を待っているプロセス
/sbin/agetty はログインプロンプトを表示し、ユーザーのログインを待ち受けるプロセス


# Page. 7

![Page Image](https://bcdn.docswell.com/page/GJ5MMQ22J4.jpg)

ターミナルとは③
ビデオ端末 (VT100など)：
キーボード、ディスプレイを制御するTTY経由でShellとつながっています
TTYをShell側から見ると
・画面サイズ、カーソルの位置といったプロパティーを持つ「画面」に見える
・「キーボード入力」 ⇒ Shellには「標準入力(ファイル)」からの入力に変換
・「標準出力(ファイル)」への書き込み ⇒ 画面へ出力される
⇒ read/write でアクセス可能なファイル風IOを持った「入出力装置 」である


# Page. 8

![Page Image](https://bcdn.docswell.com/page/LE3WWVK1E5.jpg)

疑似端末 PTY (Pseudo Terminal) とは？
物理的な端末がない環境で、端末のように振る舞うプロセス間通信チャネルのこと
・相手が物理端末では無いため「ドライバ」として
の機能は持たない
・masterはターミナルエミュレーターと接続して
入出力を行う機能を持つ擬似ファイル
・slave は「端末のように振る舞う」特別なファイ
ル(ShellからはTTYに「見える」)
・Ctrl＋C、Ctrl＋Zのような割り込み(ジョブ管理)は管理者権限のないプロセスから
はできないので、PTY(カーネル側)で行われる


# Page. 9

![Page Image](https://bcdn.docswell.com/page/8EDKK83K7G.jpg)

プログラムの全体像
・ブラウザーでターミナルを表示
・Shellの起動
・WebSocketサーバー
のソースを順に説明します


# Page. 10

![Page Image](https://bcdn.docswell.com/page/V7PKK843J8.jpg)

１. ブラウザにターミナルを表示(xterm.js)
xterm.js と WebSocket を使ってターミナルを実装します
xterm.js ：ターミナルの機能を持つjsライブラリ
・キー入力
・画面表示
・エスケープシーケンスを理解して、
文字色や背景色、カーソル制御を行う
・画面サイズの変更通知
WebSocket ：サーバーとの通信（特にPush通知の受信）のために利用
・WebSocketでサーバーへ接続
・キー入力イベントをフックして、WebSocket経由で送信
・WebSocketから通知イベントを受け取り、xterm.jsで表示


# Page. 11

![Page Image](https://bcdn.docswell.com/page/2JVVVNXNJQ.jpg)

１. ブラウザにターミナルを表示(xterm.js)
&lt;body&gt;
&lt;div id=&quot;terminal&quot; &gt;&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/xterm/lib/xterm.js&quot; &gt;&lt;/script&gt;
&lt;script type=&quot;module&quot; &gt;
// ターミナル初期化 (@xterm/xterm)
const term = new Terminal ();
// ターミナルを表示
term.open(document .getElementById (&#039;terminal&#039; ));
// WebSocket 接続
const ws = new WebSocket (`ws://localhost:8000/` );
ws.binaryType = &#039;arraybuffer&#039; ;
// ブラウザ(terminal) のキー入力をサーバへ送信
term.onData((data) =&gt; ws.send(data));
// サーバー(PTY)の出力をブラウザ (terminal) に表示
ws.addEventListener (&#039;message&#039; , (ev) =&gt; term.write(ev.data));
&lt;/script&gt;
&lt;/body&gt;
かなり端折ってますが
本質的にはこれだけです
・ターミナル作る
・WSサーバーへ接続
・キー入力をWSへ転送
・WS受信をターミナルへ表
示


# Page. 12

![Page Image](https://bcdn.docswell.com/page/5EGLLKV5JL.jpg)

２. PTYの作成と、子プロセス(Shell)の紐づけ
WebSocketサーバー(となるプロセス)が
①PTY作って(openpty())masterとslaveの
ファイルディスクリプタを取得
②子プロセスを作成して(fork())
(ファイルディスクリプタを継承）
③継承したファイルを標準入出力と
紐づけて(dup2())
④子プロセスイメージをbashに置き換え(exec())
上記を行うことで、親のmasterと、子の
slave(標準入出力)のin/outがそれぞれつ
ながり、会話ができるようになります


# Page. 13

![Page Image](https://bcdn.docswell.com/page/4JQYYN6L7P.jpg)

２. PTYの作成と、子プロセス(Shell)の紐づけ
int spawn_shell (int *master_fd , int *slave_fd ) {
// pty を作成
openpty (master_fd , slave_fd , NULL, NULL, NULL);
・PTYを作成
・子プロセスを生成
pid_t pid = fork(); // 子プロセスを生成
if (pid == 0) {
setsid(); // 子プロセスをセッションリーダーにする (親プロセスから切り離す )
ioctl(*slave_fd , TIOCSCTTY , 0); // 制御端末を slave_fd(=/dev/pts/N) に設
定
// slave 側を標準入出力に接続
dup2(*slave_fd , STDIN_FILENO );
dup2(*slave_fd , STDOUT_FILENO );
dup2(*slave_fd , STDERR_FILENO );
・slaveを標準入出力として
利用できるように接続
// 不要なファイルディスクリプタを閉じる
close(*master_fd ); close(*slave_fd );
// 現在のプロセスを bashに置き換える (オープン済みファイルディスクリプタは引き継
ぐ)
const char *shell = &quot;/bin/bash&quot; ;
char *args[] = {( char *)shell, NULL};
execvp(shell, args);
}
// 親プロセス
close(*slave_fd ); // 親はslaveを閉じる (不要なため )
return pid;
}
・プロセスを起動
※PTY経由で親プロセスと
キー入力、ディスプレイ出力
が可能になります


# Page. 14

![Page Image](https://bcdn.docswell.com/page/K74WWG45E1.jpg)

３. WebSocketサーバー
・接続受信時に、PTYとシェルを生成してから通信を開始
・以後はデータのパイプ役として互いのデータを転送する


# Page. 15

![Page Image](https://bcdn.docswell.com/page/LJ1YYD42EG.jpg)

３. WebSocketサーバー
int main(int argc, char **argv) {
// WebSocket サーバーの作成
WebSocketServer server(8000);
server.setOnConnectionCallback((ws) =&gt; { // 接続コールバックの設定
// 子プロセスを生成してシェルを起動
spawn_shell(&amp;master_fd, &amp;slave_fd);
// リーダースレッド: PTY master(bashの標準出力)を読み取り、WebSocketに転送
thread reader_thread = thread(() =&gt; {
vector&lt;char&gt; buf(4096);
// PTY master からの読み取りループ
while (running) {
ssize_t r = read(master_fd, buf.data(), 4096);
// WebSocket 経由で画面(xterm.js)へ送信
string out(buf.data(), (size_t)r);
ws-&gt;sendBinary(out);
}
}
}
//xterm.jsからのメッセージを PTY masterに書き込むコールバック
ws-&gt;setOnMessageCallback((msg) =&gt; {
// テキストメッセージ
const string &amp;s = msg-&gt;str;
// 通常の入力データとして PTY master に書き込む
write(master_fd, s.data(), s.size());
});
}
長いので疑似コードです
・接続を受け付けたらPTYと子プ
ロセスを生成
・シェルからのデータ受信用ス
レッドを生成し、無限ループで受
信して画面へ転送
・画面からの入力を受信してシェ
ルへ転送


# Page. 16

![Page Image](https://bcdn.docswell.com/page/GJWGGYX272.jpg)

デモ／結果イメージ
時間があれば実際に
・サーバーを起動
・ブラウザでターミナルを表
示
・テトリス.jsを実行
してみます


# Page. 17

![Page Image](https://bcdn.docswell.com/page/4EZLLX6473.jpg)

まとめ
●
●
●
●
●
●
ターミナルとシェルは個別に存在するプロセス
PTYがプロセス間のデータ入出力の仲介をしている
xterm.jsを使えば、ブラウザにも簡単に組み込める(VSCodeも使ってる)
Windowsでしか低レベルプログラミングをしたことが無かったので、
fork()やexec()の仕組みに驚いた
十数年ぶりにC++に触れたら、新しい構文や機能が増えてびっくりした
今日を持って、ターミナル(芸人)から卒業します！
ターミナルのことは嫌いにならないでください！
（来年は何か別のことをやりたい）
ご清聴ありがとうございました


