はじめに
こんにちは、インフラエンジニアです。
最近私はふと思いました。
「OSのコマンドや開発言語の関数一つとっても、コンピューターにとっては複数の処理命令を一塊にしたタスクの集合体なわけだよな」と。
普段の作業でコマンドを叩いたり、コードを実行したりしていても、コンピュータ上でどんな処理が行われているのか、細かく意識をすることは少ないし、淡々と作業を進めてしまう場面って多いのではないでしょうか。
※アーキテクチャのコンセプトがそうだからっていうのもある
インフラエンジニアってシステムが動いている一番身近(物理的に)で仕事してるのに、そういう感覚ってなんだか忘れがちですよね。
そんな感じのきっかけで、Linuxのcatコマンドを例にとってコンピュータの処理をまとめてみました。
catコマンドの模倣
C言語でcatコマンドの動作を模倣したコードを作成。
処理としては以下のような感じ
以下コード
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
FILE* fp;
int c;
// 引数が適切に指定されているか確認
if (argc != 2) {
fprintf(stderr, "Usage: %s <file_path>\n", argv[0]);
return 1;
}
// ファイルパスを引数から取得
char* inputPath = argv[1];
// ファイルを開く
fp = fopen(inputPath, "r");
if (fp == NULL) {
fprintf(stderr, "Error: ファイルが存在しないか、開けませんでした。\n");
return 1;
}
printf("ファイル内容:\n");
// ファイル内容を1文字ずつ読み取って出力
while ((c = fgetc(fp)) != EOF) {
putchar(c); // 標準出力に表示
}
// ファイルを閉じる
if (fclose(fp) != 0) {
fprintf(stderr, "Error: ファイルを閉じることができませんでした。\n");
return 1;
}
printf("\nファイルを正常に閉じました。\n");
return 0;
}
ソースコードの動作確認
cat.cというファイルを作成し、Rocky Linux 上でコンパイル。
アセンブリに変換
処理の流れがより細かく見えるように、以下のサイトでコードをアセンブリに変換します。
https://gcc.godbolt.org/
以下main処理の出力結果
main:
push rbp
mov rbp, rsp
sub rsp, 48
mov DWORD PTR [rbp-36], edi
mov QWORD PTR [rbp-48], rsi
cmp DWORD PTR [rbp-36], 2
je .L2
mov rax, QWORD PTR [rbp-48]
mov rdx, QWORD PTR [rax]
mov rax, QWORD PTR stderr[rip]
mov esi, OFFSET FLAT:.LC0
mov rdi, rax
mov eax, 0
call fprintf
mov eax, 1
jmp .L3
.L2:
mov rax, QWORD PTR [rbp-48]
mov rax, QWORD PTR [rax+8]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov esi, OFFSET FLAT:.LC1
mov rdi, rax
call fopen
mov QWORD PTR [rbp-16], rax
cmp QWORD PTR [rbp-16], 0
jne .L4
mov rax, QWORD PTR stderr[rip]
mov rcx, rax
mov edx, 71
mov esi, 1
mov edi, OFFSET FLAT:.LC2
call fwrite
mov eax, 1
jmp .L3
.L4:
mov edi, OFFSET FLAT:.LC3
call puts
jmp .L5
.L6:
mov eax, DWORD PTR [rbp-20]
mov edi, eax
call putchar
.L5:
mov rax, QWORD PTR [rbp-16]
mov rdi, rax
call fgetc
mov DWORD PTR [rbp-20], eax
cmp DWORD PTR [rbp-20], -1
jne .L6
mov rax, QWORD PTR [rbp-16]
mov rdi, rax
call fclose
test eax, eax
je .L7
mov rax, QWORD PTR stderr[rip]
mov rcx, rax
mov edx, 68
mov esi, 1
mov edi, OFFSET FLAT:.LC4
call fwrite
mov eax, 1
jmp .L3
.L7:
mov edi, OFFSET FLAT:.LC5
call puts
mov eax, 0
.L3:
leave
ret
アセンブリは1行につきコンピュータに命令する処理が1つ、という感じで書かれるのでコンピューティングの過程が分かりやすいですね。
そんなこんなで以下のような処理が行われていることが確認できます。
結論
普段なんにも考えずに白目向きながら使っているコマンドも、中身ではどのような処理をコンピュータにさせようとしているのか、分解してみるとおもしろいですね。
正直分解された処理の流れが見えるようになったからって何なんだっていう話なのですが、コンピュータがどのように世界を捉えていて、どのようにプログラムを動かそうとしているのか、さらにはそれらに加えてハードウェアやファイルシステムなどの要素も含めた横断的な視点もつことで、スムーズなシェルコードの作り込みや、障害時の被疑箇所の絞り込みなどに役立てられるのかなと感じました。
以上、ありがとうございました!
初めての投稿なもので、変なところがあったらコメント等で教えて頂けるとありがたいです!
PS
lsコマンドの動作を模倣したコードを作っている方のブログを見つけました。
すごい。
https://www.mm2d.net/main/prog/linux/ls-01.html