Rustメモ
What is Rust
メモ
変数宣言パターン
OKな例
// 不変の変数 let _str = String::from("hello");
// 可変の変数(mutを指定してミュータブルにする) let mut _str = String::from("hello");
// 不変の変数に対する更新NG参照を不変の変数に格納 let _str = String::from("hello"); let _refer = &str;
// 可変の変数に対する更新OK参照を不変の変数に格納 let mut _str = String::from("hello"); let _ref = &mut str;
// 不変の変数に対する更新NG参照を可変の変数に格納 let _str = String::from("hello"); let mut _refer = &str;
// 可変の変数に対する更新NG参照を不変の変数に格納 let mut _str = String::from("hello"); let _refer = &str;
NGな例
// 不変の変数に対する更新OK参照 let _str = String::from("hello"); let _refer = &mut str;
標準入力から文字列を受け取るやり方
use std::io; は前提として
let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let vec: Vec<u32> = input .split_whitespace() .map(|n| u32::from_str_radix(n, 10).unwrap()) .collect();
C言語メモ
What is C
メモ
#includeの挙動
#includeの行がヘッダファイルの内容へ置き換えられる
#ifndef ~ #endif
当該ヘッダファイルが複数回includeされてしまうのを防ぐために使われる。 以下が例。
#ifndef _HOGE_H_ #define _HOGE_H_ (インクルードしたい内容) #endif
#define で_HOGE_H_が宣言されていなかったら#ifndef ~ #endif間が実行される。 _HOGE_H_をinclude実行のフラグとして使っている感じ。
stdint
固定サイズの整数型を提供するライブラリ。
int32_t int64_t
みたいな長さ決め打ちの整数型を利用できる。 最近はあまり意識しなくてもよいはずだが、intは環境によって16ビットだったり32ビットだったり64ビットだったりする場合があるため明示的に指定できるようにこのライブラリが用意されている。
_tってそういえば何
typedefで作った型の名前に慣習的につけるもの
0をポインタ型にキャストすると
null ポインタになる
ポインタ型をifの条件節に渡すと
- null ポインタの場合false
- それ以外はtrue として判断される
getopt
optionをgetするということなので、コマンドに追加で引っ付けられるオプションを解析するもの。
実際使うときはこんな感じ(上記wikiより引用)
#include <getopt.h> int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
getoptはoptstringで指定したコロンの数によってオプションの性質を決められる。
オプション文字の直後に
- コロンが無い→引数無し
- コロンが1つある→引数アリだがなくても良い。(つまりデフォルト値が存在する)
- コロンが2つある→引数必須
という感じ。
例えば
abc:d::
となっている場合、 a,b,c,dの4つのオプションがあり、a,bは引数無し、cは引数ありだがなくても良い、dは引数必須、ということが分かる。https://en.wikipedia.org/wiki/Getopt
sigset_tの初期化パターン
sigset_tは内部的にはunsigned long int の配列でシグナルを保持できる。 sigset_tの初期化パターンは2通り覚えておく
sigfillset後に絞り込みパターン
全てのシグナルをsigset_tに突っ込んで捕捉し、その後デフォルト動作をさせたいものだけ適宜除外してから利用する方式。 割り込み絶対許さないマンになりたいときなどはこっちを使えば楽。
sigemptyset後に追加パターン
取り敢えずシグナルは無しの状態で初期化し、シグナルを適宜後から追加して補足する 特定のシグナルを受け取った時だけ動作を変えたいパターンの時はこっちが有効。
C言語でハッシュテーブル
<search.h>を使えば実現できる
hserach_data型の構造体変数を用意し、hcreateやhcreate_rで初期化すればhashを扱う準備が整う。 _rがついている方は複数のハッシュテーブルを利用することができるので、そちらを使う方が都合が良いことが多い。 hashテーブルをホワイトボード的に使う場合など単一の方が都合が良いときはr無しの方を使えば良さそう。
hsearchに渡すのはENTRY型じゃなく文字列ではだめ?
searchという名前がついているのに検索する際にはchar型配列ではなくENTRY型変数(keyとそれに対応するdataを持つ構造体)を利用するのは少し違和感がある。key文字列だけでもよいのではないかと思っちゃう。
実際のところ、この関数はsearchだけかinsertもやるのかを選択できる。第二引数において、FINDを指定した場合はsearch, ENTERを指定した場合はsearch + insertという挙動※になる。 insert処理を実行することを念頭にプログラムを組むならENTRY型でkey-valueをセットで渡す必要があるため、そこまでカバーすることを考えてENTRY型になっている。
※serachが成功したならENTER指定だろうとinsertは実行されないことに注意する。POSIXで決まっている。
timeval構造体
select などの一定時間監視するタイプの関数に引数の型として指定されていることが多い。
long tv_sec 時間の整数部。
long tv_usec 時間の小数部。マイクロ秒単位。
ミリ秒単位で利用するには
表現したいミリ秒の整数値を用意し、その整数を
- 1000で割ったものをtv_sec
- 1000で割った余りを1000倍したものをtv_usec
に代入すればよい。 1000で割った余りは必ず3桁以下になり、それを1000倍すると必ず高々6桁の整数になるので、ミリ秒をマイクロ秒に変換したものを表現できる。
typedefの考え方
「普通に変数を宣言するつもりでコードを書き、頭にtypedefをつければ変数の部分を型名として新たな方を定義できる」と考えるとわかりやすい
FD_ISSET(0, &rfds)とselect
FD_SETを実行後にFD_ISSETで確認を行うと、基本的にはtrueが返ってくる。 ただし、selectを実行してrfdsに含まれる識別子が示すファイルを監視中に該当ファイルに対して接続が発生しなかった場合はfalseとなる。
このため、listenソケットやacceptソケットを生成してファイルディスクリプタがそれぞれのソケットに割り振られている状態だったとしても、接続が発生していなければFD_ISSETはfalseを返す。 確認用プログラムが以下。大分部を上記リンクのサンプルプログラムから流用。
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main(void) { fd_set rfds; struct timeval tv; int retval; /* stdin (fd 0) を監視し、入力があった場合に表示する。*/ FD_ZERO(&rfds); FD_SET(0, &rfds); /* 5 秒間監視する。*/ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(1, &rfds, NULL, NULL, &tv); /* この時点での tv の値を信頼してはならない。*/ if (retval == -1) { perror("select()"); } else if (retval) { printf("今、データが取得できました。\n"); printf("select後かつデータ取得成功時のFD_ISSET: %d\n", FD_ISSET(0, &rfds)); /* FD_ISSET(0, &rfds) が true になる。*/ } else { printf("5 秒以内にデータが入力されませんでした。\n"); printf("select後かつデータ取得失敗時のFD_ISSET: %d\n", FD_ISSET(0, &rfds)); } exit(EXIT_SUCCESS); }
出力
失敗時
select前のFD_ISSET: 1 5 秒以内にデータが入力されませんでした。 select後かつデータ取得失敗時のFD_ISSET: 0
成功時
select前のFD_ISSET: 1 a 今、データが取得できました。 select後かつデータ取得失敗時のFD_ISSET: 1
Apache Module メモ
※この記事は2.x系前提。概ね2.4。
What is Apache Module
可変長リストやkey-valueのハッシュテーブルなど、C言語らしからぬリッチな機能が一通りそろっておりC言語と言えど効率的に開発できる。 Apacheモジュール開発に許された特権。 メモリプールをがばっと確保して解放するということをApache側が勝手にやってくれるのでメモリ管理もかなり楽。 そしてなによりCなので高速に動作する。
メモ
おおざっぱに分かっておくとよい事
httpd起動時に準備をしている
主に認識しておくとよいのは以下。
- confの読み込み
- 子プロセスのfork, 設定
- リクエスト処理ループに入る
リクエスト処理ループ
httpリクエストを受け取ったら入る処理フロー。この処理フローのどこかに到達したらモジュールが動作するように設定できる。
Apacheはモジュールの集合体である
これは忘れがちなので覚えておく
ディレクティブはモジュールの設定である
Apacheはモジュールの集合体であることを踏まえると、confはApacheに対する設定ではなくディレクティブに紐づく各モジュールに対する設定と認識する。
お手軽な作り方
CentOS系を想定
必要なrpmを入れる
yum install httpd httpd-devel
testモジュールを生成する
apxs -g -n test
MakeFile, modules.mk, mod_test.cの3つが生成される。
コンパイルする
apxs -c -i -a mod_test.c
基本的には動的リンクを用いた動的組み込みをしたほうがよいため、loadmoduleディレクティブを追加するべく -a オプションをつけてコンパイルしている。 既にLoadModuleを追加している場合は重複が発生してエラーになるようなので注意。
httpd.confにLocationを追加する
<Location "/hoge/"> SetHandler test </Location>
リクエストしてみる
curl "http://localhost/hoge/"
The sample page from mod_test.cと帰ってきたら成功。
自動生成したmod_test.cのコメントに書かれているとおりlynxを使っても良い
lynx -mime_header http://localhost/hoge/
mod_○○.cのざっくり全体構成
ヘッダインクルード 設定情報を保持する変数を作成する関数(ここで初期化) 設定情報を更新する関数(コマンドの配列とセットで定義) コマンドの配列(ディレクティブで定義している設定情報を読み込むための関数や、ディレクティブの構造を指定) フックに登録したい関数たち(フックをかけるタイミングがわかるように名前を定義するとよい) フックに実際に登録する関数 ↑で定義してきたものをディスパッチするための配列
ログ周り
ログフォーマット
カスタムログ
良くやるのが以下(上記リンクより引用)
LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common
LogFormatを決めてnicknameを名付ける(ここではcommon)。 CostomLogで既に定義したLogFormatを読み込み、指定したディレクトリにログファイルを生成する。 commonのところにLogFormatが展開されている感じ。
本格的に使っていくなら例えば以下のようにしてログローテートさせる。
CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common env=common_env SetEnvIf Request_URI "^/hoge$" common_env
env=common_envのところは、CutomLogを用いてログ収集をするのかを決めるための判断軸。 右辺に指定した環境変数が存在する場合にログを落とす。 その環境変数はSetEnvIfを使って指定している。この場合、/hogeに完全一致するURIでリクエストが来ていたらcommon_env環境変数をセットするようにしてある。これによりどのリクエストのログを落とすのかの分岐が可能。
rotatelog
以下を参照。-c -L -l あたりは良く使うはず。 httpd.apache.org
参考になるモジュール
コメントもしっかり書いてあるので勉強になる。 github.com
MySQLメモ
What is MySQL
メモ
ダミーデータを突っ込みたいとき
超大量にあるならLOAD DATA LOCAL INFILE がオススメ。バルクインサートよりも圧倒的に早い。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.6 LOAD DATA INFILE 構文
CSVの場合以下。一番シンプルなので小難しく考えずに使える。このコマンドの場合突っ込むテーブルのカラムを全て順番通りに指定してあげる必要がある。
LOAD DATA LOCAL INFILE (CSVのファイルパス) INTO TABLE (格納したいテーブルの名前) FIELDS TERMINATED BY ','
カラムを明示的に指定したい場合はCOLUMNSオプションを利用する。詳細は公式ページを参照するとよい。
なぜか開発環境のディスク使用量がひっ迫してきたとき
ダンプファイルからのrestore実行時などにバイナリログがもりもり生成されている可能性がある。
ログ確認
SHOW MASTER LOGS;
削除
本番環境などでやらないように
PURGE MASTER LOGS before now();
恒久的にはdisable-log-binをmy.cnfに追記してあげてバイナリログ自体を出力しないようにする。
Redisメモ
What is Redis
メモ
単一のRedisインスタンスに保存できるKey数の最大値
理論的には2^32まで行ける。もちろん利用者の環境制約に縛られる
Redis can handle up to 2^32 keys, and was tested in practice to handle at least 250 million keys per instance.
Every hash, list, set, and sorted set, can hold 2^32 elements.
In other words your limit is likely the available memory in your system.
MGETなどm系コマンドはクラスタ構成では利用できない
Redisをクラスタ構成にした場合に、複数のノードにまたがって存在するKeyに対してMGETは利用できない。
一つのコマンドは一つのサーバで走るため、複数サーバに情報が分散するクラスタ構成では使えない道理。
使えるようにするにはひとつのサーバに情報を寄せるといった対応が必要。
MGETの代わりにGETのパイプライン処理を使うのも手。ただしクラスタ構成でパイプライン処理を使った場合は、送ったコマンドのkeyの並びと帰ってくるvalueの並びは必ずしも順序が一致しないため注意すること。
RedisのCPUボトルネック
シングルスレッドで動作するためCPU全体の使用率ではなく特定コアの使用率がボトルネックになりやすいという認識を持っておくと取り敢えず良い。
以下の記事がとても分かりやすい。Redisモジュール利用やLuaスクリプティングができるようになるとカスタマイズの余地が大幅に上がる。
※ただし、なんでもかんでもRedisモジュール/Luaの独自カスタマイズに頼らないこと。ほかの手段がないか検討すべきということは常に頭に入れておく。
Redisモジュール
少しヘビーなので別記事にする。