さくらクラウドの実験サーバのアップグレード
学生実験で、Webプログラムのセキュリティ問題の対応をテーマに実験しているけど、Ubuntu 18系を20系にアップグレードを行った。基本的な実験だけなので、Apache+PHP程度なので、移行も手間取らないだろうと踏んでいたけど、sulinux でちょっと手間取った。
# systemctl enable apache2 # aptitude install update-manager # aptitude update # aptitude dist-upgrade # do-release-upgrade -d
無事に、更新が終わって再起動をかけたら、ブートに失敗。ssh などが繋がらなくなる。
どうしようもなくなったので、さくらのクラウドのコンソールをみると、sssd 関連でエラーが出てブート途中で止まっている。
冷や汗をかきながら、Ubuntu ブート時のリカバリーモードの起動を試みて、ようやく成功。
起動時にESCを押すと、ブートメニューが表示される。インストール済みの kernel とそのリカバリーモードの一覧がでるので、リカバリーモードでログイン。”aptitude remove sssd-common” により一旦、sssd を削除(sulinux関連)すると、無事に起動したので、基本機能の確認をして、再び”aptitude install sssd-common”。
アップグレード作業中に、apache2 が disable されていたり、php の関連パッケージが入っていなかったので、インストール。
# systemctl enable apache2 # systemctl start apache2 # aptitude install php-mbstring php-sqlite3 # a2enmod php7.4
情報処理演習室のルータを交換
EI棟3F演習室で、Aruba の WiFi 環境のための Buffalo ルータを使っていたけど、警告メッセージが出始めたので、確認したらルータの管理画面さえ表示できなくなってきた。古いルータだったので寿命と思われる。
かといって、このルータは、4EI教室,5EI教室,2F実験室,3F演習室,4F創成LABを束ねる Aruba WiFi の上流にあたるため、実験室がことごとく使えなくなる。そこで、手持ちのマニアックな Edgerouter-X に入れ替える。
データの扱いは丁寧に
うーむ、卒研発表のレジメ、有効数字がおかしいので、質疑応答の時にツッコミを入れた。
有効数字を注意すること
私らの恩師のS先生なら、このネタやると卒研発表の会場が凍りついたよなぁ… (^_^;
でも、情報系の人間は、それっぽく見える数字で嘘ついちゃいかん。
卒研の査読のお手伝いしたのでも、この辺の有効桁がいい加減なのがあったので、あえて指摘。
学会の論文とか発表で、こういうところが変な状態だと、変な有効桁というだけで内容を信用してもらえなくなる。
(ちなみに、この学生さんの卒研は内容もあり、問題なく優秀な内容なので、あえて指摘しています。)
グラフの見せ方にも注意
あと、プレゼン資料やレジメ資料だけど、色の使い方にも注意しましょう。
同じ青系でグラフを描いたら、後ろの人なら見て区別できません。
あと、学会などでも印刷資料になるときは、よほどのことがないかぎり白黒印刷。このようなグラフ資料はデータがまるっきり区別できなくなるので、モノクロにしても内容が判別できるように資料を作りましょう。
CTF実験: unix 系開発環境のインストール
CTFでは、様々な unix 系の開発環境のコマンドが用いられる。
これらの環境を構築するためのメモを記載。
Windows の場合
macOS の場合
macOS で unix 系の開発環境を使う場合には、homebrew や MacPorts を用いることが多い。
CTFでよく使われるunix系パッケージ
((ファイル系)) file - ファイルの種別判定 zip - zip,unzip(ファイル圧縮解凍) gzip - gzip,gunzip(ファイル圧縮解凍-GNU) nkf - nkf(日本語漢字フィルタ) hexcurse - hexcurse(バイナリエディタ) ((OS系)) gcc - gcc(コンパイラ-GNU) gdb - gdb(デバッガ-GNU) binutils - objdump(逆アセンブラ) , - nm(オブジェクトファイルのシンボル出力) , - strings(文字部分の出力) ((ネットワーク系)) telnet - telnet(telnetクライアント) netcat-openbsd - nc(TCP/IP 汎用ツール) bind9-dnsutils - nslookup, dig(DNS参照ツール) whois - whois(ディレクトリサービスクライアント) ((ブラウザ系)) w3m - w3m(テキストブラウザ) wget - wget(テキストブラウザ+ダウンローダ) curl - curl(ダウンローダ) ((使用上要注意)) nmap - nmap(ネットワーク調査ツール) wireshark - wireshark(パケットキャプチャ) - windows用とかmac用のバイナリの方が便利
各unix系のインストールコマンド
((WSL2の場合)) $ sudo apt-get install gcc binutils ((Homebrew の場合)) $ sudo brew install gcc binutils ((MacPorts の場合)) $ sudo port install gcc binutils
専攻科実験: unix系 開発環境のインストール
コンパイラの技術と関数電卓プログラムの実験では、unix系の開発環境で一般的なコンパイラ作成用のツール flex , bison を用いる。これらの環境を構築するためのメモを記載。
Windows の場合
((ubuntuの場合)) $ sudo apt-get install gcc flex bison make
macOS の場合
macOS で unix 系の開発環境を使う場合には、homebrew や MacPorts を用いることが多い。
((Homebrew の場合)) $ sudo brew install gcc flex bison make ((MacPorts の場合)) $ sudo port install gcc flex bison make
専攻科実験・コンパイラと関数電卓プログラム作成
- コンパイラの技術と関数電卓プログラム(1)
- 再帰下降パーサによる構文解析による電卓プログラム作成
- 補助資料:コンパイラの技術と関数電卓プログラム(1-2)
- 課題
- 複数桁の数字が使えること。
- 式中に空白が使えること。
- 何らかの演算子を追加すること。
- (例) %,単項演算子のマイナスなど
- 演算子が左結合か右結合か確認すること。
- オプション課題
- 変数が使えること。
(変数名は1文字のA-Zといったもので良い)
- 変数が使えること。
- レポート内容
- コンパイラ技術の概要、課題(1)の説明・最終的なBNF記法・ソース・動作検証、考察
- コンパイラの技術と関数電卓プログラム(2)
- コンパイラツールを使ったLR構文解析による電卓プログラムの作成
- 補助資料:専攻科実験: unix系 開発環境のインストール
- 課題
- 基本的に、lex+yaccで(1)と同様の課題で参考資料を元に改良を行う。
- レポート内容
- lex,yaccの概要、課題(2)の説明・ソース・動作検証、考察
コンパイラの技術と関数電卓プログラム(2)
前半では、1文字の数字と簡単な演算子で表現される計算式を再帰下降パーサで計算する処理で、演習を行った。
後半は、さらに実際のコンパイラに近いものとして、 C言語で広く使われている、字句解析ツール(lexical analyzer : lex or flex)、 構文解析ツール(parser : yacc or bison) を使って、 さらに現実的な関数電卓プログラムを作ってみる。
lex or flex による字句解析
lexは、字句解析のプログラムを自動生成するツール。 “%%”行の内側に、正規表現によるルールとその処理(アクション)を書き並べる。 また、“%{ … %}”の内側に、その処理に必要なC言語のソースを書き、 lex で処理を行うと、lex.yy.c というC言語のソースを出力する。
lex.yy.c の中には、yylex() という関数が自動的に作られ、次に示す構文解析ツール(yacc or bison)から呼び出される。
# flex は、lex の改良版。
(( mycalc.l )) %{ // lexの %{ %} の内側は、C言語のプログラム #include <stdio.h> // yaccが出力するヘッダファイル #include "y.tab.h" int yywrap( void ) { // 1: スキャナ終了 // 0: yyin を切り替えて継続 return 1 ; } %} %% // %% から %% までは、lex の正規表現とその処理を記述する場所。 "+" return ADD ; // 演算子の種類を示す定数 ADD,SUB,...が定義される "-" return SUB ; "*" return MUL ; "/" return DIV ; "\n" return CR ; [0-9][0-9]* { // 入力はyytextに格納されている。 int temp ; sscanf( yytext , "%d" , &temp ) ; yylval.int_value = temp ; // 返り値は、字句(トークン)の種別を表す定数 return INT_LITERAL; // 整数リテラルを返す } %%
このプログラムを、lex で処理させると、正規表現“+” 記号に ADD という定数記号を割り振るとか、正規表現の数字列” [0-9][0-9]* “をみつけると、その正規表現に対する{〜}までに書かれたアクションを処理するため、文字列から数値を生成(sscanf)して、その場合の記号に INT_LITERAL という定数記号を割り振る…といった処理のプログラムを自動生成してくれる。(INT_LITERALは構文解析側で定義する定数)
- mycalc.l
- 参考: lex のそれ以外の文字の処理を追加しておいてください。
yacc or bison
yacc ( Yet Another Compiler Compiler ) もしくはその改良版の bison は、構文解析のプログラムを自動生成してくれるツールである。構文をBNF記法で記載すると、字句解析ツール(lex等)を呼び出しながら構文に合わせたトークンの出現に応じた状態遷移のための「遷移テーブル」を自動生成し、 その遷移テーブルを用いた処理のプログラムをC言語で出力してくれる。
先に示した字句解析プログラム(lex)は、様々なトークン(C言語なら演算子やキーワード)とデータ(C言語なら数値や文字列)を返す。このデータには様々な場合考えられるので、 そのトークンに合わせたデータの型を “%union” の中に記載する(中身はC言語の共用体)。 “%%”〜”%%” の間には、BNF記法の(ルール)と、それに対応する処理(アクションは“{ }”の中の部分)を記載する。最初の“%{ … %}”の間には、それ以外の処理に必要なC言語の処理を記載する。
# bison(水牛)は、yacc(山牛)の改良版。
(( mycalc.y )) %{ #include <stdio.h> #include <stdlib.h> // yacc が定義する内部関数のプロトタイプ宣言 #define YYDEBUG 1 extern int yydebug ; extern int yyerror( char const* ) ; extern char *yytext ; extern FILE *yyin ; // 最初に呼び出される関数yyparse() extern int yyparse( void ) ; // 字句解析を呼び出す関数yylex() extern int yylex( void ) ; %} // 字句(トークン)の定義 %union { int int_value; } %token <int_value> INT_LITERAL %token ADD SUB MUL DIV CR %type <int_value> expression term primary_expression %% // 構文の定義 line_list : line // 行の繰り返し | line_list line ; line : expression CR { printf( ">>%d\n" , $1 ) ; } ; // 以下のBNFルールは、単純に再帰に置き換えると // 無限に再帰して異常終了するものであることに注意 expression : term | expression ADD term { $$ = $1 + $3 ; } | expression SUB term { $$ = $1 - $3 ; } ; term : primary_expression | term MUL primary_expression { $$ = $1 * $3 ; } | term DIV primary_expression { $$ = $1 / $3 ; } ; primary_expression : INT_LITERAL ; %% // 補助関数の定義 int yyerror( char const* str ) { fprintf( stderr , "parser error near %s\n" , yytext ) ; return 0 ; } int main( void ) { yydebug = 0 ; // yydebug=1 でデバッグ情報表示 yyin = stdin ; if ( yyparse() ) { // 構文解析を開始 fprintf( stderr , "Error ! Error ! Error !\n" ) ; exit( 1 ) ; } }
yacc では、%%-%%の間に書かれたBNF記法によるルールとアクションをプログラムに変換してくれる。BNF記法の要素は、“要素 : ルール1 {アクション1} | ルール2 {アクション2} | … ;” といった構成になっている。要素 expression に対するルール “expression ADD term“では、ルールの各要素をアクションで参照する時には、$1,$2,$3 といった変数を利用できる。ルール「加算式 + 乗算式」という文になっている部分を見つけると、そのルールに対応するアクション“{ $$ = $1 + $3 ; }”「$1(加算式部分の値)と、$3(乗算式の部分)の値を加えて、$$(式の結果)に代入する」といった処理を生成してくれる。yyparse() 関数を呼び出すと、構文の一番最上部の line_list に相当する処理が起動される。yyerror()は、構文解析の途中で文法エラーになった時に呼び出される関数。
生成されるパーサの内容に興味があるなら、生成される y.tab.c の内容を読むと良い。
make と Makefile
これらのプログラムでは、字句解析プログラム mycalc.l から生成された lex.yy.c, y.tab.h と, 構文解析プログラム mycalc.y から生成された y.tab.c を組合せて1つの実行ファイルにコンパイルする。 これらの手順は煩雑なので、make ツールを使う。
make は、 Makefile に記載されている“ターゲット”と、それを作るために必要な“依存ファイル”、 “依存ファイル”から”ターゲット”を生成する処理“アクション”から構成される。 make は、ターゲットと依存ファイルの更新時間を比較し、 必要最小限の処理を行う。
基本的な Makefile の書き方は、
ターゲット: 依存ファイル... アクション # アクションの前はタブ
の様に書き、依存ファイルが更新されていたら、アクションを実行し、ターゲットを更新する。
以下に、今回の課題で使用する Makefile を示す。
(( Makefile )) # 最終ターゲット mycalc: y.tab.o lex.yy.o gcc -o mycalc y.tab.o lex.yy.o # 構文解析処理 y.tab.o: mycalc.y bison -dy mycalc.y # -dy : yacc互換モード gcc -c y.tab.c # 字句解析処理 lex.yy.o: mycalc.l mycalc.y flex -l mycalc.l # -l : lex互換モード gcc -c lex.yy.c # 生成ファイルの削除ルール clean:; rm mycalc y.tab.c y.tab.h lex.yy.c *.o (( ファイルの依存関係のイメージ図 )) mycalc.l mycalc.y | \ | lex.yy.c y.tab.h y.tab.c | \ | lex.yy.o y.tab.o \ / mycalc
この課題にあたり、後半の実験では flex, bison などの unix 系プログラミング環境を利用する。
macOS の利用者であれば MacPorts や Homebrew 、Windows 利用者であれば、wsl2(Windows subsystem for Linux) や Cygwinなどをインストールし実行すること。
今回の実験であれば、linux(Debian系)ならば、”sudo apt-get install flex bison gcc make” にて、必要なパッケージをインストールして実験を行うこと。
コンパイラの技術と関数電卓プログラム(1-2)
前回の実験資料では、再帰下降パーサについて説明し、サンプルプログラムを示した。
演算子の左結合・右結合
ここで、プログラムの実際の動きについて考えてみる。前回の乗除式の BNF 記法による定義は以下のようであった。
exp_乗徐式 ::= DIGIT '*' exp_乗徐式 | DIGIT '/' exp_乗徐式 | DIGIT ;
このBNFによる文法において、1*2*3 を考えると、以下のように解析がすすむ。
しかし、これでは 1*(2*3) であり、右結合にて処理が行われたことになる。
exp_乗徐式 /|\ DIGIT| exp_乗徐式 | | /|\ | |DIGIT| exp_乗徐式 | | | | | | | | | DIGIT | | | | | 1 * 2 * 3
左結合とするには
これをC言語で一般的な、(1*2)*3 といった左結合の処理になるように、BNF 記法の文法を下記のように書き換えるかもしれない。
exp_乗徐式 ::= exp_乗徐式 '*' DIGIT | exp_乗徐式 '/' DIGIT | DIGIT ;
しかし、このBNF記法をそのまま下記のような再帰に置き換えると、再帰が無限に続き異常終了してしまう。
int exp_MUL_DIV( ... ) { int left = exp_MUL_DIV( ... ) ; if ( **endp == '*' ) { (*endp)++ ; if ( isdigit( **endp ) ) { int right = **endp - '0' ; (*endp)++ ; return left + right ; } } else ... : }
左結合のプログラムにする場合は、BNF記法の処理を杓子定規に再帰プログラムで記述する必要はない。
空白除去
プログラム中の空白を無視するのであれば、以下のような補助関数を作っておくと便利かな。使い方は考えること。
void skip( char**ppc ) { while( isspace( **ppc ) ) (*ppc)++ ; }
コンパイラの技術と関数電卓プログラム(1)
コンパイラを作るための技術の基礎を学んでもらうために、 簡単な関数電卓プログラム作成を課題とする。 基本は、printf( “%d” , eval( “1+2*3”) ) みたいな計算プログラムを作成する。
計算式から、計算処理を行う場合、演算子の優先順位を正しく処理できることが求められる。
一般的には、計算の機械語を生成する場合、データを準備して計算という方法であり、 逆ポーランド記法変換が行われる。
たとえば、”1+2×3″は、”1,2,+,3,×” といった表記に改められ、変換後の式は スタックを用いて、「値はpush,演算子はpop,pop,計算して,push」という 単純なルールで処理すれば、計算を行うことができる。
字句解析と構文解析
このような、計算式の処理を実行する場合、”1“,”+“,”2“,”✳︎“,”3“という 字句に切り分ける処理を、字句解析という。 この結果から、”式✳︎式”なら掛け算、”式+式”は足し算といった、 前後の字句の組合せから、構文木を生成する処理は、構文解析という。 コンパイラであれば、この後、最適化、コード生成などが行われる。
C言語であれば、コンパイル前後には以下の処理が行われる。
- プリプロセッサ処理
↓(#の無いCコード) - コンパイル処理
- 字句解析 — 今回の実験
- 構文解析 — 今回の実験のキモ
- コード生成
↓(中間コード)
- リンク処理 ← ライブラリ
↓ - 機械語
字句解析と正規表現
字句(トークン)の切り出しでは、その文法を説明する際には、正規表現などを用いる。
今回の実験の後半では、正規表現に合わせたプログラミングツール(flex)を用いるが、前半では正規表現に合わせた文字列処理をプログラムする必要がある。
簡単な正規表現 . 任意の文字 * 直前の0回以上の繰り返し + 直前の1回以上の繰り返し [0-9a-z] カッコの中の文字のいずれか - は範囲を示す。 (a|b|c) 丸カッコの|区切りのうちのどれか。 (例) C言語の変数の正規表現 [a-zA-Z_][a-zA-Z0-9_]*
構文解析の方法
構文解析では、構文を状態遷移として考え、この式の後にくる可能性のある状態は? という考えで解析を行う。 このような構文は、一般的にバッカス・ナウア記法(BNF)などで表現されることが多い。 また、簡単な構文解析であれば、
などが用いられる。
再帰下降パーサ
簡単な再帰下降パーサの演習として、1桁の数字と+,*演算子の処理プログラムを 考える。
加減,乗除の式のバッカス記法(BNF) exp_加減 ::= exp_乗除 '+' exp_乗除 | exp_乗除 '-' exp_乗除 | exp_乗除 (注意)このBNFは若干間違っているよ!! ; exp_乗除 ::= DIGIT '*' exp_乗除 | DIGIT '/' exp_乗除 | DIGIT ; DIGIT ::= [0-9] ;
練習として、上に示す再帰下降パーサに、 (1) “(“,”)” を含めた場合の、BNF 記法を考え、(2) 数値として複数桁が使えるようにし、 (3) 式を読みやすくする空白を処理できるように 改造せよ。