fork (2) に関するあれこれ

こちらは「C 言語コンプレックスを払拭したいシリーズ」の派生記事である。 fork (2) の簡単なサンプル Linux では fork (2) を使ってプロセスを複製できる。fork() のプロトタイプは次の通りだ。 #include <unistd.h> pid_t fork(void); このシステムコールを実行すると、プロセスの情報が複製される。システムコールをコールしたプロセスは親と呼ばれ、複製されたプロセスは子と呼ばれる。fork() の返り値は親プロセスの場合は子プロセスの ID であり、子プロセスの場合は 0 である。 fork() を使うもっとも簡単なサンプルは次のようなものだろう。 #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h> int main() { pid_t pid; pid = fork(); if (pid < 0) { fprintf(stderr, "fork(2) failed\n"); exit(1); } if (pid == 0) { printf("Hey, I'm a child process\n"); } else { int status; waitpid(pid, &status, 0); // 親は子を待つ必要がある printf("I spawned PID=%d, and it's done! [Read More]

「ふつうの Linux プログラミング 第2版」再入門 - 自作 cat コマンド (タブと改行を変換するバージョン)

引き続き「ふつうの Linux プログラミング 第2版」を読んでいく。6章の練習問題に次のようなものがある。 タブ文字 ('\t') を 「\t」という2文字、改行を「'$' + 改行」の2文字として置き換えながら出力する cat コマンドを書きなさい。 次のようなコードを書いてみた。 // P.132 の練習問題 #include <stdio.h>#include <stdlib.h> #define BUFFER_SIZE 2048 static void do_cat(FILE *f); int main(int argc, char *argv[]) { if (argc == 1) { do_cat(stdin); } else { for (int i = 1; i < argc; i++) { FILE *f; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); } do_cat(f); fclose(f); } } exit(0); } static void do_cat(FILE *f) { int c; while ((c = fgetc(f)) ! [Read More]

「ふつうの Linux プログラミング 第2版」再入門 - 自作 cat コマンド

何日か前の「決意表明」の後、あらためて「ふつうの Linux プログラミング 第2版」を読み直すことにした。C 言語に対するコンプレックスを払拭するのだ。 過去にも書いたことがあるけど「ふつうの Linux プログラミング」は大変な名著である。僕は第1版も第2版も購入している (そして、どちらも途中で脱落した…)。今回は練習問題をすべてこなして、最後まで読み切ろうと思う。 最初の練習問題は「cat コマンドの再実装」だ。write(2) と read(2) が紹介された章の問題なので、僕のコードも無駄にシステムコールを使っている。 全体のコードは次の通りだ。 // 本章で作った cat コマンドを改造して、コマンドライン引数でファイル名が渡されなかったら標準入力を読むようにしなさい。 #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h> static void do_cat(int fd, const char *s); static void die(const char *s); int main(int argc, char *argv[]) { if (argc == 1) { do_cat(STDIN_FILENO, "STDIN"); } else { for (int i = 1; i < argc; i++) { int fd; fd = open(argv[i], O_RDONLY); do_cat(fd, argv[i]); } } exit(0); } #define BUFFER_SIZE 2048 static void do_cat(int fd, const char *path) { unsigned char buf[BUFFER_SIZE]; if (fd < 0) die(path); for (;;) { int n = read(fd, buf, sizeof buf); if (n < 0) die(path); if (n == 0) break; if (write(STDOUT_FILENO, buf, n) < 0) die(path); } if (close(fd) < 0) die(path); } static void die(const char *s) { perror(s); exit(1); } 長いので関数ごとに見ていこう。 [Read More]

C 言語に対する畏敬の念、または C 言語コンプレックス

職業プログラマーを 15 年以上しているけれど、仕事で C 言語を書いたことがなく、C 言語に対して苦手意識を持っている。C 言語は高専および大学で習ったし、それらの教育機関で与えられた課題には C 言語で回答したものもあった。しかし、仕事ではスクリプト言語や JVM 言語 (Java や Kotlin) ばかり触れてきており、ついぞ C 言語を書かずにここまで来てしまった。

これまで、職業プログラマとしてはバックエンドのプログラムを書く他に、ミドルウェアの設定をすることも多かった。この記事の執筆時点で広く使われているミドルウェアの多くは C 言語もしくは C++ で書かれており、同僚の中にはミドルウェアの実装を深堀りして拡張したり、場合によっては開発者にパッチを投げてバグを改修する人もいた。僕は C 言語に対する苦手意識から、ミドルウェアの不具合を見つけても、ワークアラウンドを考えたり実装したりする程度で済ませてきた。こうして C 言語に対するコンプレックスをこじらせてきたのだ。

このコンプレックスを払拭したいと思う。

今後、いくつか C 言語の入門書を読みながら、しばらくブログに学習内容をメモ書きしていこうと思う。

このブログ記事はその決意表明である。