MW211 EXIT

devlog
C言語/fcntl()による排他制御・基礎知識
2013年06月07日
まず、「fcntl()」を排他制御機構として使用する場合の書式は以下のような感じ。
┌──────────────────────────────────────┐
│int fcntl(fd,                                                               │
│          コマンド,                                                         │
│          *ファイルロック構造体);                                           │
└──────────────────────────────────────┘
なお、「fcntl()」はファイル制御関数なのでロック以外にも使い道がいろいろとある。
その場合には「コマンド」の部分が変わってくる。

コマンドの種別は以下の通り(排他制御機構として使用する場合のコマンドとなる)。
┌──────────────────────────────────────┐
│・F_GETLK   …ロック参照                                                    │
│・F_SETLK   …ロック設定断念                                                │
│・F_SETLKW  …ロック設定待機                                                │
└──────────────────────────────────────┘
他がロック中の場合、「F_SETLK」はエラーとしてすぐ断念するが、
「F_SETLKW」はロックが解除されるまで待つという違いがある。

ファイルロック構造体の中身を抽出すると以下のような感じ。
┌──────────────────────────────────────┐
│struct flock{                                                               │
│    short  l_type;    // ロック種類を指定する                               │
│    short  l_whence;  // ロック領域(基準位置)                               │
│    off_t  l_start;   // ロック領域(開始位置)                               │
│    off_t  l_len;     // ロック領域(長さ)                                   │
│    pid_t  l_pid;     // ロック主                                           │
│};                                                                          │
└──────────────────────────────────────┘

「l_type」に指定するロック種類は以下の通り。
┌──────────────────────────────────────┐
│・F_RDLCK  …共有ロック                                                     │
│・F_WLCLK  …排他ロック                                                     │
│・F_UNLCK  …アンロック(非ロック中)                                         │
└──────────────────────────────────────┘

「l_whence」に指定するロック領域の基準位置は以下の通り。
┌──────────────────────────────────────┐
│・SEEK_SET  …ファイル先頭                                                  │
│・SEEK_CUR  …現在位置                                                      │
│・SEEK_END  …ファイル末尾                                                  │
└──────────────────────────────────────┘
これは「fseek()」のやつと同じだ。

「l_start」は「l_whence」で指定した基準位置からの相対位置(もちろん負数も可)。
「l_len」は「l_start」からの長さ。
これらを指定することにより、ファイルの一部分をロック領域として指定できる。

「l_pid」にはロック中のプロセスID(つまりロック主)が設定されるので、
これがある場合にはその人が排他中というわけだ。
分類:C/C++
C言語/fcntl()による排他制御・注意
2013年06月06日
fcntl()による排他制御の場合、同一ファイルであれば
ファイルディスクリプタ(fd)が違っても、「close()」するだけで
全てについてアンロックされてしまう。
┌───────────────────────┐
│              ┌──┐            ┌────┐│
│            ┌┤fd1 │←(ロック)─┤fcntl() ││
│┌────┐│└──┘            └────┘│
││ファイル├┤    ↑(アンロック)              │
│└────┘│┌──┐            ┌────┐│
│            └┤fd2 │←─────┤close() ││
│              └──┘            └────┘│
└───────────────────────┘
上記の場合は、「fd1」を扱っている人がいつのまにか
アンロックされているって状況になりうるみたい。
注意が必要だ。
分類:C/C++
C言語/スリープ「nanosleep()」
2013年06月03日
「nanosleep()」で「0.1秒」スリープをする例。
┌──────────────────────────────────────┐
│struct timespec  req;                                                       │
│req.tv_sec  = 0;                                                            │
│req.tv_nsec = 100 * 1000 * 1000;                                            │
│nanosleep(&req, NULL);                                                      │
└──────────────────────────────────────┘

「timespec」構造体は以下のような内訳で、
ナノ秒は「0~999,999,999」の範囲で指定可能。
┌──────────────────────────────────────┐
│struct timespec {                                                           │
│    time_t   tv_sec;   // 秒                                                │
│    long     tv_nsec;  // ナノ秒                                            │
│};                                                                          │
└──────────────────────────────────────┘

ちなみに、シグナルで一時停止され、再度復帰した場合には
第二引数にスリープしていない残り時間が戻ってくるので
再度実行すれば、全部で想定通りの合計になるらしい。
但し、様々な理由から合計値は若干ずれる模様。
┌──────────────────────────────────────┐
│struct timespec  req,rem;                                                   │
│if (nanosleep(&req, &rem) == -1) {                                          │
│    if (errno == EINTR) {                                                   │
│        nanosleep(&rem, NULL);                                              │
│    } else {                                                                │
│        nanosleep()のエラー;                                                │
│    }                                                                       │
│}                                                                           │
└──────────────────────────────────────┘
なお、より厳密にしたい場合には「clock_nanosleep()」という関数があるらしい。

また、ナノ秒までいらず秒単位でいいのであれば「sleep()」が使える。
┌──────────────────────────────────────┐
│残り秒 = sleep(指定秒);                                                     │
│if (残り秒 > 0) {                                                           │
│    sleep(残り秒);                                                          │
│}                                                                           │
└──────────────────────────────────────┘
みたいな感じになる。
分類:C/C++
C言語/排他処理flock()2
2013年06月02日
open()で取得されるファイルディスクリプタは
同一ファイル名であっても、openした順番に別の連番となる。

flock()にとる排他は、ファイルディスクリプタ単位ではなく
ファイルディスクリプタが対象とするファイル単位で行われる。

むしろ同一ファイルディスクリプタであれば排他対象外となる。

┌──────────────────────────────────────┐
│open(ファイル名,…);                           →ファイルディスクリプタ「1」│
│open(ファイル名,…);                           →ファイルディスクリプタ「2」│
│flock(1, LOCK_EX | LOCK_NB);                                        →OK(0) │
│flock(2, LOCK_EX | LOCK_NB);                                        →NG(-1)│
│flock(1, LOCK_EX | LOCK_NB);                                        →OK(0) │
└──────────────────────────────────────┘
分類:C/C++
C言語/排他処理flock()
2013年06月01日
┌──────────────────────────────────────┐
│flock(fd,                                                                   │
│      LOCK_SH);                                                             │
└──────────────────────────────────────┘
基本的に「flock()」でロックをかけるが、先に他でロックをかけていた場合は
「flock()」の中でずーっと待ち状態となる。
これでは埒があかないので、「LOCK_NB」を付加して、
ロック中の場合はエラーとするようにする。
┌──────────────────────────────────────┐
│if (flock(fd, LOCK_SH | LOCK_NB) == -1) {                                   │
│    if (errno == EWOULDBLOCK) {                                             │
│        他でロック中;                                                       │
│    } else {                                                                │
│        flock()エラー;                                                      │
│    }                                                                       │
│}                                                                           │
└──────────────────────────────────────┘
ロック中で即エラーするのもなんだから、リトライ処理で囲って
若干のスリープ処理なんかを挟み込んで、気にならない程度にリトライを
試みさせてみるのもよい。
┌──────────────────────────────────────┐
│for (i = 0; i <= リトライ回数; i++ ) {                                      │
│    if (flock(fd, LOCK_SH | LOCK_NB) == -1) {                               │
│        if (errno == EWOULDBLOCK) {                                         │
│              スリープ処理;                                                 │
│              continue;                                                     │
│        } else {                                                            │
│            flock()エラー;                                                  │
│            break;                                                          │
│        }                                                                   │
│    }                                                                       │
│    ロック成功フラグ = true;                                                │
│    break;                                                                  │
│}                                                                           │
│if (!ロック成功フラグ) {                                                    │
│    close(fd);                                                              │
│    return;                                                                 │
│}                                                                           │
└──────────────────────────────────────┘
分類:C/C++
C言語/fread()の端数
2013年05月31日
┌──────────────────────────────────────┐
│読込件数 = fread(読込先,                                                    │
│                 一件あたりのサイズ,                                        │
│                 要求件数,                                                  │
│                 fp);                                                       │
└──────────────────────────────────────┘
「fread()」は「一件あたりのサイズ(列)」×「要求件数(行)」のように指定して
面としてデータを取得する関数だ。

じゃ、こんな感じで端数が出た時はどうなるのか?
┌──────────────────────────────────────┐
│■■■■                                                                    │
│■■■■                                                                    │
│■■■                                                                      │
└──────────────────────────────────────┘

ちゃんと端数分も含めて最後まで取得してくれる。
┌──────────────────────────────────────┐
│■■■■                                                                    │
│■■■■  ←読込件数2件                                                     │
│■■■←ここまで取得される                                                  │
└──────────────────────────────────────┘
読込件数は完全に読み込まれた行数分となるので、要求件数と比較すると足りなくなる。

よって、「読込件数<要求件数」でEOF到達が認識できる(厳密には「feof()」と併用)。

他のポイントとしては、端数以降の部分はひっぱってこないということ。
┌──────────────────────────────────────┐
│■■■■                                                                    │
│■■■■                                                                    │
│■■■□←この部分は読込先に上書されない                                    │
└──────────────────────────────────────┘
「列×行」分ひっぱってくれて、余白は0で埋めてくれたりはしないってこと。
ま、足りない行分もひっぱってこないから当然といえば当然。
とにかく事前の初期化は大事なようだ。
分類:C/C++
C言語/ダンプのとり方
2013年05月27日
以下のような感じか。(もっといい方法がありそうだが)
┌──────────────────────────────────────┐
│char  test[4];                                                              │
│memset(test, 0, sizeof(test));                                              │
│printf("test:  %02x %02x %02x %02x", test[0], test[1], test[2], test[3]);   │
└──────────────────────────────────────┘

以下は、実体が3文字のファイルを「fread()」で「2×2」で読み込んで
4文字目の領域はどうなるかを調査した場合の例。
┌──────────────────────────────────────┐
│char  test[4];                                                              │
│memset(test, 0, sizeof(test));                                              │
│FILE*  fp = fopen("test.txt", "r");                                         │
│printf("%d = fread()", fread(test, 2, 2, fp));                              │
│fclose(fp);                                                                 │
│printf("test:  %02x %02x %02x %02x", test[0], test[1], test[2], test[3]);   │
└──────────────────────────────────────┘
分類:C/C++
C言語/fopenの重複
2013年05月26日
ファイルは「fopen()→fclose()」の流れで扱うものだが、
同一ファイルについて「fopen()→fopen()」してしまった場合はどうなるのか?

処理系によって違うらしいので一概にはいえないが、
ある処理系の場合は、異なるファイルポインタ(fp)のアドレスが取得できたので
「fopen()」毎に、FILE構造体が生成されるようだ。
但し、同時にオープンできるファイルの数に上限があるようなので
無尽蔵に「fopen()→fopen()→…」を繰り返すといつかはエラーになるみたい。
分類:C/C++
C言語/static修飾子
2013年05月24日
static修飾子には以下の二つの用途がある。

  (a)静的記憶領域を使用する

  (b)スコープをそのファイルに限定する

ローカル変数のstatic修飾子は(a)、
グローバル変数のstatic修飾子、関数のstatic修飾子は(b)になる。

どちらかといえば代表的なのは(a)の方なので、
(a)の意味で(b)の用例を解釈しようとするとわけがわからなくなる

ちなみに、グローバル変数は静的記憶領域にあるのが前提なので
(a)としての役割は必要ない。よって、(b)としての役割となる。

また、関数は静的記憶領域とか動的記憶領域とかと別の概念(プログラム領域)なので
(a)としての役割は必要ない。よって、(b)としての役割となる。

ってことなのだろうか。

(b)は別の名前にしてくれれば紛らわしくないんだけど、と思ってしまう。
分類:C/C++、注意
C言語/連想配列的定数のようなもの
2013年05月12日
連想配列を実装していないC言語において、
連想配列的定数のような、データベースの定数テーブル的なものを代用する処理。

まず、構造体を決める。列はいくつでもOK。
┌──────────────────────────────────────┐
│typedef struct {                                                            │
│    int    id;                                                              │
│    char*  name;                                                            │
│} DEF;                                                                      │
└──────────────────────────────────────┘

次に値を設定する。グローバル変数(配列)として先頭で定義してしまう感じか。
┌──────────────────────────────────────┐
│DEF gDef[] = {                                                              │
│    { 1,"北海道"},                                                          │
│    { 2,"青森県"},                                                          │
│    { 3,"岩手県"},                                                          │
│};                                                                          │
└──────────────────────────────────────┘

これを「id」指定で取得する関数をつくる。
┌──────────────────────────────────────┐
│DEF*  getDef(int  id) {                                                     │
│    for (int i = 0; i < (sizeof(gDef) / sizeof(gDef[0])); i++) {            │
│        if (gDef[i].id == id) {                                             │
│            return &gDef[i];                                                │
│        }                                                                   │
│    }                                                                       │
│    // 該当なしはNULLを返す                                                 │
│    return NULL;                                                            │
│}                                                                           │
└──────────────────────────────────────┘

ま、こんな感じで必要に応じて、関数をアレンジしつつ実装していくことになる。
switch-case文の雨あられよりは大分ましになるだろう。
分類:C/C++
前へ 1 2 3 4 5 6 7 8 9 次へ