ホーム » スタッフ » 斉藤徹 » 実験(斉藤)

実験(斉藤)」カテゴリーアーカイブ

2021年10月
 12
3456789
10111213141516
17181920212223
24252627282930
31  

最新の投稿(電子情報)

アーカイブ

カテゴリー

さくらクラウドの実験サーバのアップグレード

学生実験で、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 に入れ替える。

5G-4G-WiFi速度テスト

大講義室で、5G(KDDI)の接続ができるので、iPhone12端末にて速度テスト。

fast.com で測定したら、4G/5G/WiFiであんまり変わらない値になり、上流の限界と思われるので別サイトで測定してみた。

{CAPTION}

{CAPTION}

{CAPTION}

{CAPTION}

{CAPTION}

{CAPTION}

データの扱いは丁寧に

うーむ、卒研発表のレジメ、有効数字がおかしいので、質疑応答の時にツッコミを入れた。

有効数字を注意すること

私らの恩師のS先生なら、このネタやると卒研発表の会場が凍りついたよなぁ… (^_^;
でも、情報系の人間は、それっぽく見える数字で嘘ついちゃいかん。

卒研の査読のお手伝いしたのでも、この辺の有効桁がいい加減なのがあったので、あえて指摘。

学会の論文とか発表で、こういうところが変な状態だと、変な有効桁というだけで内容を信用してもらえなくなる。
(ちなみに、この学生さんの卒研は内容もあり、問題なく優秀な内容なので、あえて指摘しています。)

ダメな例:

  • 実験データが 0.1200 , 0.3400 , 0.3100, 0.4100 みたいな、データが20個ほど並ぶ…。
  • 5段階評価のアンケートを行い、20件の回答があった。評価値の平均値は、3.45678 となった。
  • 100枚の画像を用い学習させ、テストデータ100枚で学習の正解率を求めたら 89.1234 [%] であった。

グラフの見せ方にも注意

あと、プレゼン資料やレジメ資料だけど、色の使い方にも注意しましょう。

同じ青系でグラフを描いたら、後ろの人なら見て区別できません。

あと、学会などでも印刷資料になるときは、よほどのことがないかぎり白黒印刷。このようなグラフ資料はデータがまるっきり区別できなくなるので、モノクロにしても内容が判別できるように資料を作りましょう。

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 

専攻科実験・コンパイラと関数電卓プログラム作成

コンパイラの技術と関数電卓プログラム(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() という関数が自動的に作られ、次に示す構文解析ツールから呼び出される。

# flex は、lex の改良版。

(( mycalc.l ))
%{
#include <stdio.h>
// yaccが出力するヘッダファイル
#include "y.tab.h"

int yywrap( void )
{
   // 1: スキャナ終了
   // 0: yyin を切り替えて継続
   return 1 ;
}
%}
%%
"+"  return ADD ;
"-"  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は構文解析側で定義する定数)

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$1 ADD$2 term$3では、ルールの各要素をアクションで参照する時には、$1,$2,$3 といった変数を利用できる。ルール「加算式($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) 式を読みやすくする空白を処理できるように 改造せよ。