魔術師見習いのノート

プロフィール

魔術師見習い
Author魔術師見習い-_-.
Twitter魔術師見習い

コンピュータ関係のメモを主に書きます.

MENU

コンピュータ・ネットワーク(2) -物理層-

投稿日:
タグ:

目次

物理層は5階層TCP/IPモデルの下から1番下の層である.

アプリケーション層
トランスポート層
ネットワーク層
データリンク層
物理層

--編集中--

  • 線路
  • 無線
  • 衛生

双方向通信

全二重(full duplex)
半二重(half duplex)

ネットワーク・トポロジー

ネットワークとはいくつかのノード間が接続されてできる通信網のことである.ネットワークにはさまざまな幾何学的な形(トポロジー)がある.以下にいくつかの例を紹介する.以下の図では,○がノード,線が接続,□が接続を繋ぐ集線装置(例:ハブ)を表す.

デイジーチェイン型
ノードを数珠繋ぎで繋いだトポロジー.最もコストが低いが,冗長性も低く耐障害性が最も低い.いずれか1つのノードか経路が機能しなくなると,通信できなくなる可能性がある.
バス型
バスと呼ばれる伝送路を通じていくつものノードが接続されたトポロジー.他のノードによる通信の影響はないが,いずれか1つの経路が機能しなくなると,通信できなくなる可能性がある.ただし,バスのトポロジーによっては耐障害性も向上する.ただし,その分コストも上がる.
リング型
あるノードから別のノードへの経路が必ず2つあるトポロジー.いずれか1つの経路やそれに関わるノードが機能しなくても,通信可能である.
スター型
いずれかのノードや集線装置までの経路で問題が発生しても,通信可能である.ただし集線装置が停止した場合,全ての通信が不能になる.
ツリー型
巡回路(ループ)のないトポロジーのこと.デイジーチェイン型もまたツリー型の一種である.
フルコネクト型
耐障害性が最も高く,全てのノードに対して最短な経路があるトポロジー.あるノードから別のノードへの経路がいくつもあるので,いくつかのノードや経路が使えなくなっても通信が可能である.ただし費用も最も高い.

ネットワーク・トポロジーには次の2種類のトポロジーがある.

  • 物理的トポロジー
  • 論理的トポロジー
物理的トポロジーと論理的トポロジーのトポロジーが同じであるとは限らない.例えば,IEEE802.3の10BASE-Tの物理的トポロジーはスター型だが,論理的トポロジーはバス型になっている.

C++プログラマのためのPython

投稿日:
修正日:
タグ:

クラス定義

Pythonはクラス定義や継承、多重定義をサポートする。クラス定義の文法は、大雑把に説明すると次の通りである。

C++
class クラス名 {
  メンバの定義
};
Python
class クラス名:
  メンバの定義

なおPythonではインデントでブロックを表すため、空のクラスや関数を定義する場合、pass文を使用する。

class Hoge: pass
pass文とは何もしない。


メンバ関数

メンバ関数はクラスに属する関数である。メンバ関数の定義はC++の演算子の定義と似ている。C++は演算子の多重定義をメンバで行う際次のように行う。

class T
{
public:
  void operator+(int arg2){ printf("%d\n", arg2); }
};
これは一見すると1つの引数しか取らないが、2つの引数を受け取る2項演算子である。Pythonのメンバ関数の定義はこれとよく似ている。Pythonでは、メンバ関数を定義する場合、第一引数にオブジェクトを受け取る変数を明示的に記述しなければならない。そして呼び出しの際は第二引数以降が1つずつずれる。
class Cls:
  def setAdd(self, addee, adder):
    self.ans = addee + adder

c = Cls()
c.set(1, 3)
第一引数の変数名は言語的な制約はない。
>>> class Hoge:
...     def func1(hogefugapiyo):
...             hogefugapiyo.v = 20
...     def func2(hahaha):
...             print(hahaha.v)
しかしselfという名前を用いることが多いようである。

インスタンス変数にアクセスする場合、例のように第1引数を使用してアクセスする。


コンストラクタとデストラクタ

Pythonではコンストラクタとデストラクタは次のように定義する。

class Hoge:
  def __init__(self):  # コンストラクタ
    pass
  def __del__(self):   # デストラクタ
    pass
コンストラクタとデストラクタは共に省略可能である。

C++ではdelete文で、Pythonではdel文でオブジェクトの削除を行うが、その振る舞いは異なる。C++のdelete文は、指定したアドレスに格納されたオブジェクトを削除し、そのデストラクタを呼び出す。これに対し、Pythonのdel文は参照カウントを1減らし、参照カウントが0になった時に同様の処理が行われる。

class Hoge {
public:
        Hoge(){std::cout << "Hello\n";}
        ~Hoge(){std::cout << "Bye\n";}
        void func(){std::cout << "test\n";}
};

int main()
{
        Hoge *h = new Hoge();
        Hoge *i = h;
        delete h;  // デストラクタが呼び出される

        return 0;
}

class Hoge:
  def __init__(self): print("Hello")
  def __del__(self): print("Bye")
  def func(self): print("test")




h = Hoge()
i = h;
del h  
del i # デストラクタが呼び出される


Pythonでは、基底クラスのコンストラクタ(__init__)は自動で呼び出されない。

インスタンスアロケータ

Pythonには__init__とは別にインスタンスを生成する際に呼び出される関数がある。それが__new__である。__new__は静的メソッドで、新しいインスタンスを生成する際に自動的に呼び出される。__init__の第1引数がインスタンスであるのに対し、__new__にはクラスが与えられる。__new__()がclsのインスタンスを返さない限り、インスタンスの__init__は呼び出されない。なお__init__と__new__の第2引数以降は同じものが与えられる。

class Hoge:
  def __new__(cls):
    print("new Parent")
    return super().__new__(cls)
  def __init__(self):
    print("init Parent")
このクラスを利用した例は次の通りとなる。
>>> h = Hoge()
new Parent
init Parent
__new__は変更不能な型 (int, str, tuple など) のサブクラスでインスタンス生成をカスタマイズするために使用される。それゆえ、基本的に__new__をオーバーライドする必要はない。


クラス関数

C++ではクラス変数とクラス関数はそれぞれ次のようにstaticキーワードを使って表現する。この時、クラス関数の宣言と定義を分ける場合、定義ではstaticを記述しない。また、C++ではクラス変数はグローバル変数の一種であり、外部にstaticなしの定義を記述しなければならない。

#include 

class Hoge {
private:
	static int i;  // クラス変数
	static int get();  // (1) 宣言だけ記述し、定義は外で記述
public:
	static void set(int v) // (2) まとめて書く
	{
		i = v;
	}
};

int Hoge::i; // 必須
int Hoge::get()
{
	return i;
}

int main()
{
	Hoge v1, v2;
	v1.set(10);
	std::cout << v2.get() << std::endl;
	return 0;
}
これに対し、Pythonでは、特殊な関数やクラスの定義にはデコレータという機能を使用する。デコレータは定義するクラスや関数に機能の追加や変更を施す。クラス関数を定義する場合、関数デコレータのclassmethodを使用する。
class Hoge:
  @classmethod
  def set(cls, v):
    cls.__v = v
  @classmethod
  def get(self):
    return cls.__v
以下に実行例を示す。
>>> o1, o2 = Hoge(), Hoge()
>>> o1.set(1)
>>> o2.get()
1
なおデコレータの定義については本稿では言及しない。以前の記事の『Pythonの特徴』を参照されたし。


抽象クラス

編集中


多態化

C++で多態化を実装するには、virtualキーワードを使用して関数をオーバーライドにする宣言が必要だった。これに対してPythonのメンバ関数は、標準ではメンバ関数が全てC++のpublicとvirtualな関数である。それゆえ、継承クラスで定義した同名の関数は自動的にオーバーライドされる。


継承

Pythonでは継承は次のように記述する。

class クラス名[(基底クラス [, 基底クラス, ...])]:
以下に実際の例を示す。
class Parent:
  def __func(self):
    print("Hello, World")

class Child(Parent):
  def func(self):
    super().func()
PythonではC++と異なり、継承の際にアクセス指定を指定しない。また、基底クラスのコンストラクタ(__init__)は自動で呼び出されない。

静的な型チェックを行うC++では、コンパイル時に継承関係を考慮した型チェックが行われる。これに対し、Pythonではisinstanceという組み込み関数を使用して型チェックを行う。

>>> class Parent: pass
... 
>>> class Child(Parent): pass
... 
>>> p, c = Parent(), Child()
>>> isinstance(p, Parent)
True
>>> isinstance(c, Parent)
True

多重継承

PythonはC++と同様多重継承が可能な仕様である。文法は以下の通りである。

C++
class クラス名 : [アクセス指定子] 基底クラス名 [, ...] {
  メンバの定義
};
アクセス指定子と基底クラスが1つのセットで「,」で区切っていくつも記述できる。アクセス指定子は省略可能で、その場合はprivateである。
Python
class クラス名(基底クラス [, ...]):
  メンバの定義
しかしいくつかの振る舞いが異なる。例えば、継承の際C++にはアクセス指定子を指定してアクセス制御を行うが、Pythonにはそのような機能はない(publicな状態)。
コード出力結果説明
C++
class A{
public:
  A(){printf("I am A\n");}
};

class B : public A {
public:
  B(){printf("I am B\n");}
};

class C : public A{
public:
  C(){printf("I am C\n");}
};

class D : public B, public C{
public:
  D(){printf("I am D\n");}
};
I am A.
I am B.
I am A.
I am C.
I am D.
C++では標準で全ての基底クラスのコンストラクタが自動的に呼び出される。
C++(仮想基底クラス)
class A{
public:
  A(){printf("I am A\n");}
};

class B : virtual public A {
public:
  B(){printf("I am B\n");}
};

class C : virtual public A{
public:
  C(){printf("I am C\n");}
};

class D : public B, public C{
public:
  D(){printf("I am D\n");}
};
I am A.
I am B.
I am C.
I am D.
仮想基底クラスの場合、基底クラスの情報は共有され、同じクラスのコンストラクタは1度しか呼び出されない。
Python(オーバーライド)
class A:
  def __init__(self):
    print("I am A.")

class B(A):
  def __init__(self):
    print("I am B.")

class C(A):
  def __init__(self):
    print("I am C.")

class D(B, C):
  def __init__(self):
    print("I am D.")
I am D.
Pythonでは、__init__をオーバーライドした場合、明示的に呼び出さない限り、基底クラスのコンストラクタは呼び出されない。
Python(非オーバーライド)
class A:
  def __init__(self):
    print("I am A.")

class B(A):
  def __init__(self):
    super().__init__()
    print("I am B.")

class C(A):
  def __init__(self):
    super().__init__()
    print("I am C.")

class D(B, C): pass
I am A.
I am C.
I am B.
Python
class A:
  def __init__(self):
    print("I am A.")

class B(A):
  def __init__(self):
    super().__init__()
    print("I am B.")

class C(A):
  def __init__(self):
    super().__init__()
    print("I am C.")

class D(B, C):
  def __init__(self):
    super().__init__()
    print("I am D.")
I am A.
I am C.
I am B.
I am D.


カプセル化

C++やJava、C#などの多くの言語では、文法は違えどpublic、private、protectedというキーワードでメンバのアクセス制御を行う。

class Hoge:
  v1 = 10            # クラス変数
  _v2 = 100          # プライベートで使用する意図のクラス変数
  __v3 = 1000        # プライベートクラス変数
  def func1(self):   # メンバ関数
    print(self.v4)   # インスタンス(オブジェクト)変数
    print(self.__v5) # プライベートインスタンス変数
  def __func2(self): # プライベートメンバ関数
    pass
  @classmethod
  def func3(cls):    # クラス関数
    print(cls.v1)    # クラス変数
C++ではメンバ関数の標準のアクセス状態は"private"であるが、Pythonでは"virtual public"である。

Pythonでメンバをプライベートとする場合、以下のように接頭辞が__から始まる名前にする。

class Hoge:
  def __func(self):
    print("Hello")
このプライベートメンバにアクセスしようとすると次のようにAttributeErrorが送出される。
>>> Hoge().__func()
AttributeError: Hoge instance has no attribute '__func'

Pythonでは、プライベートメンバの名前は、外部から参照できなくなっている訳ではなく、自動的に名前を変更される。これにより継承クラスで名前の競合を防いでいる。なおプライベートメンバの変更後の名前は、プライベートメンバの名前の接頭に「_クラス名」を追加したものである。前述のサンプルコードのHogeの名前空間を閲覧した結果を以下に示す。

>>> dir(Hoge)
['_Hoge__func', '__class__', '__delattr__', '__dict__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__', '__weakref__']
このように名前が変更されただけであるため、実はこのようにアクセスすることが可能である。
>>> h = Hoge()
>>> h._Hoge__func()
Hello

多様性 検索順

  1. D
  2. B
  3. C
  4. A
class A:
  def func(self):
    print("I am A\n")

class B(A):
  def func(self):
    print("I am B\n")

class C(A):
  def func(self):
    print("I am C\n")

class D(B, C): pass


変数

C++の変数は、スコープやライフタイム、ストレージクラス、アクセス制御、修飾子などの組合せによって非常にさまざまなものがある。Pythonの変数はC++程の種類はないが、それでもいくつかの種類がある。以下にC++の定義と比較した場合のPythonの変数定義を紹介する。

種類C++Python
グローバル変数(プログラム内で共有される変数)
int v = 1;
class Cls {
  void func()
  {
  }
};
なし。
グローバル変数(ファイルスコープの変数)
static int v = 1;
class Cls {
  void func()
  {
  }
};
v = 1
class Cls:
  def func():
    pass
ローカル変数
class Cls {
  void func()
  {
    int v = 1;
  }
};
class Cls:
  def func():
    v = 1
静的ローカル変数
class Cls {
  void func()
  {
    static int v = 1; 
  }
};
なし。
インスタンス(オブジェクト)変数
class Cls {
private:
  int v = 1;
  void func()
  {
  }
};
class Cls:
  v = 1
  def func(self):
    self.w = 1
クラス変数
class Cls {
public:
  static int v;
};
int Cls::v = 1;
class Fuga:
  @classmethod
  def func(cls):
    cls.w = 1
名前空間
namespace nspace {
  int v = 1;
};
class Cls {
  void func()
  {
  }
}
fileA.py
x = 10
fileB.py
import fileA
fileA.x
定数
const int VAL = 1;
class Cls {
  void func()
  {
  }
};
言語仕様としてはないが、定数の意図で使用する場合に名前を大文字にするマナーがある。
公開メンバ変数
class Cls {
public:
  int v = 1;
  void func()
  {
  }
}
class Cls:
  def func(self):
    self.v = 1
非公開メンバ変数
class Cls {
private:
  int v = 1;
  void func()
  {
  }
}
class Cls:
  def func(self):
    self.__v = 1
限定公開メンバ変数
class Cls {
protected:
  int v = 1;
  void func()
  {
  }
}
なし。


型変換

編集中


特殊関数

編集中


例外

Pythonではtry文を次のように記述する。

try:
  // 例外が発生しうる処理
except Exception:
  // 例外取得と処理
else:
  // try節が末尾までいった場合の処理
finally:
  // 最後に必ず行われる処理
Pythonではcatch節の代わりにexcept節がある。except節は送出された例外オブジェクトをasで指定した名前に束縛することができる。except節には、BaseExceptionのサブクラスを指定できる。しかし通常独自の例外を定義する場合、ExceptionというBaseExceptionのサブクラスを継承する。

また、tryとexcept節以外にもelse節とfinally節がある。else節を記述した場合、制御がtry節の末尾までいった場合に実行される。これに対し、finally節を記述した場合、どの節を処理したかに関わらず必ず最後に呼び出される。except節とelse節、finally節は省略可能であり、except節は複数記述でいる。ただし、except節とfinally節の両方を省略することはできない。

Pythonの覚えておきたい関数やクラス、モジュール

投稿日:
タグ:

本稿はPython3の言語仕様とは別に覚えておきたい関数やモジュールについてのメモ。

組み込み関数

map(func, iterable, ...)
第二引数以降で指定したコンテナから同じ添字番号のものを順に抜き出し、それをfuncに引数として与え、その結果を返すジェネレータ。funcに与える引数は、mapの第2引数のコンテナに属するオブジェクトがfuncの第1引数に、mapの第3引数のコンテナに属するオブジェクトがfuncの第2引数といった感じで、mapの第n引数のコンテナに属するオブジェクトがfuncの第n-1引数に与えられる。
def func(x, y, z):
  return x * y * z

print(list(map(func, [1, 2, 3], [2, 4, 6], [3, 6, 9])))
mapはPython2ではジェネレータではなく、コンテナを返す関数である。
filter(func, iterable)
iterableが返すオブジェクト群を順にfuncに引数として与えて評価し、真となるオブジェクトを返すジェネレータ。これを利用すると例えば次のように10から50の範囲の素数を検索するコードを記述することができる。
def isPrime(x):
  for y in range(2, x-1):
    if x%y==0:
      return False
  return True

print(list(filter(isPrime, range(10, 50))))
このコードはこのような結果を標準出力に出力する。
[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
filterもまたPython2ではジェネレータではなく、コンテナを返す関数である。
zip(*iterable)
行列の行と列を交換するような関数。複数のコンテナを受け取り、同じ添字を1つのタプルを順に返すジェネレータ。 例えばこのようなコードがあったとする。
print(list(zip([1,2,3], [4,5,6])))
これは次のように標準出力に出力される。
[(1, 4), (2, 5), (3, 6)]
isinstance(object, class-or-type-or-tuple)
指定したオブジェクトが指定したクラスかその継承クラスである場合にTrueを、それ以外の場合にFalseを返す。また、クラスはタプルの中に記述することで複数指定できる。
isinstance(False, bool)
isinstance(False, (int, str))
id(object)
オブジェクトの識別値(ID)を返す。
算術
abs(number)
絶対値を返す関数

モジュール

以下にいくつか便利なモジュールの使い方を説明する。詳細は情報はhelpコマンドによるドキュメントを参照されたし。

re
正規表現を扱うためのモジュール。
一致
import re
r = re.match("h.*o", "hello")
if r is None:
  print("Not match")
else:
  print("match")
抽出
>>> import re
>>> r = re.match("^\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$", "apple, 100, 5")
>>> if r is not None:
...   r.group(0)
...   r.group(1)
...   r.group(2)
...   r.group(3)
... 
'apple, 100, 5'
'apple'
'100'
'5'
置き換え
>>> re.sub("([^ ,]+)", "hoge", "foo, bar")
'hoge, hoge'
sys
システム系のモジュール。
コマンドライン引数の取得
import sys
print("program name: " + sys.argv[0])
print(" argument 1 : " + sys.argv[1])
print(" argument 2 : " + sys.argv[2])
標準出力
標準エラー出力
import sys
sys.stderr.write("error message\n")
これを利用すると次のようにリダイレクトができる。
import sys
out = sys.stdout
f = open("/tmp/test.txt", "w")
sys.stdout = f
cnt = sys.stdout.write("fuga\n")
print("hoge")
f.close()
exit
import sys
sys.exit(0)
os
システムコールのモジュール。
ディレクトリの生成
import os
os.mkdir("/tmp/hoge", 755)
exec系関数
dup
pipe
fork
import sys
import os

r, w = os.pipe()
pid = os.fork()
if pid:
  os.close(w)
  out = os.read(r, 10)
  while True:
    buf = os.read(r, 10)
    if buf:
      out += buf
    else:
      break
  os.write(sys.stdout.fileno(), out)
  os.close(r)
else:
  os.close(r)
  os.dup2(w, sys.stdout.fileno())
  os.close(w)
  os.execlp("ls", "ls", "-l")


特殊関数

編集中

Python2と3

投稿日:
タグ:

本稿はPython3と2の差異に関するメモ。

編集中

apply
basestring
buffer
callable
dict
except
exec
print
Python2ではexecやprintは文だったが、Python3では関数である。それゆえ、printの文法が異なる。
execfile
exitfunc
filter
map
zip
filterやmap、zipはPython2ではコンテナを返す関数だが、Python3ではジェネレータである。
2
>>> zip([1,2,3], [4,5,6])
[(1, 4), (2, 5), (3, 6)]
3
>>> zip([1,2,3], [4,5,6])
<zip object at 0xb731964c>
funcattrs
future
getcwdu
has_key
idioms
import
imports
imports2
input
intern
isinstance
itertools_imports
itertools
long
metaclass
methodattrs
ne
next
nonlocal文
Python3ではnonlocal文という文が追加された。
nonzero
numliterals
paren
raise
raw_input
reduce
renames
repr
set_literal
standard_error
sys_exc
throw
tuple_params
types
unicode
urllib
ws_comma
xrange
xreadlines

除算
除算(division)または切り捨て除算(floor division)演算は、はじめに型変換によって2つの項の型を統一する。そしてPython2では、除算演算や切り捨て除算演算に関わらず、項が2つとも整数型または長整数型であれば、整数型や長整数型の除算結果を返す。しかしPython3では、除算演算の場合は浮動小数点型を返し、切り捨て除算の場合は2つの項が整数型か長整数型であれば、整数型を返す。

PF メモ

投稿日:
編集日:
タグ:

本稿はBSD系OSのパケットフィルタリングツールPFのメモ。参考にしたのはmanと以下のページである。

実行環境
OSFreeBSD
バージョン10.0
アーキテクチャi386

はじめに

PFを利用するには以下の4つの要素がある。

カーネルの設定
PFやPFのログ機能などを利用するにはカーネルの設定が必要である。私の実行環境では標準でPFやログ機能は利用可能だが、ALTQといわれるキューイング制御のためのフレームワークは利用不能であった。
設定ファイル
PF関連の起動スクリプトの設定(/etc/rc.conf)
pf_enable="YES"	# pfを実行可能に
pflog_enable="YES"		# PF用のログインタフェース
pflog_logfile="/var/log/pflog"	# PF用のログファイルのパスを指定
pf_rules="/etc/pf.conf"		# pfの設定ファイルを指定
/etc/sysctl.conf
net.inet.ip.forwarding=1
PFでパケット転送機能を使用するには前述の設定が必要である。OS起動後にこの値を変更するには、次のような処理を行えば良い。
user% sysctl net.inet.ip.forwarding=1
また、現在の値については以下のようにして取得できる。
user% sysctl net.inet.ip.forwarding
PFの設定(/etc/pf.conf)
以下に例を示す。
# Tables
table <private> const { 127.0.0.1 192.168/16 }
table <hosts1> persist

# Options
set block-policy drop

# Filtering
block in all
pass in from any to <private>
起動スクリプトの実行
PF関連のスクリプトにはpfやpflog、pfsyncなどがある。
  • user% /etc/rc.d/pf start
/dev/pf制御用コマンド(pfctl)
PFはpfctlコマンドを利用して制御できる。以下にいくつか例を示す。
ルールテキストのロード
user% pfctl -f /etc/pf.conf
テーブル操作
user% pfctl -t hosts1 -T add 192.168.1.3
ルールの確認
user% pfctl -s rules
本稿では特にこの3つを重視する。


カーネルの設定

PFやログ機能を利用するには、カーネルがそれに対応していなければならない。

pf
device pf
pflog
device pflog
ALTQ
ALTQ(ALTernate Queueing of network packets)というBSD系OSのネットワークインタフェースでのキューイング制御のためのフレームワークのこと。ALTQには以下に記すようないくつかのオプションがあるが、その全てが必要な訳ではない。各オプションの機能はmanを参照されたし。
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_CDNR
options ALTQ_PRIQ
options ALTQ_NOPCC
options ALTQ_DEBUG
私の実行環境では標準でPFやpflogは利用可能だが、ALTQは利用不能だった。


pf.conf

PFの設定はpf_rulesで設定したファイル、または/etc/pf.confに記述する。pf.confはいくつかの種類の文からなり、それらは次の2種類に分類できる。

  • 変数やアドレスリストの定義
    • Macros
    • Tables
  • ルール行
    1. Options
    2. Normalization
    3. Queueing
    4. Translation
    5. Filtering

pf.confのサンプルを以下に示す。

#Comments #から文末までコメント
#Macros 変数の定義 net_if="alc0"
#Tables アドレスリストの定義 table <default> const { 10/8, 172.16/12, 192.168/16 } table <goodhost> persist # pfctlで追加するリスト
#Options パラメータの設定 set block-policy drop set timeout { icmp.first 20 }
#Normalization パケットの正規化(分断化されたパケットを再構築すること)の制御 scrub in all scrub out all random-id max-mss 1414
#Queueing 帯域幅の制御 queue http on parent std bandwidth 100M default
#Translation NATや他のアドレスへのリダイレクトの制御 # nat|rdr|binat on 〜 rdr on $net_if proto tcp from 192.168.1.4 to 192.168.1.2 port 10080 -> 192.168.1.3 port 80
#Filtering ブロックや通過の設定 #block|pass in|out 〜 block in on $net_if all # ファイアウォールから入るのを防ぐ pass in on $net_if from <default> # default内のアドレスの通過を許可 pass in on $net_if from <goodhost> # goodhost内のアドレスの通過を許可
pf.confの作成や修正が終わったならば、起動スクリプトの再実行("/etc/rc.d/pf restart")や設定ファイルの再読み込み("/etc/rc.d/pf reload")を行えば、ルールが適応される。


変数やアドレスリストの定義

Macros
PFではマクロが利用可能である。
代入
var="string"
評価
$var
マクロはインタフェース、IPアドレスやポート番号などを記述するのに便利である。
myaddr="192.168.1.2"
nif="alc0"
mport="{ 25, 110 }"
また、マクロの機能という訳ではないがインタフェースを()で囲うことでそれに割り当てられたIPアドレスを指定できる。
($nif)
Tables
PFではいくつかのアドレスの集合を表す仕組みにテーブルがある。テーブルはアドレスのリストで、これによっていくつかのアドレスに対してまとめてブロックや通過などを指定できる。アドレスはIPアドレスやネットワークアドレスを指定できる。また、テーブルには、タイプがいくつかある。以下にいくつか例を紹介する。
const
不変なアドレスリスト。以下のように設定ファイルに記述できる。
table <private> const { 10/8,  172.16/12, 192.168/16 }
table <local> const { 127.0.0.1, 192.168.1.15 }
persist
可変なアドレスリスト。以下のように設定ファイルに記述し、
table <badhost> persist
pfctlによって次のように制御できる。
追加
user% pfctl -t badhost -T add 192.168.1.3
削除
user% pfctl -t badhost -T delete 192.168.1.3
コマンドの詳細は"man pfctl"を参照されたし。


ルール行(1)

ルール行は標準では次のような設定に従う。

  • PFはフィルタルールセット(/etc/pf.conf)の先頭から末尾へと順に評価される。
  • 最後に一致したルールが採用される。
  • quickキーワードが付いたルールと一致する場合、そのルールを最終的に一致するルールとなる。
  • デフォルトではパケットは通過するように設定されている。

また、ルール行は次の順番で記述しなければならない。

  1. Options
  2. Normalization
  3. Queueing
  4. Translation
  5. Filtering
ただし本稿ではこの順番ではないので注意されたし。また、はじめに以下の3つを、続けて残りの要素を記す。
  • Filtering
  • Translation
  • Options
(Packet) Filtering
ファイアウォールの中や外への通信の遮断(block)や通過(pass)などを制御。組合せは以下の4つである。
inout
blockblock inblock out
passpass inpass out
そして、それにネットワークインタフェースの指定や宛先や送信元の指定することで細かな制限が可能である。例えば、ファイアウォールの外部のネットワークの"183.77.202.242"から内部の"192.168.1.2"への通信を遮断するには次のように記述する。
block in from 183.77.202.242 to 192.168.1.2
Filteringの行の書き方は以下のような流れである。
block/pass [quick] [on interface] in/out [log] from 〜 to 〜
指定するIPアドレスは直接記述したりTablesで定義したテーブルを指定したりできる。また、全てのアドレスを指す場合は"any"というキーワードが使用でき、"from any to any"と記述する場合は"all"と書いても良い。
block block-policy
通信を遮断。blockはblock-policyの設定に基づいて動作が変化する。blockのblock-policyを省略した場合の設定はOptionsで設定できる。block-policyには次のいずれかを設定できる。
drop
パケットは黙って破棄。
return
ブロックされたTCPパケットに対してTCP RST パケットが返され、 他のすべてのパケットに対しては ICMP UNREACHABLE パケットが返されます。
block-policyは次のようにして指定する。
block drop on all
また、Optionsでデフォルトを設定する場合、次のように記述する。
set block-policy drop
pass
パケットを通過。
route-to
replay-to
dup-to
fastroute
キーワードには次のようなものがあり、基本的に他のルール行と共通である。
<キーワードの一例>
inファイアウォールの中への通信か外への通信
out
logログを記録(pflogは通常/var/log/pflogにtcpdumpのバイナリ形式で記録される。ログの保存先はrc.confのpflog_logfileで指定可能)
on interfaceネットワークインタフェース
proto protocolプロトコル名。/etc/protocolsに記述されているプロトコル。
inetIPv4/v6
inet6
from送信元
to宛先
any全てのアドレス
allfrom any to anyのこと
portポート番号(TCPやUDP通信などの場合)
quick一致する場合それを最終的に一致するルールとする
以下に実際のFilteringルールの例を紹介する。
<サンプルルール>
re0を通したファイアウォール内の192.168.1.21のTCPの110番(POP3)に対するアクセスをブロック
block in on re0 inet proto tcp from any to 192.168.1.21 port 110
または
block in on re0 inet proto tcp from any to 192.168.1.21 port = 110
ポートの指定には次の記号が利用可能。
port = xxと等しい
port != xxと等しくない
port < xx未満
port <= xx以下
port > xxより大きい
port >= xx以上
port x:yx以上y以下
port x >< yx未満でかつyより大きい
port x <> yx未満かyより大きい
また、IPアドレスと同様にリストが使用できる("{ 22, 80 }")。
re0を通したファイアウォール内の192.168.1.21のTCPかUDPの110番(POP3)に対するアクセスをブロック
block in on re0 inet proto {tcp, udp} from any to 192.168.1.21 port = 110
指定したアドレス(badhost)からファイアウォール内へのアクセスをブロック
table <badhost> persist
block in from <badhost>
テーブル<badhost>へのアドレスの追加は次のように行う。
user% pfctl -t badhost -T add 192.168.1.3
ファイアウォール内への全てのICMPアクセスをブロック
block in inet proto icmp all
ログを記録
block in log inet proto icmp all
ログはpflogによって制御され、通常/var/log/pflogにtcpdumpのバイナリ形式で記録される。icmpであれば、次のようなコマンドでログを取得できる。
user% sudo tcpdump -n -r  /var/log/pflog icmp
reading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)
00:13:43.605085 IP 192.168.1.4 > 192.168.1.2: ICMP echo request, id 7202, seq 1, length 64
00:13:44.612563 IP 192.168.1.4 > 192.168.1.2: ICMP echo request, id 7202, seq 2, length 64
00:13:45.620305 IP 192.168.1.4 > 192.168.1.2: ICMP echo request, id 7202, seq 3, length 64
また、次のようなpop3をブロックするログがあるとする。
block in log on re0 inet proto tcp from any to 192.168.1.21 port 110
この時、このログは次のようにして取得できる。
user% sudo tcpdump -n -r /var/log/pflog port pop3
reading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)
00:25:00.624451 IP 192.168.1.4.51868 > 192.168.1.2.110: Flags [S], seq
 1646204942, win 14600, options [mss 1460,sackOK,TS[|tcp]>
00:25:01.622446 IP 192.168.1.4.51868 > 192.168.1.2.110: Flags [S], seq
1646204942, win 14600, options [mss 1460,sackOK,TS[|tcp]>
00:25:03.627292 IP 192.168.1.4.51868 > 192.168.1.2.110: Flags [S], seq
1646204942, win 14600, options [mss 1460,sackOK,TS[|tcp]>
Translation
宛先や送信元のアドレスの変更やポート番号などを制御。
rdr
リダイレクトまたはポートフォワーディング。あるパケットを別のIPアドレスや別のポート番号に転送する。例えば"192.168.1.4"から"192.168.1.2"のポート番号10080へのTCP通信を"192.168.1.3"のポート番号80に転送する場合、次のように記述する。
rdr proto tcp from 192.168.1.4 to 192.168.1.2 port 10080 -> 192.168.1.3 port 80
また、直接IPアドレスを指定せずインタフェースを()で囲えば、そのインタフェースに割り当てられたIPアドレスを指定できる。
rdr proto tcp from any to ($nif) -> 192.168.1.5
nat
NATまたはNAPT。あるパケットのアドレスやポート番号を変更する。例えば"192.168.1.2"からの通信を"192.168.1.100"に変更する場合、次のように記述する。
nat on alc0 from 192.168.1.2 to any -> 192.168.1.100
->の右辺は以下のようにインタフェース名を指定することもできる。
nat on alc0 from 192.168.1.2 to any -> alc1
rdrとは異なり、->の右辺で指定するのは宛先ではなく送信元である。また、宛先ポート番号は指定できないが、送信元ポート番号を指定できる。NAPTは次のように行う。
nat on re0 proto tcp from 192.168.1.2 port 80 to any -> 192.168.1.100
natの状態はpfctlで確認可能である。
user% pfctl -s nat
なお変更後のアドレスには任意のアドレスを利用できるが、通信が成り立つためには相手が経路を確保できることとこちらのインタフェースがそれを取得できることが必要である。例えばローカルネットワーク"192.168.1.0"内で、"192.168.1.2"と"192.168.1.3"のマシンが通信を行い、"192.168.1.2"を"192.168.1.100"に変更する場合、それぞれのマシンで次のような処理が必要である。
192.168.1.2のマシン
pf.confに次の行を追加。
nat on re0 from 192.168.1.2 to any -> 192.168.1.100
インタフェースに"192.168.1.100"をエイリアスし、受信可能にする。
user% ifconfig re0 alias 192.168.1.100
192.168.1.3のマシン
192.168.1.2のMACアドレスが"01:23:45:67:89:ab"の時、192.168.1.100に対応するMACアドレスをそれに対応づける。
user% arp -s 192.168.1.100 01:23:45:67:89:ab -i eth0
binat
双方向マッピング。ある2つのアドレスやポート番号を対応させる。例えば192.168.1.1と192.168.1.5を対応付けるには次のように記述する。
binat on re0 from 192.168.1.1 to any -> 192.168.1.5
Translationの特定の通信だけを一致させないために、"no"というキーワードがある。例えば特定のアドレス("192.168.1.4")への通信の場合にNATを行わない場合、次のように記述する。
no nat on re0 from 192.168.1.2 to 192.168.1.4
nat on re0 from 192.168.1.2 to any -> 192.168.1.100
なおこのような場合最初に一致したルールが最終的な一致となる。
Options
PFや通信に関する基本的な情報を設定。以下にサンプルを示す。
set block-policy drop
set timeout { icmp.first 20 }
詳しくは"man pf.conf"を参照されたし。
timeout
loginterface
limitパケットフィルタで使用するメモリプールの限界値。
ruleset-optimization
optimization
block-policyFilterのblockでblock-policyを省略した場合のblock-policy。
state-policy
hostid
require-order
fingerrpints
skip on
debug


ルール行(2)

編集中

Traffic Normalization
分断化されたパケットを再構築(正規化)する際の制御。詳しくは"man pf.conf"を参照されたし。
scrub in all
scrub out all random-id max-mss 1414
Queueing


pfctl

pfctlの詳細は"man pfctl"で閲覧できる。以下にいくつかpfctlの機能を紹介する。

ルールテキストのロード
user% pfctl -f /etc/pf.conf
テーブル操作
user% pfctl -t hosts1 -T add 192.168.1.3
ルールの確認
user% pfctl -s rules
PFを有効に
user% pfctl -e
PFを無効に
user% pfctl -d


メモ

  • タグ

UML2.4(1)

投稿日:
タグ:

UML2.4

構造
ダイアグラム名特徴
オブジェクト図オブジェクト図とは、システムのある時点におけるオブジェクトの静的なスナップショットを表す図。
クラス図クラス同士の静的な関係を表現した図。オブジェクト図を抽象化して記述できる。
コンポジット構造図クラスやコンポーネントの内部構造(クラスやコンポーネント)を表す図。
コンポーネント図ソフトウェアを部品に分解した場合の全体構成を表現する図。
パッケージ図パッケージとパッケージ間の関係を表現する図。
配置図システムの物理的な構成を表現する図。
プロファイル図
動作
ダイアグラム名特徴
アクティビティ図
ユースケース図
シーケンス図
コミュニケーション図
ステートマシン図
タイミング図
相互作用概要図
プログラミング言語よりも抽象的な表現が可能であり、 プログラミング以外にも表現できる。

UML2.4(3) -クラス図-

投稿日:
修正日:
タグ:

クラス図

クラス図とは、クラス同士の静的な関係を表現した図である。オブジェクト図が処理の流れのある瞬間の関係を表しているのに対し,クラス図は処理の流れ全体を通した関係を示している。クラス図で表現する関係は以下の通りである。

  • 汎化
  • 実現
  • 関連
    • 誘導可能性
    • 集約
    • コンポジション
  • 依存
名称意味
クラス
  • 属性
  • 操作
classA
  • + varA
  • - varB
  • # varC
  • ~ varD
  • + funcA()
  • - funcB()
  • # funcC()
  • ~ funcD()
クラス
オブジェクトの共通部分を抜き出して抽象化したもの。クラスを実体化させたもののことをオブジェクトまたはインスタンスという。クラス図では、クラスはクラスやそのインスタンスを表すのに使用される。
属性
クラスが持つ性質のこと。
操作
クラスが持つ振る舞いのこと。アクセッサ(ゲッタ/セッタ)やコンストラクタの記述は省略される場合がある。
可視性
UMLアクセス指定子(Java)意味
+public全てのクラスからアクセス可能
-private自分のクラスからだけアクセス可能
#protected自分のクラスと継承クラスからのみアクセス可能
~同一パッケージ内からアクセス可能(package)
ローカル可視性
メソッド内だけで繋がること。
属性可視性
オブジェクトが存在している間繋がる長期的な関係のこと。
パラメータ可視性
メソッドの引数として受け取った際だけに繋がる一時的な関係のこと。

インタフェース
Javaのインタフェースを表現する場合、クラス名を<<Interface>>のようにステレオタイプ(<<type>>)で表現。
操作や属性の型を表現する場合、:で区切って右辺に記述する。
  • + 山田太郎 : 人
  • - var : int
  • + func() : boolean
  • + func(arg1 : int) : boolean
初期値
初期値は次のように=を使って表す。
  • + りんごの数 : 非負数 = 5
集約集合-要素の関係。例えば"A has a B"の時,AはBを集約するという意味となる。関連の一種で,JavaではAの属性としてBがある場合にこの記号で表す。
コンポジション集約の中でも強い関係のこと。 例えば"A is part of B"の時,コンポジションで表す。Javaでは,集合クラスのインスタンスが消滅すると、要素クラスのインスタンスも消滅するような関係の時にコンポジションで表現する。
関連
2つのクラスのインスタンス間に長期的な繋がりがあること。Javaでは属性として相手のオブジェクトを持つような場合に関連とする。集約やコンポジションがどちらのクラスの属性なのか明示しているのに対して,関連はどちらかの属性である場合や,それぞれ対等な関係の場合に関連で表現する。
誘導可能性関連がある2つのクラスのインスタンスで一方のクラスがもう一方のクラスを参照(メッセージを送信)できるか否か。参照できる場合は矢印、できない場合は✕印、未定義の場合はそのどちらでもない。Javaで使用する場合,例えば"A has a B"の関係で,かつCがBを属性として保持しなければならない場合で,概念的にはCとBが集約関係でない場合は,誘導可能性で"C→B"のように表現する。これにより,開発者はどちらのクラスの属性かを判断できる。
関連端(ロール)名 属性を関連で表現した場合,属性名や可視性は関連端名で表現する。
多重度 あるクラスの複数のインスタンスと別のクラスのインスタンスに関連がある時、それをまとめることができる。数は次のように表現する。
11
*0以上
1..31以上3以下の範囲
2..*2以上
例えばJavaで配列やコレクションを使用する際に多重度でまとめて表現できる。
依存 2つのクラスが一時的な関係がある時に依存で表現する。Javaでは,引数として一時的にそのクラスを利用するような場合やローカル変数で利用する場合に依存で表現する。例えばクラスAのあるメソッドがクラスBを引数として使用する場合に,"A→B"の方向で記述する。
汎化抽象-具象の関係(class)。 Javaでは"class B extends A"の時,"A→B"の方向で記述する。
実現抽象-具象の関係(interface)。Javaでは"class B implements A"の時,"A→B"の方向で記述する。

以下のコードは記事『UML2.4(2) -オブジェクト図-』 と同じものである。

import java.util.ArrayList;

class Guest {
  /*- 属性 -*/
  private ApplicationForm memAddAF;

  /*- 操作 -*/
  public Guest(){
    memAddAF = new ApplicationForm("鈴木太郎", "ZZZZZ", 012345, "XXX");
  }
  public ApplicationForm getMemAddAF(){return memAddAF;}
}


class ApplicationForm {
  /*- 属性 -*/
  public String name;
  public String address;
  public int tel;
  public String work;

  /*- 操作 -*/
  public ApplicationForm(String name, String address, int tel, String work){
    this.name = name;
    this.address = address;
    this.tel = tel;
    this.work = work;
  }
}


class Receptionist {
  /*- 属性 -*/

  /*- 操作 -*/
  public void postApplicationForm(ApplicationForm memAddAF){
    // 受付処理
  }
}

class Sample {
  /*- 属性 -*/

  /*- 操作 -*/
  /** エントリポイント */
  public static void main(String args[]){
    Guest suzuki = new Guest();
    Receptionist receptionist = new Receptionist();
    receptionist.postApplicationForm(suzuki.getMemAddAF());
  }
}

これをクラス図で抽象的に表現すると次のようになる。

UML2.4(5) -コンポーネント図-

投稿日:
タグ:

コンポーネント図

コンポーネント図とは、コンポーネントの構造を表現するダイアグラムであり、ソフトウェアを部品に分解した場合の全体構成を表現する。コンポーネント図は、各部品の内部構造には着目せず、外部に公開している機能、外部に要求している機能だけに着目する。

名称意味
コンポーネント
  • 《component》
  • コンポーネント名
コンポーネントは入れ子構造であり、内部にいくつかのコンポーネントを記述できる。
  • 《component》
  • コンポーネント名
インタフェース
提供インタフェース
外部に公開する機能を表す。白丸(ボール)で表記。
要求インタフェース
外部に要求する機能を表す。半円(ソケット)で表記。
実現 あるコンポーネントが別のコンポーネントを利用していることを表す。

UML2.4(8) -プロファイル図-

投稿日:
タグ:

プロファイル図

UMLの内容を独自に拡張するためのダイアグラムで、メタモデルの構築が行える。

名称意味
メタクラス
《Metaclass》
メタクラス
クラスのためのクラスのこと。
ステレオタイプ
《stereotype》
ステレオタイプ
モデル要素に特別な意味をもたせ、特定の用途にモデル要素を拡張して使用することを表す。
プロファイル
《profile》
プロファイル
UMLを拡張するための特殊パッケージ。
《profile》
プロファイル
拡張 メタクラスの拡張。
プロファイル適用 外部のプロファイルの適用。

UML2.4(11) シーケンス図

投稿日:
タグ:

シーケンス図

シーケンス図とは,オブジェクト(クラス)間の相互関係を時系列的に表現したダイアグラムのことである。 シーケンス図の構成要素は次の通りである。

実行指定
オブジェクトの操作が呼び出されている時間。
ライフライン
使用されていないがオブジェクトがまだ存在していることを表す。
メッセージ
同期メッセージ
呼び出し先の処理が終わる(返事が返ってくる)のを待って,処理を行う場合のメッセージパッシング。
リプライメッセージ
返事。処理が終わったことを伝えるためのメッセージ。
reply
非同期メッセージ
呼び出し先の処理を待たずに処理の継続や処理の終了を行う場合のメッセージパッシング。
複合フラグメント
種類説明
loop繰り返し文
alt条件文
ステレオタイプ

分析

概念クラス図
概念シーケンス図

設計

実装クラス図
実装シーケンス図

実装

Book.java
class Book {
  /*- 属性 -*/
  /** タイトル。 */
  private String title;
  /** 著者。 */
  private String author;

  /*- 操作 -*/
  /**
   * コンストラクタ。
   * @param title タイトル。
   * @param author 著者。
   */
  Book(String title, String author){
    this.title = title;
    this.author = author;
  }

  /**
   * 本のタイトルを取得。
   * @return 本のタイトル。
   */
   public String getTitle(){
     return this.title;
   }

  /**
   * 本の著者を取得。
   * @return 本の著者。
   */
   public String getAuthor(){
     return this.author;
   }
}
Library.java
import java.util.*;

class Library {
  /*- 属性 -*/
  /** 図書館名。 */
  private String name;
  /** 住所。 */
  private String address;
  /** 本リスト。 */
  private ArrayList<Book> book_list;


  /*- 操作 -*/
  /**
   * コンストラクタ。
   * @param name 図書館名。
   * @param address 住所。
   */
  public Library(String name, String address){
    this.name = name;
    this.address = address;
    this.book_list = new ArrayList<Book>();
  }

  /**
   * 本を借りる。
   * @param title 借りたい本のタイトル。
   */
  public Book lendBook(String title){
    Book book = searchBook(title);
    if (book!=null){
      this.book_list.remove(book);
      return book;
    }else{
      return null;
    }
  }
  
  /**
   * 本を追加する。
   * @param book 追加する本
   */
  public void addBook(Book book){
    this.book_list.add(book);
  }

  /**
   *
   */
  private Book searchBook(String title){
    Iterator<Book> it = this.book_list.iterator();
    while (it.hasNext()){
      Book book = it.next();
      if (book.getTitle()==title){
        return book;
      }
    }
    return null;
  }
}
Client.java
class Client {
  /*- 属性 -*/
  private Library lib;
  
  /*- 操作 -*/
  /** エントリポイント */
  public static void main(String args[]){
    Client client = new Client();
    client.init();
    client.exec();
  }
  /** デフォルトコンストラクタ。 */
  public Client(){
  }
  /** 1つ目の実行指定 */
  private void init(){
    final String[][] BOOKS = {{"たいとるA", "山田太郎"},
                              {"たいとるB", "鈴木次郎"},
                              {"たいとるC", "佐藤三郎"}};

    lib = new Library("市立図書館", "XXX-YYY ○○市△△町1-1-1");
    
    for (String[] book : BOOKS){
      lib.addBook(book[0], book[1]);
    }
  }
  /** 2つ目の実行指定 */
  private void exec(){
    lib.lendBook("たいとるB");
  }
};

Docker メモ

投稿日:
タグ:

Dockerとは

Linux用のオープンソースのソフトウェアコンテナ(コンテナ)のこと。類似したソフトにはLXCやFreeBSDのjailなどがある。


インストール(Debianの場合)

  1. APTのデータ取得元の設定リストにURLを追加 (以下jessieの場合)。
    /etc/apt/sources.list
    • deb https://apt.dockerproject.org/repo debian-jessie main
      
    • deb https://apt.dockerproject.org/repo debian-wheezy main
  2. パッケージを更新する。
    sudo apt-get update
  3. Dockerをインストールする。
    sudo apt-get install docker-engine
  4. デーモンが実行していない場合、dockerデーモンを起動する。
    sudo service docker start
apt-getでhttpsを利用する

apt-getはデフォルトではhttpsを利用できない(はず)。

E: メソッドドライバ /usr/lib/apt/methods/https が見つかりません。
apt-getで利用可能なプロトコルは、以下のようにして確認できる。
ls /usr/lib/apt/methods

対応してない場合、httpsのプロトコルと証明書を追加する。

sudo apt-get install apt-transport-https ca-certificates
APTキー管理ユーティリティで、公開鍵の設定を行う。
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

失敗談

はじめ、Dockerをインストールしようとして、

sudo apt-get install docker
とやって実行を試みた。
sudo docker run hello-world
すると結果は、
Segmentation Fault or Critical Error encountered. Dumping core and aborting.

このDockerはドッキングアプリケーションのことである。詳しくは"aptitude show docker"とかで確認できる。


とりあえず実行する

現在管理しているイメージはimagesで確認できる。

user% sudo docker images
デフォルトでは、Welcomメッセージを表示するhello-worldイメージが用意されている(はず)。

イメージは、runコマンドで指定したコンテナを実行できる。

user% sudo docker run hello-world
コンテナ名を指定しない場合、デフォルトで"nostalgic_almedia"というコンテナが使用される。

実行中のコンテナは、psで確認できる。

user% sudo docker ps -a
シェルのような対話的なコマンドをコンテナで実行するには、-itオプションを使用する。
user% sudo docker run -it ubuntu:14.04 /bin/bash
ubuntuのイメージがない場合、イメージをダウンロードしてから実行する。

また、--nameでコンテナに名前をつけることができる。

user% sudo docker run --name="test" ubuntu /bin/bash
user% sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
86f74cf40355        ubuntu              "/bin/bash"         18 seconds ago      Exited (0) 16 seconds ago                             test

Bashからexitで抜けて、ホストに戻った後、imagesをもう一度実行すれば、イメージが増えているのが分かるだろう。

user% sudo docker images


停止中のコンテナを再開する

  1. コンテナを確認する。
    user% sudo docker ps -a
    CONTAINER ID        IMAGE               COMMAND		CREATED              STATUS                      PORTS		NAMES
    9fec70d35bde        ubuntu              "/bin/bash"     About a minute ago   Exited (0) 14 seconds ago            	sleepy_williams
  2. (停止中の)コンテナを実行する。
    user% sudo docker start 9fec70d35bde
    startは以下のように使用する。
    user% sudo docker start [オプション] コンテナ名|コンテナID ...
  3. 実行中のコンテナでコマンドを実行する。
    user% sudo docker exec 9fec70d35bde ls
    user% sudo docker exec オプション コンテナ名|コンテナID コマンド
  4. 実行中のコンテナをアタッチする。
    user% sudo docker attach 9fec70d35bde

コンテナからイメージを作成

  1. コンテナを確認する。
    user% sudo docker ps -a
    CONTAINER ID        IMAGE               COMMAND		CREATED              STATUS                      PORTS		NAMES
          9fec70d35bde        ubuntu              "/bin/bash"     About a minute ago   Exited (0) 14 seconds ago            	sleepy_williams
  2. コンテナからイメージを作成する。
    user% sudo docker commit 9fec70d35bde hoge
  3. イメージを確認する。
    user% sudo docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED		SIZE
    hoge                latest              8a2222fb559e        5 seconds ago       188 MB
    ubuntu              14.04               07c86167cdc4        4 days ago		188 MB
  4. 作成したイメージからコンテナを作成し、実行する。
    user% sudo docker run hoge
  5. コンテナを確認する。
    user% sudo docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED	STATUS                     PORTS               NAMES
    937b91f51708        hoge                "/bin/bash"         9 seconds ago       Exited (0) 7 seconds ago	               elegant_northcutt
    9fec70d35bde        ubuntu              "/bin/bash"         31 minutes ago      Up 13 minutes                                  sleepy_williams

基本コマンド

docker コマンド オプション
ps
実行中のコンテナの一覧を表示。
run
新しくコンテナを実行。
rm
コンテナを削除。
images
取得したイメージの一覧を表示。
rmi
取得したイメージを削除。
search
Docker Hubからイメージを検索する。
stop
実行中のコンテナを停止させる。
commit
コンテナからイメージを作成する。
docker run イメージ名
新しくコンテナを実行する。
docker images
ダウンロード済みのイメージの一覧を表示する。
docker ps -a
実行されたコンテナの情報を表示する。
docker save イメージ > tarファイル
docker save -o tarファイル
docker save --output tarファイル
イメージをtarファイルに保存する。saveとloadはセットで覚えた方が良い。xzやgzipと組み合わせれば、tar.gzやtar.xzなども作成可能。
docker load イメージ < tarファイル
docker load -i tarファイル
docker load --input tarファイル
保存したtarファイルからイメージを読み込む。

忘れた時は...

サブコマンドの確認..
sudo docker
サブコマンドの使い方の確認..
sudo docker サブコマンド --help

その他

管理者権限なしでdockerを使用する
sudo groupadd docker
sudo gpasswd -a ユーザ docker

メモ

  • 3.14以前のLinuxカーネルではPAMを利用できない。

RDBとSQL 応用

投稿日:
タグ:

DBやSQL関連の中級以上向け技術のメモ。

三層スキーマ

データベースの構造・スキーマを3階層に分けて、定義する方式がある。

三層スキーマ
外部スキーマ
概念スキーマ
内部スキーマ
内部スキーマ
DBの実態。データを効率的に管理するための物理設計。DBもデータである以上、ファイルとして管理される。DB開発者が考えるスキーマ。
概念スキーマ
テーブル。データを一意に効率的に管理するための設計。DB設計者が考えるスキーマ。
キーワード:正規化, ER図
外部スキーマ
テーブルやビュー、SELECTの結果。ユーザ視点のもので、実際にデータを使用する際に考えるスキーマ。

縦持ちと横持ち

生徒のテストの点数を管理する場合、エクセルのような表計算ソフトを使用するヒトは、以下のような表を作成するだろう。

名前
佐藤太郎5060567060
鈴木二郎6055705570
田中三郎4070806080
横持ち

このような表を横持ちテーブルという。1つのレコードに対して各フィールドに必ず1つの値が入る場合、有効な設計である。しかしRDBでは横持ちテーブルは、列間の独立性が高く、常に良いとは限らない。横持ちテーブルに対し、以下のようなテーブルを縦持ちテーブルという。

名前科目点数
佐藤太郎50
佐藤太郎60
佐藤太郎56
佐藤太郎70
佐藤太郎60
鈴木二郎60
鈴木二郎55
鈴木二郎70
鈴木二郎55
鈴木二郎70
田中三郎40
田中三郎70
田中三郎80
田中三郎60
田中三郎80
縦持ち

一見すると、縦持ちテーブルは横持ちテーブルに比べて、可読性が低く、かつデータが重複しているため、効率が悪いように見えるかもしれない。しかし、RDBでは縦持ちテーブルの方が効率が良い場合がある。

横持ちテーブルで科目数を増減したい場合、列を追加する必要がある。RDBでは列の追加は、ALTER文を使用してテーブル設計を変更する必要がある。これに対し、縦持ちテーブルはINSERT文だけで追加可能である。すなわち、テーブルを再設計するコストがない。

横持ちテーブル
ALTER TABLE テーブル ADD 美 int;
ALTER TABLE テーブル DROP 美;
縦持ちテーブル
INSERT INTO テーブル VALUES ('衛宮四郎', '美', 50);
DELETE FROM テーブル WHERE 科目='美';

更に、横持ちテーブルでこのような変更を行った場合、各フィールドにデータが入らないケースが発生する(すなわちnull)。そうなると、列ごとの何らかの処理を行う場合、nullを除外するような処理を記述しなければならなくなる(例:COALESCEを使う)。

また、データ処理においても、縦持ちテーブルは優れた性質を持つ。生徒ごとの合計点を計算する場合、縦持ちテーブルと横持ちテーブルではそれぞれ次のように記述する。

横持ちテーブル
SELECT 名前,国+数+社+理+英 FROM テーブル GROUP BY 名前;
縦持ちテーブル
SELECT SUM(点数) FROM テーブル WHERE 名前="佐藤太郎"

このように、縦持ちテーブルは、ある関連するデータの値が0個〜複数個ある場合に、横持ちテーブルよりも拡張性に優れている。

しかしその反面、縦持ちテーブルは横持ちテーブルに比べて、レコード数が増加しやすい。RDBにおいて、レコード数が多いと、検索や処理速度の低下を招くため、前述のようなケースでも必ず縦持ちテーブルが良いとは限らない。例えば前述の横持ちテーブルは、1つのUPDATE文やINSERT文で5教科の更新や追加をできるのに対し、縦持ちテーブルではそれぞれ5回発行しなければならない。

横持ち→縦持ち

以下のようなテーブルがあったとする。

名前ニックネーム1ニックネーム2ニックネーム3
佐藤太郎タローtaro3たろっち
鈴木二郎ジロー2ronull
このテーブルは1つのINSERT文やUPDATE文で1レコードを更新できるが、前述した通り何らかの処理をする場合は縦持ちの方が都合が良いことがある。このような場合、UNIONを使うと横持ちテーブルを縦持ちに変換できる。
SELECT ニックネーム1 AS ニックネーム FROM テーブル WHERE 名前 ='佐藤太郎' UNION
SELECT ニックネーム2 AS ニックネーム FROM テーブル WHERE 名前 ='佐藤太郎' UNION
SELECT ニックネーム3 AS ニックネーム FROM テーブル WHERE 名前 ='佐藤太郎';
ニックネーム
タロー
taro3
たろっち
※別にWHEREは結合後につけても結果自体は変わらないが、性能が同じとは限らない。

前述の三層スキーマでも述べたように、SQLでは実態のテーブル(概念スキーマ)と実際に扱うデータ構造(外部スキーマ)を分けることができる。

縦持ちから横持ちテーブルへの変換については後述する。


ビュー

頻繁に使用するSELECT文は、ビューを作成することで、擬似的なテーブルとして利用できる。 例えば前述のテーブルに以下のようなSQL文を発行する。
CREATE VIEW ニックネームテーブル (名前,ニックネーム) AS
SELECT 名前,ニックネーム1 FROM テーブル UNION
SELECT 名前,ニックネーム2 FROM テーブル UNION
SELECT 名前,ニックネーム3 FROM テーブル;
そうすると次のようなビューが作成される。
名前ニックネーム
佐藤太郎タロー
佐藤太郎taro3
佐藤太郎たろっち
鈴木二郎ジロー
鈴木二郎2ro
鈴木二郎null

CASE式

プログラミング経験者のSQL初心者は、はじめてSQLを使用するとSQLはただデータを格納するためだけに使い、データの整理はプログラミングでどうにかしようとしたくなる(少なくとも私はそうだった)。

例えば以下のような学校があったとする(Appendixに初期化用のSQL文を記載)。

クラス
1-A1512
1-B1410
2-A1120
2-B2110
3-A1516
3-B1415
この時、運動会で全校を対象に同じ組同士でチームを組む場合、各色のチーム人数を調べる場合、例えばRubyでは次のように記載できる。
db = func()
# func()は「select * from sample_table」の結果を受け取る。
# [
# {"クラス"=> "1-A", "男"=>15, "女"=>12}, 
# {"クラス"=> "1-B", "男"=>14, "女"=>10}, 
# {"クラス"=> "2-A", "男"=>11, "女"=>20}, 
# {"クラス"=> "2-B", "男"=>21, "女"=>10}, 
# {"クラス"=> "2-A", "男"=>15, "女"=>16}, 
# {"クラス"=> "3-B", "男"=>14, "女"=>15}
# ]

red = 0
white = 0
db.each do |x|
  case x["クラス"]
  when "1-A", "2-A", "3-A"
    red += x["男"] + x["女"]
  when "1-B", "2-B", "3-B"
    white += x["男"] + x["女"]
  end
end
puts "赤:#{red} ホワイト:#{white}"
この方法では、DBのデータを全て取得した後にアプリがもう一度全てのデータにアクセスしている。しかし、この程度の処理であれば、DBがデータにアクセスしながら値を整理し、アプリはその結果を出力するだけで良い。
SELECT
  CASE class WHEN '1-A' THEN '赤'
             WHEN '2-A' THEN '赤'
             WHEN '3-A' THEN '赤'
             WHEN '1-B' THEN '白'
             WHEN '2-B' THEN '白'
             WHEN '3-B' THEN '白'
             END,
  SUM(population)
from sample_table
  GROUP BY CASE class WHEN '1-A' THEN '赤'
             WHEN '2-A' THEN '赤'
             WHEN '3-A' THEN '赤'
             WHEN '1-B' THEN '白'
             WHEN '2-B' THEN '白'
             WHEN '3-B' THEN '白'
             END;
実行結果
168
178

CASE式を覚えることで、性能改善や利便性の向上を図ることができる。ただし、CASE式や結合処理はDBの負荷を上げるため、アプリ側で対処するケースもあるため、そこは状況に応じた使い分けが必要である。


縦持ち→横持ち
CASE式を使用すると、縦持ちテーブルを横持ちテーブルに変換できる。例えば以下のようなテーブルがあったとする。
クラス性別人数
1-A15
1-A12
1-B14
1-B10
2-A11
2-A20
2-B21
2-B10
3-A15
3-A16
3-B14
3-B15
縦持ち
この時、次のようなSQL文を実行することで横持ちテーブルに変換できる。
SELECT class,
       sum(CASE WHEN sex='男' THEN population ELSE 0 END) as '男',
       sum(CASE WHEN sex='女' THEN population ELSE 0 END) as '女'
FROM sample_table GROUP BY class;

制約

-編集中-
PRIMARY KEY制約
UNIQUE制約
NOT NULL制約
CHECK制約

正規化

データの重複をなくし、整合的にデータを取り扱えるデータベースを設計することを正規化という。 正規化は段階に合わせて6つに分類される。

正規化(データの整合性)とパフォーマンスは基本的にトレードオフの関係にある。そのため、必ずしも段階が高ければ良いとはいう訳ではなく、状況に応じて使い分けるのが望ましい。

なお、一般的には第3正規化まで行っている設計が多い。

第1正規化
以下の条件を満たしたテーブルにすること。
  • 1つのセルに1つのデータのみ。
  • レコードを一意に特定するキー(主キー:PK)がある。
非正規形のままではRDBで扱うことができない。 言ってみれば第1正規化とはRDBに格納可能な形にすることである。
1つのセルに1つのデータのみ。
名前子供
田中一郎太郎
花子
伊藤五郎null
名前子供
田中一郎太郎
田中一郎花子
伊藤五郎null
レコードを一意に特定するキー(主キー:PK)がある
例1)主キーが名前の場合
名前
佐藤太郎5060567060
鈴木二郎6055705570
田中三郎4070806080
横持ち
例2)主キーが名前と科目(複数)の場合
名前科目点数
佐藤太郎50
佐藤太郎60
佐藤太郎56
佐藤太郎70
佐藤太郎60
鈴木二郎60
鈴木二郎55
鈴木二郎70
鈴木二郎55
鈴木二郎70
田中三郎40
田中三郎70
田中三郎80
田中三郎60
田中三郎80
縦持ち
キー
キーには、ナチュラルキーサロゲートキーの2種類がある。前述したようなあるデータモデルから一意に特定できる要素を見出して、それをキーとしたものを、ナチュラルキー、データモデルにはないが、一意に特定するために新しく追加したキーをサロゲートキーという。例えば前述の縦持ちテーブルのPKをサロゲートキーにすると、次のようになる。
ID名前科目点数
01佐藤太郎50
02佐藤太郎60
03佐藤太郎56
04佐藤太郎70
05佐藤太郎60
06鈴木二郎60
07鈴木二郎55
08鈴木二郎70
09鈴木二郎55
10鈴木二郎70
11田中三郎40
12田中三郎70
13田中三郎80
14田中三郎60
15田中三郎80
サロゲートキーを使用するメリットには、次のようなものがある(ミックさん談)。
  1. そもそも主キーにできる項目がない場合(重複してる)
  2. 主キーの値が使いまわされる場合
  3. 主キーの体系が変化する場合
2と3の例には、商品コードと商品を扱う場合に、商品コードが再利用されるような場合がある。 商品コードをナチュラルキー(PK)とした場合、レコードを上書きするしかないが、別にキーを設定することで、新たに追加するだけで良い。
第2正規化

主キーの一部から非キーが一意に特定できる場合、テーブルが分けること。第2正規化とは、第1正規形から部分関数従属性を取り除くことである。

関数従属性
ある属性(列)の値が決まる時、別の属性が一意に決まること。
部分関数従属性
キーの一部に非キー属性が関数従属している状態のこと。

言ってみれば、第2正規化とは情報からエンティティ毎に整理することである。 大雑把に説明すると、例えばサッカーのグループリーグの情報を表すDBがあり、選手を起点に考える。

  • 選手の名前(キー)
  • 選手の身長
  • 選手の体重
  • 選手の所属チーム
  • 所属チームのホームグラウンド
  • 所属チームの勝利数
  • 所属チームの敗北数
第2正規形でない第1正規形は、選手と所属チームという2つのエンティティをひとまとめに管理しているが、これらを別のものとして分けるのが第2正規形である。前述の一覧でいえば、「〜の」でテーブルを分割ことである。

RDBではキーがなければレコードを作成できない。そのため、第2正規形でない第1正規形のように1つのテーブルにエンティティが混在する場合、全体の主キーが決まらなければレコードが作成できない。また、同一のエンティティを複数のレコードで管理するため、データの整合性を保持するのが難しい。これを解消したものが第2正規形である。

第3正規化

第2正規形から推移関数従属性を取り除くこと。非キーから別の非キーを一意に特定できる場合、それをテーブルに分けること。

言ってみれば、第3正規化とはエンティティの情報を更に整理することである。第2正規化で全体の情報をエンティティに分けるが、このエンティティの持つ情報から更に一意に特定できる情報を独立化させたものが第3正規形である。

ボイス・コッド正規化

第3正規形のうち非キーから主キーが一意に定まらないDBのこと。ボイス・コッド正規形とは、非キーから主キーへの関数従属をなくした状態。

第4正規形

ボイス・コッド正規化までが情報の整理を行うのに対し、第4・5正規化はエンティティ間の関係を整理する。

エンティティはキーによって一意に特定できる。そのため、キーだけのテーブル(エンティティ)を作成することで、エンティティの関係を表すエンティティを作成できる。

例えば「A→→B|C」の関係のエンティティがある場合、「|Aのキー|Bのキー|」と「|Aのキー|Cのキー|」のようにして関係を表す。

第5正規形

1つの関係エンティティにつき1つの関係を表すDBが第5正規形である。

A→→B、A→→Cに加えてB→→Cの関係があるでは、3つのエンティティの関係を表すテーブルができる。

この時に以下のようにして分割することで、全てのエンティティの関係を表すことができる。

  • |A|B|
  • |A|C|
  • |B|C|

Appendix

my.cnf
[client]
default-character-set=utf8

[mysqld]
character-set-server=utf8

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8
初期化
START TRANSACTION;
CREATE DATABASE sampledb;
USE sampledb;
CREATE TABLE sample_table
(class  CHAR(3),
 sex CHAR(1),
 population  INTEGER);
INSERT INTO sample_table VALUES ('1-A', '男', 15);
INSERT INTO sample_table VALUES ('1-A', '女', 12);
INSERT INTO sample_table VALUES ('1-B', '男', 14);
INSERT INTO sample_table VALUES ('1-B', '女', 10);
INSERT INTO sample_table VALUES ('2-A', '男', 11);
INSERT INTO sample_table VALUES ('2-A', '女', 20);
INSERT INTO sample_table VALUES ('2-B', '男', 21);
INSERT INTO sample_table VALUES ('2-B', '女', 10);
INSERT INTO sample_table VALUES ('3-A', '男', 15);
INSERT INTO sample_table VALUES ('3-A', '女', 16);
INSERT INTO sample_table VALUES ('3-B', '男', 14);
INSERT INTO sample_table VALUES ('3-B', '女', 15);
COMMIT;

メタプログラミングRuby まとめ

投稿日:
タグ:

Ruby

本稿は『メタプログラミングRuby』を読んだ自分用のまとめ。

Rubyの基本メソッド
BasicObject#ancestors
継承関係
メソッドを呼び出す場合,ここで取得する配列の0から順番に検索され,使用される。 例えば,「"hoge".func」のようなメソッドを呼んだ場合,Stringから順に検索される。
[String, Comparable, Object, Kernel, BasicObject]
Kernel#p
オブジェクトの中身を形成(Object#inspectメソッドを呼び出す)して出力する
BasicObject#methods
クラスで定義されたメソッド一覧
public_methods
protected_methods
private_methods
singleton_methods
public・protected・private・特異メソッド一覧
BasicObject#instance_varitables
オブジェクトが持つインスタンス変数一覧
Object#class
クラスを取得
Class#superclass
スーパクラスを取得
オブジェクトモデル
オープンクラス

Rubyでは,定義されたクラスを開いて,内容を変更することができる。

p String.methods.include?(:to_num) # false

class String
  def to_num()
    self.to_i # ※ Rubyのメソッド定義ではreturnがない場合,最後に評価した値を返す
  end
end

a = "123"
p a.to_num # 123

ちなみに既に定義されたメソッドがある場合,上書きする(モンキーパッチ)。

class Hoge
  def func()
    puts "hello"
  end
end

a = Hoge.new

class Hoge
  def func()
    puts "bye"
  end
end

a.func() # byeを出力

Refinements(Ruby2.0以降)
usingを使用すると,引数で指定したモジュールで定義された拡張を現在のクラス、モジュールで有効にできる。
module RefStr
  refine String do
    def to_num()
      self.to_i
    end
  end
end


using RefStr

"123".to_num
メソッド
動的ディスパッチ
Object#send(name [, args, ...])
実行時に呼び出すメソッドを動的に変更する。
class Hoge
  def func1()
    puts "hello"
  end
  def func2(str)
    puts "#{str}"
  end
  def func3()
    puts "bye"
  end
end

a = Hoge.new
a.send(:func1)
a.send(:func2, "yeah!")  
a.send("func3".to_sym)   # 文字列 -> シンボル
動的メソッド
Module#define_method(methodname){|arg, ..| block }
メソッドを動的に定義する。
define_method(:func1){|x| puts x}

class Hoge
  define_method(:func2){|x| puts x}
end

func1("hello")
Hoge.new.func2("bye!")
Object#method_missing(methodname, [*args, &block])
メソッドが見つからない場合,method_missingが呼び出される。
class Hoge
  def method_missing(methodname)
    puts "No #{methodname}!"
  end
end

a = Hoge.new
a.func # No func!
ゴーストメソッド
def method_missing(methodname, *args, &blcok)
  if methodname==:func1 then
    puts "#{args}" 
  else
    super.method_missing(methodname, args, block)
  end
end

func1 1 # [1]
func2 2 # ↓
# NoMethodError: undefined method `func2' for main:Object
#       from (irb):8:in `method_missing'
#       from (irb):13
#       from /usr/bin/irb:12:in `<main>'
その他
可変長引数(*v)
呼び出される側
func(*v)
  p v
end
irb(main):002:0> func(1,2,3)
[1, 2, 3]
=> [1, 2, 3]
呼び出す側
def func(a,b)
   puts "a=#{a}, b=#{b}"
end
irb(main):015:0> args = [1,2].product(["a", "b"])
=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
irb(main):016:0> args.each{|a| func(*a)}
a=1, b=a
a=1, b=b
a=2, b=a
a=2, b=b
=> [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
irb(main):017:0>

一覧