root/WordTreeBuilderDaemon.cpp

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. corpus_scan
  2. main

   1 /* -*- coding: utf-8; mode: c++; -*-
   2  * Concordance to A. S. Pushkin's Works - Building Word Trees
   3  * - Corpus ファイルを読む
   4  * - 位置情報と作品テクストを分離する
   5  * - テクストから単語を切り出し,Lemmatizer により見出語に変換する
   6  * - 単語二分木ノードに単語,出現回数,位置情報リストを登録し,共有メモリ二分木を構築する
   7  * - 単語二分木は,Lemmatized,Non Lemmatized の 2 種類を構築する
   8  * - 単語二分木アドレスハンドルを表示する
   9  * - Web 環境からアクセスする場合は,Web server user 権限で実行すること
  10  * $Id: WordTreeBuilderDaemon.cpp 50 2014-06-09 07:18:37Z isao $
  11  * Copyright (C) 2012, isao yasuda
  12  */
  13 
  14 #include <turglem/lemmatizer.hpp>
  15 #include <turglem/russian/charset_adapters.hpp>
  16 #include <boost/tokenizer.hpp>
  17 #include <boost/locale.hpp>
  18 #include <boost/timer.hpp>
  19 #include "SharedMemoryCommon.hpp"
  20 #include "SharedMemoryTree.hpp"
  21 #include "RussianLemmatizer.hpp"
  22 #include "DeclareFunctions.hpp"
  23 #include "ConcordanceConfig.hpp"
  24 
  25 using namespace boost::interprocess;
  26 
  27 // 共有メモリアロケーションポインタ
  28 extern char* ShmP;
  29 // グローバル変数
  30 static offset_ptr<tree> words;   // 見出語形単語ツリーへのオフセットポインタ
  31 static offset_ptr<tree> wordsap; // 出現形単語ツリーへのオフセットポインタ
  32 static int wordp = 0;            // 単語アドレス(作品先頭からの相対位置)
  33 static int flid  = 0;            // 現作品番号(ファイル名でもある)
  34 static int wordc = 0;            // 処理単語数(のべ数)
  35 // Lemmatizer Russian Grammatical Resources
  36 const char* dict =
  37     "/usr/local/share/turglem/russian/dict_russian.auto";
  38 const char* prdm =
  39     "/usr/local/share/turglem/russian/paradigms_russian.bin";
  40 const char* pred =
  41     "/usr/local/share/turglem/russian/prediction_russian.auto";
  42 tl::lemmatizer lem;              // lemmatizer instance
  43 
  44 // corpus 行をスキャンし,単語をツリーに登録
  45 void corpus_scan(std::string& cl, offset_ptr<tree>& wtp, bool sw)
  46 {
  47     std::vector<std::string> wordv; // 単語 vector
  48     int posd[4];                    // 位置情報
  49     int lwp = 0;                    // 行内単語アドレス
  50 
  51     // corpus line を語分割して単語 vector に格納
  52     corpus_tokenizer(cl, wordv);
  53 
  54     // 位置情報,corpus 分離
  55     int i = 0;
  56     for (std::vector<std::string>::iterator it = wordv.begin();
  57             it != wordv.end(); it++) {
  58         if (i < 4)
  59             // 先頭4語(数字)は,位置情報として int 配列に格納
  60             posd[i] = atoi((*it).c_str());
  61         else {
  62             // 大文字変換
  63             *it = boost::locale::to_upper(*it);
  64             // lem が true のとき Lemmatizer を使用して見出語に変換
  65             if (sw) {
  66                 try {
  67                     lemmatize(*it, lem);
  68                 } catch (const std::exception& e) {
  69                     std::cerr << "Lemmatizer error: " <<
  70                               e.what() << std::endl;
  71                 }
  72             }
  73         }
  74         i++;
  75     }
  76 
  77     // 現作品番号がキーブレークしたら,作品番号を更新し,単語アドレスをリセット(Global)
  78     if (posd[1] != flid) {
  79         flid = posd[1]; // 作品番号
  80         wordp = 0;      // 単語アドレス(作品先頭からの相対位置)
  81     }
  82 
  83     // 単語 vector の単語,位置情報,単語アドレスを二分木に登録
  84     for (int i = 4; i < wordv.size(); i++)
  85         wtp->enter_tree
  86         (wordv[i], posd[0], posd[1], posd[2], posd[3], lwp++, wordp++);
  87 }
  88 
  89 // 単語情報二分木共有メモリ構築・主処理
  90 int main(int argc, char** argv)
  91 {
  92     // 引数チェック
  93     if (argc != 2) {
  94         std::cerr <<  "Usage: " << argv[0] << " Corpus-file" << std::endl;
  95         exit(8);
  96     }
  97 
  98     //  Daemonize
  99     //  ログファイルをオープンし,標準出力・エラー出力を接続
 100     std::ofstream log(LOGFILE, std::ios::out | std::ios::app);
 101     std::cout.rdbuf(log.rdbuf());
 102     std::cerr.rdbuf(log.rdbuf());
 103 
 104     //  起動済チェック
 105     //  CorpusLoader Shared Memory Construction が完了していること
 106     std::ifstream cpidf;
 107     cpidf.open(CLPIDF);
 108     if (!cpidf) {
 109         log << "WordTreeBuilder CorpusLoader not executed yet." << std::endl;
 110         cpidf.close();
 111         return 1;
 112     } else {
 113         std::string ln;
 114         getline(cpidf, ln);
 115         if (ln == "STARTING") {
 116             log << "WordTreeBuilder CorpusLoader not completed yet." <<
 117                 std::endl;
 118             cpidf.close();
 119             return 1;
 120         }
 121     }
 122     cpidf.close();
 123 
 124     //  すでに pid ファイルが存在していればただちに終了する
 125     //  pid ファイルが存在しなければ,初期作成し STARTING を書き込む
 126     std::ifstream opidf;
 127     opidf.open(WTPIDF);
 128     if (opidf) {
 129         log << "WordTreeBuilder already started." << std::endl;
 130         return 1;
 131     }
 132     std::ofstream pidf(WTPIDF);
 133     log << "WordTreeBuilder starting." << std::endl;
 134     pidf << "STARTING";
 135     pidf.close();
 136 
 137     //  端末分離
 138     if (pid_t pid = fork()) {
 139         if (pid > 0) {
 140             // ここは親プロセス。終了しなければならない
 141             exit(0);
 142         } else {
 143             log << "WordTreeBuilder First fork failed." << std::endl;
 144             log.close();
 145             return 1;
 146         }
 147     }
 148 
 149     // プロセスを新セッションリーダとし,プロセスを端末から切断
 150     setsid();
 151 
 152     // ルートディレクトリに移動
 153     chdir("/");
 154 
 155     // ファイルのパーミションマスクをクリア
 156     umask(0);
 157 
 158     // プロセスが制御端末を捕まえてしまうことのないことを確実化
 159     if (pid_t pid = fork()) {
 160         if (pid > 0) {
 161             exit(0);
 162         } else {
 163             log << "WordTreeBuilder Second fork failed." << std::endl;
 164             log.close();
 165             return 1;
 166         }
 167     }
 168 
 169     // コーパスファイル存在チェック
 170     std::ifstream icf;
 171     icf.open(argv[1]);
 172     if (!icf) {
 173         std::cerr << "WordTreeBuilder " << argv[1] <<
 174                   " open failed." << std::endl;
 175         remove(WTPIDF);
 176         return 1;
 177     }
 178 
 179     // 標準入出力をクローズする。これは起動端末からデーモンを分離
 180     close(0);
 181     close(1);
 182     close(2);
 183 
 184     // デーモン化完了・共有メモリ処理開始メッセージ
 185     pid_t pid = getpid();
 186     log << "WordTreeBuilder daemon started. PID: " << pid << std::endl;
 187 
 188     // シグナルハンドリング
 189     sigset_t ss;      // signal set
 190     int signo;        // signal number
 191     bool flag = true; // signal wait flag: wait->true; terminate->false;
 192 
 193     // シグナル集合に全シグナルをセット
 194     sigfillset(&ss);
 195     // シグナルをブロック
 196     sigprocmask(SIG_BLOCK, &ss, NULL);
 197 
 198     // 共有メモリを開始前と終了後に削除
 199     shm_wtree_remove remover;
 200 
 201     // 共有メモリに単語二分木を構築
 202     log << "WordTreeBuilder Word Tree Shared Memory Construction." << std::endl;
 203     log << "WordTreeBuilder Corpus: " << argv[1] << std::endl;
 204     managed_shared_memory segment(create_only, SHMTREE, SHMTRSZ);
 205     log << "WordTreeBuilder shared memory segment attached." << std::endl;
 206     void* pt;
 207     try {
 208         pt = segment.allocate(SHMTRSZ - MNGSIZE);
 209     } catch (const std::exception& e) {
 210         log << "WordTreeBuilder shm allocation error: " <<
 211             e.what() << std::endl;
 212         remove(WTPIDF);
 213         return 1;
 214     }
 215     log << "WordTreeBuilder shared memory allocated." << std::endl;
 216 
 217     // 使用可能共有メモリの先頭アドレスをポイント
 218     ShmP = static_cast<char*>(pt);
 219     log << "WordTreeBuilder Shared Memory pointer origin:  "
 220         << (void*) ShmP << std::endl;
 221 
 222     // locale をセット
 223     boost::locale::generator gen;
 224     std::locale::global(gen("fr_FR.UTF-8"));
 225     std::locale::global(gen("ru_RU.UTF-8"));
 226 
 227     // Lemmatizer 辞書リソースのロード
 228     try {
 229         lem.load_lemmatizer(dict, prdm, pred);
 230     } catch (const std::exception& e) {
 231         log << "WordTreeBuilder Lemmatizer error: " << e.what() << std::endl;
 232         remove(WTPIDF);
 233         return 1;
 234     }
 235 
 236     // ツリー用各種ポインタ
 237     void* hp;                               // ツリー先頭アドレス
 238     managed_shared_memory::handle_t handle; // 連絡用ハンドラ
 239     // Config ファイル (追記モード)
 240     std::ofstream conf(CONFIG, std::ios::out | std::ios::app);
 241 
 242     // 共有メモリに見出語形単語二分木を割当
 243     char* lmsp = ShmP;                        // 見出語形ツリー入口ポインタを退避
 244     words = new(reinterpret_cast<tree*>(lmsp)) tree;
 245     ShmP += sizeof(tree);                     // ポインタ更新
 246     log << "WordTreeBuilder Lemmatized form Word Tree:" << std::endl;
 247 
 248     // 共有メモリ上に lemmatized 単語二分木を構築
 249     boost::timer t;             // 処理時間タイマ
 250     std::string ldata;          // 入力バッファ
 251     int lcnt = 0;               // 処理行数
 252     while (icf && getline(icf, ldata)) {
 253         corpus_scan(ldata, words, true);    // lemmatized form
 254         lcnt++;
 255         if ((lcnt % 10000) == 0)
 256             log << "WordTreeBuilder\t" <<
 257                 lcnt << " lines proceeded." << std::endl;
 258     }
 259     icf.close();
 260 
 261     // 実行実績出力
 262     log << "WordTreeBuilder\tWord Tree elapsed   "
 263         << t.elapsed() << " sec." << std::endl;
 264     log << "WordTreeBuilder\tWord Tree proceeded " <<
 265         lcnt  << " lines." << std::endl;
 266     log << "WordTreeBuilder\tWord Tree proceeded "
 267         << words->getwordproccount() << " words." << std::endl;
 268     log << "WordTreeBuilder\tWord Tree built     "
 269         << words->getwordtotalcount() << " words." << std::endl;
 270     log << "WordTreeBuilder\tShared Memory pointer current: "
 271         << (void*) ShmP << std::endl;
 272     log << "WordTreeBuilder\tWord Tree entry address: "
 273         << (void*) lmsp << std::endl;
 274     log << "WordTreeBuilder\tAddress difference:      " << std::hex
 275         << ShmP - lmsp << " (" << std::dec << ShmP - lmsp << ")" << std::endl;
 276 
 277     // 見出語形単語二分木の共有メモリアドレスを連絡するためのハンドルを取得
 278     hp = static_cast<void*>(lmsp);
 279     handle = segment.get_handle_from_address(hp);
 280     log << "WordTreeBuilder\tLemmatized form Word Tree handle:  "
 281         << handle << std::endl;
 282     conf << "WORDTLHDL=" << handle << std::endl;
 283 
 284     // 共有メモリに出現形単語二分木を割当
 285     char* apsp = ShmP;                        // 出現形ツリー入口ポインタを退避
 286     wordsap = new(reinterpret_cast<tree*>(apsp)) tree;
 287     ShmP += sizeof(tree);                     // ポインタ更新
 288     log << "WordTreeBuilder\tAppearance form Word Tree:" << std::endl;
 289 
 290     // 共有メモリ上に出現形単語二分木を構築
 291     t.restart();                // タイマをリセット
 292     icf.open(argv[1]);          // 入力コーパス
 293     lcnt = 0;                   // 処理行数
 294     while (icf && getline(icf, ldata)) {
 295         corpus_scan(ldata, wordsap, false); // non lemmatized form
 296         lcnt++;
 297         if ((lcnt % 10000) == 0)
 298             log << "WordTreeBuilder\t" <<
 299                 lcnt << " lines proceeded." << std::endl;
 300     }
 301     icf.close();
 302 
 303     // 実行実績出力
 304     log << "WordTreeBuilder\tWord Tree elapsed   "
 305         << t.elapsed() << " sec." << std::endl;
 306     log << "WordTreeBuilder\tWord Tree proceeded " <<
 307         lcnt << " lines." << std::endl;
 308     log << "WordTreeBuilder\tWord Tree proceeded "
 309         << wordsap->getwordproccount() << " words." << std::endl;
 310     log << "WordTreeBuilder\tWord Tree built     "
 311         << wordsap->getwordtotalcount() << " words." << std::endl;
 312     log << "WordTreeBuilder\tShared Memory pointer current: "
 313         << (void*) ShmP << std::endl;
 314     log << "WordTreeBuilder\tWord Tree entry address:  "
 315         << (void*) apsp << std::endl;
 316     log << "WordTreeBuilder\tAddress difference:       " << std::hex
 317         << ShmP - apsp << " (" << std::dec << ShmP - apsp << ")" << std::endl;
 318 
 319     // 出現形単語二分木の共有メモリアドレスを連絡するためのハンドルを取得
 320     hp = static_cast<void*>(apsp);
 321     handle = segment.get_handle_from_address(hp);
 322     log << "WordTreeBuilder\tAppearance form Word Tree handle:  "
 323         << handle << std::endl;
 324     conf << "WORDTAHDL=" << handle << std::endl;
 325     conf.close();
 326 
 327     // PID ファイルに pid を書き込んで,共有メモリ処理完了を示す
 328     pidf.open(WTPIDF);
 329     pidf << pid;
 330     pidf.close();
 331 
 332     // 終了指示シグナルを待つ
 333     log << "WordTreeBuilder waiting for termination signals: " <<
 334         "SIGINT, SIGTERM, SIGHUP, SIGQUIT." << std::endl;
 335     while (flag) {
 336         if (sigwait(&ss, &signo) == 0) {
 337             switch (signo) {
 338             case SIGINT:
 339             case SIGTERM:
 340             case SIGHUP:
 341             case SIGQUIT:
 342                 log << "WordTreeBuilder signal " << signo <<
 343                     " accepted." << std::endl;
 344                 flag = false;
 345                 break;
 346             default:
 347                 log << "WordTreeBuilder signal " << signo <<
 348                     " ignored." << std::endl;
 349                 break;
 350             }
 351         }
 352     }
 353 
 354     // PID ファイル削除
 355     remove(WTPIDF);
 356     log << "WordTreeBuilder daemon terminated by signal " << signo <<
 357         "." << std::endl;
 358 
 359     return (0);
 360 }

/* [previous][next][first][last][top][bottom][index][help] */