Objects.checkFromIndexSizeについて
Objects.checkFromIndexSize
というメソッドに遭遇したので調べたことを書きます。
まずはソースコードを見てみると、Preconditions.checkFromIndexSize
に委譲されているシンプルなメソッドです。
public static int checkFromIndexSize(int fromIndex, int size, int length) { return Preconditions.checkFromIndexSize(fromIndex, size, length, null); }
このPreconditions
クラスはguavaのPreconditions
クラスとは別者でjdk.internal.util
パッケージに属するクラスです。
このパッケージ自体はJava 9で追加されたもののようで、名前からして普通の開発者が直に触るものではないでしょう。
しかし、見るだけはタダなのでちょっと覗いてみることにします。
public static <X extends RuntimeException> int checkFromIndexSize(int fromIndex, int size, int length, BiFunction<String, List<Number>, X> oobef) { if ((length | fromIndex | size) < 0 || size > length - fromIndex) throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length); return fromIndex; }
fromIndex
, size
, length
の引数を取ります。if文には以下のような条件式が設定されています。
- (length | fromIndex | size)
が負かどうか。
ビット演算で正負の判定をしています。各変数の最上位ビットに一つでも1が入っていた場合、演算結果の最上位ビットに1が入り、負となります。
つまり、3つの変数のどれか一つでも負であった場合、true
となる。
- size > length - fromIndex
例として配列を考えると、fromIndex
からlength
分を取り出そうとするときに、それが配列のsizeを超えているかどうか、のような判定をしたいときに使われる条件で、size
を超えているときにtrue
になる。
つまり、checkFromIndexSize
メソッドは配列の範囲外にアクセスしようとしていないかのチェックに使えるメソッドのようです。
そして、範囲外のアクセスであれば例外を投げます。(Bifunction
型の引数についてはここではおいておきます。)
Objects.checkFromIndexSize
メソッドが使われている例として、BufferedReader.read
メソッドがあります。
public int read(char[] cbuf, int off, int len) throws IOException { synchronized (lock) { ensureOpen(); Objects.checkFromIndexSize(off, len, cbuf.length); if (len == 0) { return 0; } int n = read1(cbuf, off, len); if (n <= 0) return n; while ((n < len) && in.ready()) { int n1 = read1(cbuf, off + n, len - n); if (n1 <= 0) break; n += n1; } return n; } }
read
メソッドはReader
クラスの抽象メソッドで、一般にリソースから引数のlen
だけ読み込んで、cbuf
配列にoff
で指定したindexから書き込むメソッドです。
このときに引数をObjects.checkFromIndexSize
に渡して範囲外のアクセスがないかをチェックしています。
わざわざ新しく引数のチェックのコードを書かなくて済むというわけですね。
プロセスとは
「オペレーティングシステムの仕組み」を読んでプロセスの勉強をしています。
プロセスとは
OSがプログラムを実行するためには、実行ファイルをメモリ上に読み込み、実行のために様々な準備をしたのち、プログラムを実行可能状態にし、実行を開始する。このように実行可能状態であるプログラムをプロセスと呼び、実行ファイルであるプログラムと区別している。プログラムである実行ファイルは一つしか存在しないが、プロセスは複数存在することがある。
メモリ上に読み込まれたプロセスにはメモリ領域が割り当てられ、大きく下記の3つの部分に分けられる。
- コード領域(テキスト領域とも言う)
プログラムコードが格納されている領域。 - データ領域(ヒープ領域を含む)
プログラム実行に必要なデータが格納されている領域。グローバル変数や動的確保されたメモリ(オブジェクト)などが格納される。動的確保されたメモリ領域のことをヒープ領域という。ヒープ領域はメモリアドレスの小さい方から大きい方へ順に確保されていく。 - スタック領域
こちらもプログラム実行に必要なデータが格納されている領域でスタックとして利用される。関数呼び出しの戻りアドレスや、関数のローカル変数を格納する。スタックはメモリアドレスの大きい方から小さい方へ順に確保されていく。
CPUは一つしか存在しないため、本来ならば一度にひとつのプロセスしか実行できない。しかし、ひとつのプロセスが終わってから次のプロセスを実行、というように順番に処理していると不便であることが多い。そこで、CPUで処理するプロセスを極めて短い時間で切り替えて同時実行しているように見せかける。人間にとっては認識できないレベルの短い時間のため、複数のプロセスが同時に実行しているように見える。このような擬似的な同時実行処理を擬似並列という。
複数のプロセスが同時に動作することを「プロセスが並行に動作する」という。これは擬似並列であるかどうかは問わず、同時に動いているように見えればよい。それに対し、「プロセスが並列に動作する」というと、複数のプロセスが本当に同時に動いていなければならない。複数のCPUが搭載されているPCならCPUの数だけ同時にプロセスが実行できる。
プロセス切替
一つのCPUで複数のプロセスを切り替えて処理することをプロセス切替、あるいはコンテキスト切替という。プロセスA,プロセスBが交互に実行されているとする。プロセスAからプロセスBへ切替を行うとき、後にBからAへ再切替がおきるので、A実行時のレジスタの状態を保存する必要がある。この処理はOSの仕事である。まずAのレジスタ状態をカーネル内の領域に退避する。次にBへの切替を行うために、保存されていたBのレジスタの状態を取り出し、レジスタの内容をその状態に再設定する。この再設定により、レジスタにはBが前回実行していたプログラムカウンタやスタックポインタが設定されているので、あたかも前からBを実行していたのと同じ状態となり、Bの処理を再開できる。
カーネル内部でプロセスを管理するために様々な情報を保持している。その情報はプロセステーブルまたはプロセス制御ブロックと呼ばれる。C言語の構造体として定義されていることが多い。
放送大学はじめました
この4月から選科履修生として放送大学の講義を受けています。私が放送大学に入学したきっかけは以下の記事を読んだからでした。
放送大学と学位授与機構で情報工学の学位を取る(科目対応表付き)|lumpsucker|note
情報工学の学位については、以前から取りたいと思っていました。私は情報工学科以外の学部を卒業してIT業界で働いています。情報工学の学位がなくても業務上とくに困ることは無いんですが、日々生まれる新しい技術を追いかけるだけになっている現状に対して情報工学の知識があればもっと俯瞰してみえるのではないか、と思うことはよくあります。まあ正直考え過ぎだとは思いますし、情報工学を勉強すれば生まれ変われる!みたいな幻想を持っているわけではないんですが、他方情報工学の学位がないと就職できない企業もある、なんてのを聞くと、完全に切り捨てられない感じがするのも事実です。このへんは博士号に対してよく言われる「足の裏についてるご飯粒」みたいな話と似ています。
実際に学位を取るためには学位を取らなければならない。だが仕事もあるので、対面授業に通うのは流石に厳しい。オンラインで勉強できる放送大学が一番便利なんですが、この大学は教養学部しかない。どうしようか、と迷っているときに、前述の記事を目にしたわけです。なるほど、学位授与機構なんてのがあるのか、と。
そんなわけで、放送大学で学ぶことにしました。情報工学の勉強は業務と直接関係ないといっても、授業の範囲は応用情報の試験範囲と重なる部分も多く、放送大学で勉強しながら応用情報の資格をとることができれば一石二鳥なのでは?と思っています。
履修登録した科目はこちら(英語名を載せているのは、英語の方が内容がわかりやすい科目があるからです)
科目名 | 英語科目名 |
---|---|
計算の科学と手引き | Introduction to Computer Science |
情報理論とデジタル表現 | Information Theory and Digital Representation |
データ構造とプログラミング | Data Structures and Programming |
アルゴリズムとプログラミング | Algorithms and Programming |
コンピュータの動作と管理 | Computer Operation and Management |
コンピュータ通信概論 | Introduction to Computer Telecommunication |
情報ネットワーク | Information Network |
身近なネットワークサービス | Networking Services in Our Life |
情報セキュリティの理論と基盤 | Fundamentals of Information Security |
情報セキュリティ概論 | Introduction to Information Security |
身近な統計 | Introduction to Statistics |
履修登録したときは、今年度はそんなに忙しくならない予定だったので多めに申請してしまったのですが、急に4月から忙しくなって単位が取れるか怪しくなってきたところではあります。とりあえず、5月から通信指導が始まるので、そこに向けて尽力するのみです。
一口Tech : Pingについてちょっとだけ
新人がpingにポート番号が設定できないと困っていたのでちょっと調べてみた。
Pingとは : ICMP (Internet Control Message Protocol) を利用した疎通確認プログラム
ICMPパケットの構造
名称 | 長さ(byte) | 内容 |
---|---|---|
Type | 1 | ICMPメッセージの機能や種類 pingは8:エコー要求/0:エコー応答 を利用している |
Code | 1 | ICMPメッセージの機能や種類(詳細) |
Checksum | 2 | - |
Data | - | ICMPメッセージごとで異なる追加データ |
OCI参照レイヤについて
ICMP: インターネット層
TCP/UDP: トランスポート層
ポート番号はトランスポート層に固有な概念。 それゆえpingでポート番号を指定できない。pingはインターネット層で目的地に到達すればOKとなる(多分)
同期式順序回路その2 (有限状態機械)
組み合わせ回路は与えられた入力によって一意に出力が決まるので、回路の性質は真理値表を用いて記述される。しかし順序回路の出力は入力だけではなく、内部記憶の内容、つまり回路の状態にも依存するので真理値表だけでは記述することができない。順序回路の記述には有限状態機械とよばれるものを利用する。そもそも順序回路自体が有限状態機械とも言える。順序回路は状態の有限集合を保持し、回路の構成要素として次状態回路と出力回路を持つ。それを模式的に表したのが下掲の図である。
レジスタが現在の状態を保持しており、レジスタ出力が順序回路の入力とともに出力回路を経由して順序回路の出力となる。そして、同時にレジスタ出力が順序回路の入力とともに次状態回路に入力され、次の状態がレジスタに入る。そして、クロックエッジによってレジスタに記憶される状態が次状態に更新される。
有限状態機械(順序回路)についてわかりやすくするために、出力回路に入力されるのをレジスタ出力のみとする。このような有限状態機械をMoore方式と呼ぶ。上掲の回路のように出力回路がレジスタ出力と順序回路に基づくタイプをMealy方式と呼ぶ。この2つの状態機械は機能的には等しく、機械的に相互に変換することができる。Moore方式はMealy方式より速くできるが、Mealy方式は必要とする状態数が少ないのでより小型にできる。
同期式順序回路
組み合わせ回路とは現在の入力の値のみで出力が決まる回路であり、順序回路とは組み合わせ回路以外の回路のことをいう。具体的には出力は現在の入力値と現在の回路の状態に依存する。
順序回路はさらに同期回路と非同期回路に分けられる。いくつかの回路を見ていくことでその違いを考える。
非安定状態
上掲の図でどんな事が起こるか考えてみる。X=1のとき、Y=0でZ=1となりX=0となるが、最初のX=1と矛盾する。実際には回路素子には遅延が存在するので、具体的に回路がどういう振る舞いを示すか見てみることにする。ここではそれぞれのインバーターの伝播遅延時間を1nsとする。つまり、入力が入ってから出力が出てくるまで1ns掛かるということである。Xの初期値をX=1とする。t=1nsでX=1→0と立ち下がると, t=2nsでY=0→1と立ち上がる。するとt=3nsでZ=1→0とたち下がり、t=4nsでX=0→1と再び立ち下がる。これを繰り返し、Yはt=5nsで立ち下がり、Zはt=6nsで立ち上がり、Xはt=7nsで立ち下がり、t=1nsの状況に戻り、同じ遷移を繰り返す。このときそれぞれのノードは6nsの周期で0と1を振動する。このように回路の状態が変化し続けることをアンステーブル状態、または非安定状態という。
レース状態
上掲はDラッチを改造したものであるが、この回路はどう動作するだろうか。初期状態をCLK=D=1とする。このとき、このDラッチは透過状態になりQ=1となる。ここでCLK=1→0と立ち下がるとする。この回路に遅延がないならば、上のANDゲートの出力が0になり、同時に下のANDゲートの出力が1になるはずである。しかし実際には、CLK=1→0の立ち下がったあとにが立ち上がるまでに遅延が発生する。そのため、CLKとが同時に0になる瞬間が存在することになり、Qが0になってしまうことがある。ひとたびQ=0になるとが立ち上がっても下のANDゲートの出力は1にはならない。電気のパスによって信号の伝播の速度が違うことによる意図しない挙動を示すことをレース状態という。
同期式順序回路
上掲の2つの回路は出力が直接入力にフィードバックしている。こういったループを巡回パスと呼び、この巡回パスによって非安定状態やレース状態が引き起こされている。そして巡回パスにレジスタを挿入しループを断つことによってこのような問題を回避できることがわかっている。非安定状態では、巡回パスにレジスタを挿入することによって状態の移り変わりがせき止められ、安定状態に落ち着く。レース状態がある場合では、レース状態の過程が出力に直接反映されなくなる。
非安定状態の例でいうと、下掲の図のようにレジスタを挿入する。するとレジスタで信号の伝播がせき止められ、振動が止まる。クロックエッジによってレジスタがX'の信号を通すとX→Y→Z→X'と伝播するが、再びレジスタで止まることになり、非安定状態を解消できる。
この回路の状態はとの2通りであり、非安定状態ではこの2つの状態で振動していた。レジスタを挿入することによって、この2つの状態をクロックエッジによって切り替える事ができたとも言える。このように順序回路の状態がクロックエッジによって遷移していることをクロックに同期しているという。
一般に下記の条件を満たす回路は同期式順序回路という。
・レジスタか組み合わせ回路ブロックのみで構成されている
・回路ブロックの少なくとも1つはレジスタである
・すべてのレジスタには同じクロック信号が入力されている
・すべての巡回パスには少なくとも1つのレジスタが挿入されていること
Dフリップフロップ・レジスタ
Dラッチの復習
以前にDラッチについて勉強したが(ストレージ基礎(Dラッチ) - jinyux’s diary)、順序回路の勉強に入るにあたりもう一度復習する。
Dラッチの回路図を以下に再掲する。
SRラッチは2つの状態を持ち、1bitのデータを保存できる順序回路である。SRラッチはSとRに「どちらに何」を「どのようなタイミング」で入力するかが重要になってくる。「どちらに何」は次の状態を反映し、「どのようなタイミング」は文字通りいつ状態を反映させるかに対応する。SRラッチではこの2つの要素がS、Rの入力パターンとタイミングで指定されるわけだが、2つの要素が混在しているので設計上このままでは扱いにくい。そこでその2つの要素を切り分けたのがDラッチである。「どちらに何」はD入力のオン/オフで、「どのようなタイミング」はCLKのオン/オフでコントロールする。
CLK=1のとき、ラッチは透過状態であるといい、Dに入力されたデータがQにそのまま流れ出る。CLK=0のときは、不透過となりDにいかなるデータが入力されてもQは直前の値を維持し続ける。すなわちCLK=1のときにDの値が変化すると、Qの値もそれに応じて変化し続けることになる。次に考えたいことは、CLK=1のときずっと変化し続けるのではなくて、ある瞬間の値だけ反映する回路は作れないかということである。もっと具体的に言うと、CLKが0→1に変化した瞬間にのみ状態を更新するような回路である。
Dフリップフロップ
以下のような回路をDフリップフロップと呼び、前述の「ある瞬間にのみ状態を更新する回路」の一つの例である。
2つのDラッチを接続したものであり、左側(1番目)のラッチをマスタ、右側(2番目)をスレーブと呼ぶ。
CLK=0のとき、マスタラッチは透過状態となり、スレーブは不透過となる。それゆえDの値はN1まで透過するがその先には伝播しない。次にCLK=1になると、マスタは不透過となり、スレーブが透過状態となる。N1の値がスレーブを透過してQまで伝播する。一方、マスタに入力されるDはブロックされ、N1には反映しない。CLKが0→1に立ち上がる直前のDの値が、CLKが立ち上がった直後に出力Qにコピーされることになる。それ以外の安定状態のときには、DとQの間に常にブロックしているラッチが存在するのでQの値は変化しない。
まとめると、Dフリップフロップは「クロックの立ち上がりの瞬間にその時のDの値をQにコピーし、それ以外のときはQの値(状態)を保持し続ける」ということである。Dフリップフロップをモジュール化した図を以下に示す。