/* */
This source file includes following definitions.
- corpus_scan
- 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 }
/* */