自作webサーバーでPHPを動かしてみる

これまででPOSTのたいおうもできたので、ここからはPHPを実際に動かせるようにします。

取りあえずphp-cgiをインストール

とりあえずは、php-cgiを使って今回は動かしてみようと思います。

apt install php-cgi

僕は今回はphp-cgiを直接インストールしていますが、php-srcから自分でビルドすることもできそうです。

github.com

どんな感じの動きになるか考える

今僕が作っているソースコードはこんな感じの動きになっているかと思います。

  • 親プロセスがリクエストを受け付ける
  • 実際の処理は子プロセスが行う

PHPを動かすときは、さらに別のプロセスで動かすほうが楽そうなので、孫プロセスみたいなものを用意します。

多分こんな感じになると思います。

取りあえず動かすよ

一旦主要部分のソースコードです。

まずはhandle_client

void handle_client(int client_fd)
{
    char buffer[BUFFER_SIZE];
    ssize_t header_size;

    printf("handle_client started, fd=%d, pid=%d\n",
           client_fd, getpid());
    fflush(stdout);

    ssize_t request_size =
        read_http_header(client_fd,
                         buffer,
                         sizeof(buffer),
                         &header_size);

    if (request_size < 0)
    {
        printf("ERROR: read_http_header failed, fd=%d\n",
               client_fd);
        fflush(stdout);
        return;
    }

    printf("REQUEST:\n%.*s\n",
           (int)header_size,
           buffer);
    fflush(stdout);

    HttpRequest req;

    if (parse_http_request(buffer, &req) == PARSE_ERROR)
    {
        int len = snprintf(buffer, sizeof(buffer),
                           "HTTP/1.1 400 Bad Request\r\n"
                           "Content-Type: text/html; charset=utf-8\r\n"
                           "Content-Length: 0\r\n"
                           "Connection: close\r\n"
                           "\r\n");

        write(client_fd, buffer, len);
        return;
    }

    char file_path[512];

    build_file_path(req.path, file_path, sizeof(file_path));

    const char *content_type = get_content_type(file_path);

    if (strcmp(req.method, "GET") == 0)
    {
        handle_get(client_fd, req.path, content_type, file_path);
    }
    else if (strcmp(req.method, "POST") == 0)
    {
        handle_post(client_fd, req, buffer, header_size, request_size, content_type);
    }
    else
    {
        int len = snprintf(buffer, sizeof(buffer),
                           "HTTP/1.1 405 Method Not Allowed\r\n"
                           "Content-Type: text/html; charset=utf-8\r\n"
                           "Content-Length: 0\r\n"
                           "Connection: close\r\n"
                           "\r\n");

        write(client_fd, buffer, len);
    }
}

ここでは

  • リクエストの内容をメモリにコピー
  • リクエストの内容をパース
  • ファイルパスの作成
  • コンテンツタイプの取得

をメインにしていて、その先の処理を

  • handle_get
  • handle_post

に丸投げしています。

次にhandle_get関数です

void handle_get(int client_fd, const char *request_path, const char *content_type, const char *file_path)
{

    FileData file_data = read_file(file_path);

    const char *status;

    if (file_data.data == NULL)
    {
        printf("error: read content\n");
        status = "404 Not Found";
    }
    else
    {
        status = "200 OK";
    }

    send_response(client_fd,
                  status,
                  content_type,
                  file_data.data,
                  file_data.size);

    free(file_data.data);
}

ここでは

  • ファイルの取得
  • レスポンス作成

を行っています。今回はここに、PHPの処理を追加していきます。

まずは、リクエストが静的コンテンツか動的(PHP)コンテンツかを確認する関数を作成します。

const char *get_request_file_type(const char *path)
{
    const char *ext = strrchr(path, '.');

    if (ext == NULL)
        return "text/html";

    if (strcmp(ext, ".php") == 0)
        return "php";

    return "static";
}

一旦こんな感じで。次にhandle_get関数の中に、

  • PHPの場合
  • 静的コンテンツの場合

的な条件を加えていきます。

void handle_get(int client_fd, const char *request_path, const char *content_type, const char *file_path, const char *request_file_type)
{
    const char *status;
    FileData file_data;

    if (strcmp(request_file_type, "static") == 0)
    {
        file_data = read_file(file_path);
    }
    else if (strcmp(request_file_type, "php") == 0)
    {
        // ここにPHPの処理書くよ
    }

    if (file_data.data == NULL)
    {
        printf("error: read content\n");
        status = "404 Not Found";
    }
    else
    {
        status = "200 OK";
    }

    send_response(client_fd,
                  status,
                  content_type,
                  file_data.data,
                  file_data.size);

    free(file_data.data);
}

多分コレで動くはずです。次にCGI呼び出してPHP動かすところまで一気に書いてみました。

void handle_get(int client_fd, const char *request_path, const char *content_type, const char *file_path, const char *request_file_type)
{
    const char *status;
    FileData file_data;

    if (strcmp(request_file_type, "static") == 0)
    {
        file_data = read_file(file_path);
    }
    else if (strcmp(request_file_type, "php") == 0)
    {
        int pipefd[2], errpipefd[2];
        if (pipe(pipefd) == -1 || pipe(errpipefd) == -1)
        {
            perror("pipe");
            return;
        }

        pid_t gpid = fork();
        if (gpid == 0)
        {
            printf("Executing PHP CGI for %s\n", file_path);
            close(pipefd[0]);
            dup2(pipefd[1], STDOUT_FILENO);
            close(pipefd[1]);

            close(errpipefd[0]);
            dup2(errpipefd[1], STDERR_FILENO);
            close(errpipefd[1]);

            setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
            setenv("REQUEST_METHOD", "GET", 1);
            setenv("SCRIPT_FILENAME", file_path, 1);
            setenv("REDIRECT_STATUS", "200", 1);

            execlp("php-cgi", "php-cgi", NULL);
            perror("execlp");
            exit(1);
        }
        else if (gpid > 0)
        {
            printf("Forked process for PHP CGI, PID=%d\n", gpid);
            close(pipefd[1]);
            close(errpipefd[1]);
            char cgi_buf[4096];
            ssize_t n;
            size_t total = 0;
            char *php_output = NULL;

            while ((n = read(pipefd[0], cgi_buf, sizeof(cgi_buf))) > 0)
            {
                php_output = realloc(php_output, total + n);
                memcpy(php_output + total, cgi_buf, n);
                total += n;
            }

            char *cgi_err_buf[4096];
            ssize_t err_n;
            size_t err_total = 0;
            char *cgi_error = NULL;

            while ((err_n = read(errpipefd[0], cgi_err_buf, sizeof(cgi_err_buf))) > 0)
            {
                // メモリ確保し直し
                // (一個前のループまでで読み込んだ量と今回読み込んだ量の合計サイズにする)
                cgi_error = realloc(cgi_error, err_total + err_n);

                // エラー内容を蓄積
                memcpy(cgi_error + err_total, cgi_err_buf, err_n);

                // 総エラーサイズを更新
                err_total += err_n;
            }

            close(pipefd[0]);
            close(errpipefd[0]);

            // exit code取得
            int wstatus = 0;
            waitpid(gpid, &wstatus, 0);

            FILE *debug_log_file = fopen("/tmp/php_cgi_debug.log", "a");

            if (debug_log_file)
            {
                fwrite(php_output, 1, total, debug_log_file);
                fputc('\n', debug_log_file);
                fputs("-------------- values from pipe --------------\n", debug_log_file);
                fwrite(cgi_error, 1, err_total, debug_log_file);
                fputc('\n', debug_log_file);
                fputs("-------------- exit status --------------\n", debug_log_file);
                fprintf(debug_log_file, "Exit code: %d\n", WEXITSTATUS(wstatus));
                fclose(debug_log_file);
            }

            char *body = strstr(php_output, "\r\n\r\n");
            size_t body_offset = body ? (body - php_output) + 4 : 0;

            char *status_line = strstr(php_output, "Status:");

            if (status_line)
            {
                char *status_end = strstr(status_line, LINE_END);

                if (status_end)
                {
                    *status_end = '\0';
                    status_line += strlen("Status: ");

                    printf("Extracted Status Line: %s\n", status_line);
                }
            }

            if (cgi_error && err_total > 0)
            {
                fprintf(stderr, "PHP CGI Error Output:\n%.*s\n", (int)err_total, cgi_error);

                send_response(client_fd,
                              "500 Internal Server Error",
                              "text/plain",
                              cgi_error,
                              err_total);

                free(cgi_error);

                return;
            }

            printf("PHP CGI output received, total size=%zu\n", total);
            send_response(client_fd,
                          status_line ? status_line : "200 OK",
                          "text/html",
                          php_output + body_offset,
                          total - body_offset);

            free(php_output);
            return;
        }
        else
        {
            perror("fork");
            return;
        }
    }

    if (file_data.data == NULL)
    {
        printf("error: read content\n");
        status = "404 Not Found";
    }
    else
    {
        status = "200 OK";
    }

    send_response(client_fd,
                  status,
                  content_type,
                  file_data.data,
                  file_data.size);

    free(file_data.data);
}

実際に動きました!!!とっても嬉しい!!

ちょっと内容雑ですが、今回はこんな感じですかね。

自作webサーバーのコード量が増えたので、ファイルを分けてみる

はじめに

この記事は自作webサーバーでforkという処理をさせてみたので、どう動いているの観察してみたいの続きの内容となっています。

takashiraki.hatenablog.com

また、まだまだ未熟者のため間違った内容を書いていることもあるかと思います。そのような際には暖かくコメントいただけると幸いです。

現状

下記構成になっていて

# tree 
.
├── Dockerfile
├── README.md
├── app
│   ├── http-server
│   └── http-server.c
└── var
    └── www
        └── html
            └── http-server.html

http-server.cはこんな感じです(めっちゃ長いです)

#define _POSIX_C_SOURCE 200809L
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

const int LOOP_INFINITY = 1;
const int HEADER_END_LEN = 4;
const int HEADER_END_OVERLAP = HEADER_END_LEN - 1;

typedef struct
{
    char *data;
    size_t size;
} FileData;

FileData read_file(const char *path);

const char *get_content_type(const char *path);

void build_file_path(const char *request_path, char *full_path, size_t size);

ssize_t read_http_request(int fd, char *buffer, size_t buffer_size);

void handle_client(int client_fd);

void singchld_handler(int sig);

void handle_get(int client_fd, const char *request_path);

void send_response(int client_fd, const char *status, const char *content_type, const void *body, const size_t body_size);

int main(void)
{
    // 待ち受けソケット
    int server_fd;

    // accept後に得られるソケット
    int client_fd;

    // v4アドレスとポートなど
    struct sockaddr_in addr;

    struct sigaction sa;
    sa.sa_handler = singchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;

    if (sigaction(SIGCHLD, &sa, NULL) == -1)
    {
        perror("sigaction failed");
        return 1;
    }

    // AF_INETはIPv4を使うことを示す定数
    // SOCKET_STREAMはTCPを使うことを示す定数
    // 0はプロトコル自動選択
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0)
    {
        perror("socket failed");
        return 1;
    }

    int yes = 1;

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
    {
        perror("setsockopt failed");
        return 1;
    }

    // アドレス設定(ゼロ初期化)
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;         // IPv4を使うことを示す
    addr.sin_port = htons(8080);       // htonsはOSのエンディアンに合わせる関数
    addr.sin_addr.s_addr = INADDR_ANY; // ここですべてのIPを受け付けるらしい

    // バインドするよ
    // ここで8080ポート向けの全てのIPからのリクエストを受け付けるよう紐づける

    // この1行で「server_fdというソケットを、
    // ポート8080・すべてのIPアドレス(INADDR_ANY)で待ち受けるように設定する」
    // ということをOSに伝えています。
    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind failed");
        return 1;
    }

    // ここでリッスンするよ(接続待ち状態にする)
    if (listen(server_fd, 5) < 0)
    {
        perror("listen failed");
        return 1;
    }

    printf("listening on port 8080...\n");

    while (LOOP_INFINITY)
    {
        // ここでリクエストを受け入れる
        client_fd = accept(server_fd, NULL, NULL);

        if (client_fd < 0)
        {
            perror("accept failed");
            return 1;
        }

        printf("Accepted connection, client_fd=%d\n", client_fd);
        printf("Parenting process ID: %d\n", getpid());

        pid_t pid = fork();

        if (pid < 0)
        {
            perror("fork failed");
            close(client_fd);
            continue;
        }

        if (pid == 0)
        {
            printf("Child process ID: %d\n", getpid());
            close(server_fd);

            handle_client(client_fd);

            close(client_fd);
            exit(0);
        }

        // ソケットを閉じる
        close(client_fd);
    }

    close(server_fd);
}

FileData read_file(const char *path)
{
    // ファイルを取得
    FileData result = {NULL, 0};

    FILE *file = fopen(path, "rb");

    if (file == NULL)
        return result;

    // ファイルサイズを取得
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // メモリ確保
    char *data = malloc(file_size);

    if (data == NULL)
    {
        fclose(file);
        return result;
    }

    // ファイルの読み込み
    fread(data, 1, file_size, file);
    fclose(file);

    result.data = data;
    result.size = file_size;

    return result;
}

const char *get_content_type(const char *path)
{
    const char *ext = strrchr(path, '.');

    if (ext == NULL)
    {
        return "text/html";
    }

    if (strcmp(ext, ".html") == 0)
        return "text/html";
    if (strcmp(ext, ".css") == 0)
        return "text/css";
    if (strcmp(ext, ".js") == 0)
        return "application/javascript";
    if (strcmp(ext, ".json") == 0)
        return "application/json";
    if (strcmp(ext, ".png") == 0)
        return "image/png";
    if (strcmp(ext, ".ico") == 0)
        return "image/png";
    if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0)
        return "image/jpeg";

    return "text/plain";
}

void build_file_path(const char *request_path, char *full_path, size_t size)
{
    const char *base = "/var/www/html";

    if (strstr(request_path, "..") != NULL)
    {
        full_path[0] = '\0';
        return;
    }

    if (strcmp(request_path, "/") == 0)
    {
        snprintf(full_path, size, "%s/index.html", base);

        FILE *file = fopen(full_path, "r");

        if (file != NULL)
        {
            fclose(file);
            return;
        }

        snprintf(full_path, size, "%s/http-server.html", base);
        return;
    }

    snprintf(full_path, size, "%s%s", base, request_path);
}

ssize_t read_http_request(int fd, char *buffer, size_t buffer_size)
{
    ssize_t total = 0;
    ssize_t n;

    while (total < (ssize_t)(buffer_size - 1))
    {
        n = read(fd, buffer + total, buffer_size - 1 - total);

        if (n < 0)
        {
            perror("read error");
            return -1;
        }

        if (n == 0)
        {
            break;
        }

        total += n;

        ssize_t start = total - n - HEADER_END_OVERLAP;
        if (start < 0)
        {
            start = 0;
        }

        for (ssize_t i = start; i + HEADER_END_LEN <= total; i++)
        {
            if (buffer[i] == '\r' &&
                buffer[i + 1] == '\n' &&
                buffer[i + 2] == '\r' &&
                buffer[i + 3] == '\n')
            {
                buffer[total] = '\0';
                return total;
            }
        }
    }

    buffer[total] = '\0';
    return total;
}

void handle_client(int client_fd)
{
    char buffer[1024];
    ssize_t request_size = read_http_request(client_fd, buffer, sizeof(buffer));

    if (request_size < 0)
    {
        // 読み込みエラー
        return;
    }

    printf("REQUEST:\n%s\n", buffer);

    // ファイル読み込み
    char request_path[256];

    // メソッド読むよ(GETのみ対応)
    char method[8];
    int len;

    if (sscanf(buffer, "%7s %255s", method, request_path) != 2)
    {
        // 400返す
        printf("error: parse method\n");
        len = snprintf(buffer, sizeof(buffer),
                       "HTTP/1.1 400 Bad Request\r\n"
                       "Content-Type: text/html; charset=utf-8\r\n"
                       "Content-Length: 0\r\n"
                       "Connection: close\r\n"
                       "\r\n");
        write(client_fd, buffer, len);
        return;
        ;
    }

    if (strcmp(method, "GET") == 0)
    {
        handle_get(client_fd, request_path);
    }
    else
    {
        // 405返す
        printf("error: method not allowed\n");
        len = snprintf(buffer, sizeof(buffer),
                       "HTTP/1.1 405 Method Not Allowed\r\n"
                       "Content-Type: text/html; charset=utf-8\r\n"
                       "Content-Length: 0\r\n"
                       "Connection: close\r\n"
                       "\r\n");
        write(client_fd, buffer, len);
        return;
    }
}

void singchld_handler(int sig)
{
    (void)sig;

    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}

void handle_get(int client_fd, const char *request_path)
{
    char file_path[512];
    char buffer[1024];
    build_file_path(request_path, file_path, sizeof(file_path));

    FileData file_data = read_file(file_path);

    int len;
    const char *status;

    if (file_data.data == NULL)
    {
        printf("error: read content\n");
        status = "404 Not Found";
    }
    else
    {
        status = "200 OK";
    }

    const char *content_type = get_content_type(file_path);

    send_response(client_fd, status, content_type, file_data.data, file_data.size);

    free(file_data.data);
}

void send_response(int client_fd, const char *status, const char *content_type, const void *body, const size_t body_size)
{
    // ヘッダー生成
    char header[512];

    if (content_type == NULL)
    {
        content_type = "text/plain";
    }

    int header_len = snprintf(header, sizeof(header),
                              "HTTP/1.1 %s\r\n"
                              "Content-Type: %s\r\n"
                              "Content-Length: %zu\r\n"
                              "Connection: close\r\n"
                              "\r\n",
                              status, content_type, body_size);

    // ボディー生成
    const char *p = body;

    // レスポンス返す
    ssize_t header_written = 0;
    while (header_written < header_len)
    {
        ssize_t written = write(client_fd, header + header_written, header_len - header_written);
        if (written <= 0)
        {
            perror("write failed");
            return;
        }

        header_written += written;
    }

    ssize_t body_written = 0;
    while (body_written < (ssize_t)body_size)
    {
        ssize_t written = write(client_fd, p + body_written, body_size - body_written);
        if (written <= 0)
        {
            perror("write failed");
            return;
        }

        body_written += written;
    }
}

という感じで何だな長いな〜というのと、責務増えたな〜と思ったので試しに分けてみようと思います。

先にどんな感じに分けたのか

今回は一旦こんな感じに分けてみました。

tree
.
├── Dockerfile
├── README.md
├── app
│   ├── http-server
│   ├── http.c
│   ├── http.h
│   ├── http.o
│   ├── main.c
│   └── main.o
└── var
    └── www
        └── html
            └── http-server.html

シンプルにメインプロセスとそれ以外で分けました。(http.cの責務は依然複数のままだけど気にしない...)

ここで.oファイルとか、.hファイルとか増えてきたので、ちょっと色々と整理してみます。

.hファイル

  • 関数
  • オブジェクト
  • データ型

これらを定義しておいて、別のソースファイルから参照できるようにするものらしいです。今回はmain.c

  • FileDataデータ構造
  • handle_client関数

を利用したいので、ヘッダファイルに定義して見ています。実際には下記コードにしてみました。

#ifndef HTTP_H
#define HTTP_H

#include <stddef.h>

typedef struct
{
    char *data;
    size_t size;
} FileData;

void handle_client(int client_fd);

#endif // HTTP_H

.oファイル

オブジェクトファイルは実行や編集はできないけど、大きなプログラムのパーツとなるフアイルというイメージでいます。 C言語でそこそこな規模のソースコードを記載し用とした際に、一つのファイルにまとめてソースコードを書くのは大変かと思います(主に可読性などが思い当たるが、それ以外はあまりまだわかっていない...)。

こうしたときに、細かなパーツをたくさん作っておいて最後にそれらをまとめてしまうほうが効率的ではないでしょうか?

そのような際に.oファイルを作成します。

こんな感じでいくつかのファイルに分けておいて、それを最後にまとめるための中間ファイルってイメージがあります。


今回はこんな感じにして、次はPOST対応したいですね。

参考文献

qiita.com

qiita.com

qiita.com

https://wa3.i-3-i.info/word12026.html

自作webサーバーでforkという処理をさせてみたので、どう動いているの観察してみたい

はじめに

最近個人で暇なときに

  • ネットワークとか
  • CS50とか

そのへんの勉強とか動画を見ていたら、ふとなんでwebサーバーって動くんだろうと気になったので、試しにチャッピーをフル活用して簡易サーバーを作ってます。 作ってみて入るのですが、forkとかそのあたりであまりイメージがわかなかったので、試しにめっちゃリクエスト投げたらどうなるか、メモ程度に残しておきます。

ソースコードは下記リポジトリにあります。

github.com

取り敢えずソースコード紹介

まずはファイル構成

ubuntu/
├── Dockerfile
├── README.md
├── app
│   ├── http-server
│   ├── http-server.c
│   ├── sample
│   ├── sample.c
│   └── server.c
└── var
    └── www
        └── html
            └── http-server.html

今回はhttp-server.cに色々と書いています。全部貼ると長いなーと思ったので、メインプロセスのみソースコード貼ります。

int main(void)
{
    // 待ち受けソケット
    int server_fd;

    // accept後に得られるソケット
    int client_fd;

    // v4アドレスとポートなど
    struct sockaddr_in addr;

    struct sigaction sa;
    sa.sa_handler = singchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;

    if (sigaction(SIGCHLD, &sa, NULL) == -1)
    {
        perror("sigaction failed");
        return 1;
    }

    // AF_INETはIPv4を使うことを示す定数
    // SOCKET_STREAMはTCPを使うことを示す定数
    // 0はプロトコル自動選択
    server_fd = socket(AF_INET, SOCK_STREAM, 0);

    // アドレス設定(ゼロ初期化)
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;         // IPv4を使うことを示す
    addr.sin_port = htons(8080);       // htonsはOSのエンディアンに合わせる関数
    addr.sin_addr.s_addr = INADDR_ANY; // ここですべてのIPを受け付けるらしい

    // バインドするよ
    // ここで8080ポート向けの全てのIPからのリクエストを受け付けるよう紐づける

    // この1行で「server_fdというソケットを、
    // ポート8080・すべてのIPアドレス(INADDR_ANY)で待ち受けるように設定する」
    // ということをOSに伝えています。
    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind failed");
        return 1;
    }

    // ここでリッスンするよ(接続待ち状態にする)
    if (listen(server_fd, 5) < 0)
    {
        perror("listen failed");
        return 1;
    }

    printf("listening on port 8080...\n");

    while (LOOP_INFINITY)
    {
        // ここでリクエストを受け入れる
        client_fd = accept(server_fd, NULL, NULL);

        if (client_fd < 0)
        {
            perror("accept failed");
            return 1;
        }

        printf("Accepted connection, client_fd=%d\n", client_fd);
        printf("Parenting process ID: %d\n", getpid());

        pid_t pid = fork();

        if (pid < 0)
        {
            perror("fork failed");
            close(client_fd);
            continue;
        }

        if (pid == 0)
        {
            printf("Child process ID: %d\n", getpid());
            close(server_fd);

            handle_client(client_fd);

            close(client_fd);
            exit(0);
        }

        // ソケットを閉じる
        close(client_fd);
    }

    close(server_fd);
}

handle_clientの中でリクエスト解析したりコンテンツ取得したり、レスポンス返したりしています。

今回はこんな感じでforkさせるコードを書いたわけです。普段は業務でPHPとかTypeScriptとかしか書いていないので、あまりこのへんの馴染みがないというのが事実です。 それ故、あまりイメージが湧きませんでした。ので、実際にたくさんリクエストを送って観察してみたいと思います。

観察できるように、handle_clientの冒頭にsleepを追加しています。

void handle_client(int client_fd)
{
    sleep(10);
    char buffer[1024];
    ssize_t request_size = read_http_request(client_fd, buffer, sizeof(buffer));

取り敢えずやってみる

取り敢えずやってみようと思います。

今回は下記コマンドを使います。

コマンド 意味
ps 確か稼働中のプロセスを確認するやつ
wc ファイルの行数、ワード数、バイト数をはかるやつ
free メモリ使用量のみ確認するコマンド
pstree プロセスの親子関係見るやつ
watch 指定したコマンドを定期実行したりプロセス監視したり?
ab 手軽なベンチマークツールとして広く利用されているコマンドラインツール

この辺のコマンドを利用して、プロセスがたくさんできる過程を見てみます。

// これでhttp-serverというプロセスの数を監視する
watch -n0.5 "ps -ef | grep http-server | wc -l"

// メモリ監視
watch -n0.5 free -m

// プロセス監視
watch -n0.5 "pstree -p 親プロセス"

同時200リクエストを送信し、合計1000リクエスト送る
ab -n 1000 -c 200 http://127.0.0.1:8080/

親プロセスのプロセスIDはメインプロセスの中で吐くようにしているので、そこから持ってきます。

Accepted connection, client_fd=4 
Parenting process ID: 31424
Child process ID: 31924 

こんな感じで吐いてくれます。実際にtmuxとか使ってターミナルでやると、こんな感じになります。

なんか叩いているコマンドが怪しかったり、改善するしたほうが良さげな部分が色んな所にあるな...

pgrep -c http-server

のほうが良い??

まあ、今日はこんな感じにしておきます。

参考文献

www.ibm.com

packages.debian.org

knowledge.sakura.ad.jp

www.ibm.com

zenn.dev

転職活動が色々と終わったので、振り返りと今後の整理をしてみる振り返りと今後の整理をしてみる

久しぶりにしっかりと記事を書いてみようと思います。色々と整理しながら書くことって結構重要かなと思ったので、ここに色々とまとめていこうと思います。

とりあえず転職活動がひと段落した

色々とスキルアップなどのために転職活動を行っておりまして、ようやくひと段落しました。そこで今後のキャリアとかどこ目指していくかとかちゃんと考えるいいタイミングでもあったので考えつつ、つらつらと書いてみようと思います。

今後何を目指していくか

ざっくりではありますが、頭の中では

  • 更なる技術力の向上
  • マネジメント経験を積むこと
  • CS関係の学習
  • 海外でのキャリアを築くための準備(できるかどうかはおいておいて)

的なところになるのかなと思っています。

更なる技術力の向上

現職では、プロダクトの立ち上げから運用まで幅広くやってきたのですが、『広く浅く全体的にやった』みたいなところがあるので、逆に深い部分があまりないのではないかなと思っています。

もちろん広くできるのはいい側面もありますが、やはり何か一つくらい深くできていた方が良いかなと思ってもいます。一応歴だけでいうとバックエンド(PHP)はインターン含めると4年ほど(社会人としては1年半くらい)でして、その他は

  • フロントエンド:1年ほど(Vue.js/Nuxt.js)
  • インフラ:2年半ほど(AWS

となっており、歴だけでみるとバックエンドは長いですが、まだまだ実力不足を感じているところです。

そこで、全体的にもレベルを上げつつ、何かしら特定の分野で深い知識まで習得できるようになりたいなと考えています。

マネジメント経験を積むこと

これまで色々と与えられてきた立場なので、今度は自分が何かでサポートできるようになりたいと考えています。

CS関係の学習

一応大学・大学院までは工学部だったのですが、情報系ではなかった(授業は一部やったことはあるくらい)ため、全然理解できていないなと感じているのと同時に、必要性をすごい感じています。

基礎となる力をつけていかないといけないなとすごい感じており、せっかくなら元々興味のあったCSちゃんとやってみるか!と思っています(どうやっていくかは現在検討中)

海外でのキャリアを築くための準備(できるかどうかはおいておいて)

できるかできないかはさておき、とにもかくにも情報が足りていないのと、英語力が壊滅的にないのでそこをどうにかしてレベル上げをしていきたいなと思っています。