kono先生のOSの講義の指南書

目次

  • 自己紹介
  • はじめに
  • kono先生のOSの講義に関する情報
    • シラバスを眺める
    • 授業評価アンケート結果を眺める
  • 単位取得の流れ・課題の提出方法・成績の基準について
    • 単位取得の流れ
    • 課題の提出方法
    • 成績の基準
    • 単位を落としたときのバックアップ
  • 講義のスケジュール(2021年度)
    • 10月
    • 11月中旬
    • 12月下旬
    • 2月中旬
    • 講義のスケジュールのまとめ
  • OSのベストプラクティス
  • 最後に

「自己紹介」

ヨシザウルスです。最近、kono研に仮配属されました。OSで時々後輩を助けてます。


「はじめに」

B2の必須科目は重たい講義が多く、その中でも最も難しいとされる講義が「OS」です。

この記事では、OSが難しいとされる所以や、攻略の仕方、スケジュール、学生が取れるベストプラクティスを解説しています。

(特に先生に向けて) 保険ですが、著者は第一志望でkono研に入りましたし、kono先生も好きですし、OSも楽しく攻略しました。

しかし...、初めてOSの講義を受ける学生にとっては、それはそれは難しいはずなので、手助けのつもりでこの記事を書いてます。

ヘイトスピーチは一切ないです!


「kono先生のOSの講義に関する情報」

OSの講義のシラバスや授業評価アンケートを眺めて、ieの講義の中でOSがどれほど特殊で難しい講義か理解しましょう。


シラバスを眺める

大学の講義は大体、どのように成績をつけるのかがシラバスにかかれています。OSの講義を理解するために、まずはOSのシラバスを眺めてみましょう。

シラバスに「授業内容と方法」という項目があるので読んでみましょう。

コンピュータに接続された機器や、コンピュータ上のアプリケーション、そして、コンピュータのユーザの間の関係を調整する役目をになうのがオペレーティング・システム(OS)である。OSの基礎と理論、そして、実装を、プログラミングや実際のプログラムを読むことから学んでいく。英語の教科書を使います。必ず購入すること。

英語の教科書を使う」と書かれていますが、kono先生はよく学生に「毎日2時間は英語勉強しなさい」と言っているので授業内容に英語を取り込むのはかなりkono先生らしいですね。


次に「達成目標」を読んでみましょう。

(F)OSの基礎と理論を理解し、OSを自分でインストールし自在に使いこなすことができる(実践性) (G)現在のオペレーティングシステムの適用範囲や限界を良く理解し,将来,技術者, 研究者として,より有効な新しい情報システムを開発することができる(創造性) (H)オペレーティングシステムに関する専門的な深い知識を身に付け,専門用語が示すものの具体的内容及びその有効性,適用範囲を説明することができる(専門性)

実際に著者が実感したOSの講義の効能として、OSの講義を受けて「オペレーティングシステムの適用範囲や限界」を加味した上で、高レイヤーの処理を書くことができるようになりました。


次に「 評価基準と評価方法」を読んでみましょう。

レポート、出席状況、演習の結果を総合して判定する。
必須指定されたレポートは確実に提出すること
レポート数15以下は不可とする

レポート数16以上なら単位は貰えるということでしょうか? とりあえず、ボーダーラインぎりぎりを目指すのではなく、できる限り提出してみましょう。力試しとして挑戦してみてください!


授業評価アンケートの結果を眺める

実際に講義を受けた学生からの評価も見ておくと、OSの難易度が鮮明に伝わるので参考になります。

OSの授業評価アンケート(2021年度)で、学生からのOSの評価を調べてみましょう。

この記事では、同年の「実験・実習系」である、「知能情報実験Ⅱ」と「ソフトウエア開発演習Ⅰ」の授業評価アンケートの結果と見比べて表示しています。


「回答人数」

  • OS
    • 48人
  • 知能情報実験Ⅱ
    • 31人
  • ソフトウエア開発演習Ⅰ
    • 31人

回答人数がOSの方が多いですね。


「Q. 使用したテキストは、講義をうける上で効果的(分かりやすかった,役に立った etc...)でしたか? 」

OS 知能情報実験Ⅱ ソフトウエア開発演習Ⅰ

OSのテキスト(講義資料)は、アンケート上では「分かりにくい」と学生に思われているらしいです。OSに合格したあとだと自然と資料が分かりやすく感じますので、このようなアンケート結果になった原因としては、「初めてOSを取るB2の学生には初めて聞くような単語が多いから」だと推測します。自分で調べて理解するフェーズは、どのエンジニアリングにも必ず存在するので、まずは自力で調査する練習だと思って授業に食いついてください。


「Q. 教員の説明は分かりやすかったですか? 」

OS 知能情報実験Ⅱ ソフトウエア開発演習Ⅰ

OSのアンケートでは、半数が「分かりやすくなかった」と回答していますね...。なにより、kono先生の授業は「授業」というよりかは「実況」に近いです。「自由」がkono先生のいいところでもあるので目を瞑ってあげましょう...。


「Q. この講義の課題の難易度はどうでしたか? 」

OS 知能情報実験Ⅱ ソフトウエア開発演習Ⅰ

OSの課題は、アンケートを答えた学生が「難しかった」以上の評価をしています。これは課題の質もそうですが、量も多いので初めてOSを受けるB2は、課題に一番苦戦しているのではないかと思います。乗り越えるには、技術力、友達に頼る力、先輩に頼る力の中のなにか一つが秀でている必要がありそうです。


「成績評価(課題、試験、評価方法 など)に対するコメント」

OSの評価に対するGoodな学生の感想は以下の通りです。

  • 「授業が終わっても課題を提出したいです。」
  • 「OSについて詳しく学ぶことができた。また、先生の講義自体も試行錯誤しながら進めていく形で非常に勉強になった。」
  • 「 OSの基礎や理論について学んだことをこれからの授業や卒業後に活かしていきたい。」
  • 「今までOSは抽象的な意味しか理解できなかったが、この講義を通してOSについての具体的な概要をある程度イメージできるようになったと思う。」

「OSについて理解できたので、これからの技術の応用にその知識を活用したい」といった感想が多いですね。

OSの評価に対するBadな学生の感想は授業評価アンケートを見てください...!

OSと同じ時期に修辞学の講義でも取ったのかは不明ですが、文才が溢れるコメントがあって面白いです。


授業評価アンケートのまとめ

アンケート結果を眺めると「OSは資料と先生の説明が分かりにくい上に、課題が難しい(すぎる)」ということがわかります。

しかし、著者自身は、OSはieの講義の中で最も価値のある講義だと思っていますよ! 調べる力や技術力は確実に上がりました!


「単位取得の流れ・課題の提出方法・レポート提出状況の確認・成績の基準について」

OSの特徴が理解できたところで具体的にどのように攻略するかを見ていきましょう。


単位取得の流れ

単位取得の流れを簡単に表現すると、以下のようになります。

kono先生が授業中に課題を 紹介 or 修正 する。
紹介 or 修正された課題のみ学生のレポート提出が許可される。
kono先生にメールでレポートを出す。
そのレポートが先生によって、「合格」or「不合格」の判定が下される。
「不合格」の場合は、レポートの内容の悪い点が指摘されているFBメールの返信が来る。(レポートの内容がひどい場合は返信なくても「不合格」になるときがある)
「合格」になるまで、レポートを提出していい。(当てずっぽう的に提出し続けるとkono先生がキレるのでなるべく避けたほうがいいが、そういう精神性は持っておいた方がいい)
「合格」の判定の課題提出数を最大化することを学生は目指す。

課題の提出方法

ie.u-ryukyu.ac.jpドメインのメールアドレスでkono@ie.u-ryukyu.ac.jp宛に以下のような題名でメールを送信します。

Operating System Lecture Exercise x.x

例えば、1.1の問題を提出する場合は、Operating System Lecture Exercise 1.1という題名でメールを送信してください。

メールの本文で氏名や学籍番号を書く必要はないです。

「不合格」を意味するkono先生によるFBメールが返信されたら、それに返信するのではなく新たにメールを作成してレポートを提出することをおすすめします。

レポート提出状況の確認

レポート提出状況を知る手段が2種類あります。discordでのbotの通知と先生からのメールです。

頻度としては「discordのbot(2021年度は9回) > 先生からのメール(2021年度は3回)」です。

情報量としては「discordのbot(曖昧) < 先生からのメール(かなり詳細)」です。

まずは、discordでのレポート提出状況の確認方法を見てみましょう。

discordのOSサーバーのteacherチャンネルで以下のようなメッセージがbotによって送信されます。

e2057** * in *
...
e2057** * in *
e2057** * in *
e2057** * in *
...
e2057** * in *

自分の学籍番号が書かれている行を探してみましょう。

e2057** x in y

上記の行の意味としてx in yの部分は、以下を意味しています。

y通のレポート提出メールをkono先生が受け取って、x通は先生に見られた

結論としては、「discordの通知によって、先生が学生の提出したレポートを見た数がわかるだけで、何通が合格したかはわからない」ということになります。

discordのbotの通知で合格したレポートの数がわからなくても、不合格のレポートには先生から何かしらのFBが返信で返ってくるので、それを参考にして学生自身でレポートの合格判定をしてみましょう。


次に、先生からのメールによるレポート提出状況の確認方法について調べてみましょう。

先生から送られるメールの内容は以下のようなものです。

Exercise 1.1 -- Date: <レポートが提出されたときの時間>
        ok Operating System Lecture Exercise *.*
Exercise 1.2 -- Date: <レポートが提出されたときの時間>
        unchecked Operating System Lecture Exercise *.*
Exercise 1.3 -- Date: <レポートが提出されたときの時間>
        bad Operating System Lecture Exercise *.*
...
↑のような報告がずっと続く
↓メールの末尾
...
e2057xx@ie.u-ryukyu.ac.jp os exercise is not finished! a(ok)/b(received) in c mails

okは「合格」、badは「不合格」、uncheckedは「メールは受信したが、まだ評価していない」です。

ちなみに、2021年度は、終盤の「1月18日」、「2月8日」、「2月12日」に上記のようなメールが来ました。

同時期に来たdiscordの通知とメールによる通知を見比べて見た結果、discordの「e2057** x in y」とメールの末尾にある「e2057xx@ie.u-ryukyu.ac.jp os exercise is not finished! a(ok)/b(received) in c mails」は、xとb、yとcが同じ値でした。

結論としては、「先生によるメールによって、どの課題レポートが合格したか、不合格か、まだチェックされていないかがわかるが、頻度が少ない」ということになります。


成績の基準

OSの場合は成績の付け方がかなりシンプルでわかりやすいです。

成績は「A」、「B」、「C」、「D」、「F」とありますが、OSの場合は「A」か「B」か「F」の三段階です。


Aの基準

課題提出数が単位取得の基準より上だった場合


Bの基準

課題提出数が単位取得の基準すれすれだった場合、別の言い方で表現すると「お情け」です。

結構レアなので、単位取得≒「A」だと思ってもいいです。


Fの基準

課題提出数が単位取得の基準に達していない場合


成績の付け方の結論

ほぼ、単位が取れたら「A」、取れなかったら「F」だと考えていいです。


単位を落としたときのバックアップ

後期でOSの単位を落としたら、来年の夏休みの補講があります。

2022年度の補講では、最後にkunita先生がメールで生徒に合否の結果を教えてくれたらしいです。(10月13日にメールが送信されたらしい)

GPA的には、去年度が0で補講で4なので、実質2(=C)みたいな感じになります。


「講義のスケジュール」(2021年度)

OSの講義の世界観がわかったところで、講義のスケジュールを把握してみましょう。2021年度のもので、毎年スケジュールが変わるかも知れませんが、参考になるはずです。


10月

  • kono先生の講義が開始される

11月中旬(2021/11/09)

  • 教科書の問題番号が配布される
    • 配布されるには一定数のレポートをある期日までに提出しなければならない
    • 学生によって与えられる問題が異なる
    • 問題番号が教科書に存在しなかったら好きな問題を解いていい
    • 以下のようなメールが送られる
Your OS assignments for Paperback are
Chapter 11 ex 11.15
Chapter 6 ex 6.17
Chapter 12 ex 12.5
Chapter 21 ex 21.15

12月下旬(12月31日)

  • 12月31日までに6個のレポートを「提出? or 合格?(どちらか不明)」していなければ、足切りになる(先生談)
    • ≒その日、以降どんなに出しても採点されずに不合格が決定する
    • しかし、足切りのはずの生徒も爆速で課題提出して合格していたこともあったので真相は謎

2月中旬

  • 課題の締め切り
  • 締め切りに間に合わせて課題を出しても、学生全体が最後に先生の処理しきれないレポートを提出すれば採点されずに、成績評価されてしまう

講義のスケジュールのまとめ

計画的にOSを倒しましょう。


「OSのベストプラクティス」

OSで取る行動によって、レポートを作成するハードルが下がったり、逆に単位取得難易度を上げたりすることがあります。

OSの「やるべきこと」と「アンチパターン」を見てみましょう。


やるべきこと


作業ログは残しておこう

出したレポートが不合格だったとき、何も作業ログを残していないとまた0からのスタートになるので、作業ログを残しておきましょう!


教科書の問題を貰う

講義のスケジュールで、教科書の問題番号が配布されるフェーズがあります。

4つ解ける課題が増える上に、教科書の問題はkono先生が作った問題よりも解きやすいので、OS攻略では重要な要素になります。

しかし、教科書の問題番号を配布してもらうには、ある条件をクリアしなければならないです。

条件といっても「x月y日までにn個のレポートを提出する」のようなものですが、詳細は毎年変わるので、授業中に口頭で説明されるときに聞き逃さないようにしましょう。


授業中にkono先生とモブプロする機会があれば、ぜひやってみよう

kono先生は、モブプロで積極的に手を挙げる生徒には優しくレポートの解き方を教えてくれます。


なるべくDMは避けてパブリックな場所で質問してみよう

質問することは良いことですが、DMだと回答が他に同じところで詰まっている学生が見れないので、できるだけパブリックな場所で質問しましょう。


友達と協力しよう

OSだけでなく、他の教科もB2後期は難しいので協力しましょう。GPA争いとかは気にしないほうが平和です。


今のうちにOSの単位を取得しておく

これは完全に陰謀論なのですので、スクロールしながら適当に読んでください。

最近のkono先生はOSの講義や補講のレポートの採点が終わるタイミングで「翁長先生が必須科目の線形代数の講義で学生全員を落とす話」のツイートをします。

kono先生ならいつか同じことをやりかねないので今のうちに単位を取得しておきましょう。

補講の人は絶対に補講中に合格してください。補講の間は問題が変更されませんが、補講でも落とした場合、毎年問題が変更されるので去年度の課題のストックがほぼ全て無効になります。


アンチパターン


ボーダーラインを先生に訊く

kono先生的には「ボーダーラインを知る」→「ボーダーラインに乗っかった瞬間にOSの課題をやらなくなる」ということなので(大半の学生はそうだろうな...)、ボーダーラインを先生に訊く=やる気ないです宣言なので、先生がキレます。キレるとボーダーラインが上がります。


『わからない』と吐き捨てる

モブプロとかをするチャンスを振られても、投げやりに「わからないのでやりません」と言うのは避けましょう。わからなくても、「わからないですが、やってみます」と言いましょう。そういう生徒には本当に優しく接してくれます。


スクリーンショット画像をメールに添付する

課題の目標達成の証拠としてスクリーンショット画像を添付すると、レポートが「不合格」になる確率が上がります(ほぼ100%)。出力結果はコピペで貼っつけていいです。


画像ファイルにsvgじゃなくてjpegを使う

河野先生はjpeg大大大嫌いです。jpegは拡大すると解像度が荒くなりますが、svgはそのようなことは起きません。画像ファイルをWebサイトに掲載、メールに添付するときは、jpegじゃなくてsvgにしましょう。


ソースコードをGitLab(もしくはGitHub)に上げずに、メールに貼る

この行為も、レポートが「不合格」になる確率が上がります(ほぼ100%)。

ソースコードを提示するときは、GitLab or GitHubにpushしてレポジトリのurlを貼りましょう。


英語を和訳する

これは教科書(英語)の問題を解く課題でよくある話ですが、英語で書かれている問題文を和訳してメールの内容に追加すると、レポートが「不合格」になる確率を高める...ということはないですが、不合格のとき、FBの返信で「だから和訳書くなよ」と言われます。


「最後に」

この記事でkono先生のOSについて理解が深められたでしょうか、多少なりとも手助けになれば幸いです。

OSは難しい講義ですが、乗り越えれば必ずみなさんの糧になります。英語や調べる技術など、得られるものがあるはずです。

みなさんが、2年次が終わった頃に、OSで技術力が上がった! などの実感が得られることを期待してます。

ではみなさん、また今度!

スタバJKデビューみたいなことをした

スタバでJKした

スタバってメニューの品を注文したら細かいオプションを設定できるじゃないですか、結構細かい注文を最近したんですよね

抹茶クリームフラペチーノ

  • オプション
    • パウダー増量
    • シロップ抜き
    • 無脂肪ミルク
    • ライトINホイップ

甘すぎず抹茶の味が濃い感じで僕好みでした

どうやって頼んだの?

スマホでメモした内容をガン見しながら注文しました

定員さんがスタバデビューJKみたいなことをする僕のことを優しくほほえみ見守ってくれました、結構恥ずかしかったです

OSの11.xの課題

OSの講義の11.xの問題を解く

これが正解だぜ(どや)みたいなノリでは書いてないです、ただの平均的な技術力を持っているB2が解いているだけ

課題の取り組みをブログで書いてもいいという許可をもらった上で記事を書いてます

最新の課題内容であることや、答えが正しいかどうかは保証しません

このブログの情報で読者に何らかの不都合が生じても著者は責任は取らないです


11.1 VMの課題 (2022年度)

問題

このページの問題を解く

学科サーバ 上の KVM に自分の仮想マシンを作る

二つの方法
KVMのcommand line interfaceである virsh の wrapper ie-virsh を使う
(1) 自分の手元のPCで、仮想マシンのimageを qcow2 で作り、それを upload する
(2) Server(amaneなど)で、Fredoraのdvd imageから boot する

Server上のKVMからの Fedoraのinstall

Level 1
VNC または ssh で仮想マシンへの login し動作を確認する

Level 2
web server を起動し、 外部から curl でアクセスできることを確認する。
Hugoでサイトを作成する

absible
VNCたは virsh console で IPv6を確認して、amane 上とかから password抜きで ssh できるようにする。

(2) Server(amaneなど)で、Fredoraのdvd imageから boot する

  • amaneにssh
  • $ mkdir /mnt/ie-virsh/e20/e205723 (amane)
  • $ qemu-img create -f qcow2 /mnt/ie-virsh/e20/e205723/e205723-os-11-1.qcow2 20G (amane)
    • e205723-os-11-1.qcow2というファイルが先程mkdirで作成したディレクトリに生成される
    • e205723-os-11-1.qcow2というファイル名のos-11-1の部分に注目
  • $ ie-virsh define os-11-1 (amane)
    • os-11-1という名前のVMを作成する
    • qcow2ファイルを作成するときに$ qemu-img create -f qcow2 /mnt/ie-virsh/e20/e205723/e205723-xxxx.qcow2 20Gを実行していたら、$ ie-virsh define xxxxと実行しなければならない
  • $ ie-virsh start os-11-1 (amane)
    • VMを起動する
  • $ ie-virsh attach-disk os-11-1 /home/open/Fedora/Fedora-Server-dvd-x86_64-37_Beta-1.5.iso (amane)
    • /home/open/Fedora/Fedora-Server-dvd-x86_64-37_Beta-1.5.isoというディスクイメージをos-11-1にマウントする
  • $ ie-virsh vncdisplay os-11-1 (amane)
    • VMVNCディスプレイのポートを表示する
    •  uid: ***** gid: **** name: e205723
       :xx(←これが重要、VMごとに違う)
      
      • ↑これOS初見なのにエグい実力の後輩が言ってた
  • ローカルPCのターミナルを使う
  • $ ssh -L 10024:localhost:59xx -N amane (local pc)
    • amaneの59xxポートをlocalhost(自分の使っているPCのこと)の10024にポートフォワーディングする
  • VNC Viewerを起動
  • VNC Viewerを起動したらlocalhost:10024を入力すると、VNCディスプレイが見れる
  • uefi shellが立ち上がるので速攻でexitを入力する
    • ↓こんな画面になる
  • Boot Managerを十字キーでカーソル操作して選択
  • UEFI QEMU DVD-ROM QM00007を選択
  • Install Fedora 37を選択
  • インストーラーが起動する
    • ここでAttempting to reconnect to VNC Server...と出たら、「Options > General > Picture qualityをMediumとかAutomatic以外にするといける」って優秀な後輩が言ってた、Yoshisaurの環境ではなかった
    • 言語設定をする(英語)
    • Rootユーザー、ユーザー作成、ディスクの設定をする
      • Rootユーザー
        • Rootユーザーにsshログインできるようにするのはあまりベストではないけどね
      • ユーザー作成
      • ディスクの設定
  • Begin InstallationをクリックしてFedoraのインストールを開始する
    • 時間計測をした結果、5分7秒だった
  • qcow2の大きさを示す
    • /mnt/ie-virsh/e20/e205723/e205723-os-11-1.qcow2にqcow2ファイルが置かれている
    • $ ls -l /mnt/ie-virsh/e20/e205723/e205723-os-11-1.qcow2; date
      • 出力:
        • -rw-r--r-- 1 libvirt-qemu kvm 3546677248 Feb  8 14:06 /mnt/ie-virsh/e20/e205723/e205723-os-11-1.qcow2
          Wed Feb  8 02:38:29 PM JST 2023
          
  • Rebootする
    • 再起動しなかったらamaneで$ ie-virsh start os-11-1を実行
  • コンソールログインしたらysano(ユーザー作成のスクリーンショットを参考)と入力
  • パスワードを入力

Level 1:「VNC または ssh仮想マシンへの login し動作を確認する」

  • 現段階でVNCを使い続けて作業をするとコピーペーストができないので、sshでログインできるようにする
  • akatsukiでVMにipを付与してもらう
  • ローカルPCのssh configを書く
    • $ vim ~/.ssh/config (local pc)
    • 以下を追記
      •  Host fedora
           HostName 10.0.4.125
           User ysano
           ProxyJump amane
        
  • $ ssh-copy-id fedora (local pc)
    • 11.2の「fedora に password 抜きで ssh できるようにする」で出てくる作業なので忘れずに!
    • 公開鍵認証方式でVMsshログインできるようにする
  • `$ ssh fedora "date; ls -ld" (local pc)
    • これが無事に実行できたらLevel 1は大丈夫だと感じる
    •  Sat Nov 26 11:02:44 PM JST 2022
       drwx------. 3 ysano ysano 74 Nov 26 23:01 .
      

Level 2: 「web server を起動し、 外部から curl でアクセスできることを確認する」

  • このサイトを参考に作業をする
  • VM上で作業をする
  • $ sudo dnf install hugo (VM)
    • hugoのインストール
  • $ hugo new site yoshisaur-blog(VM)
  • $ cd !$(VM)
  • $ git init(VM)
  • $ git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke(VM)
  • $ echo "theme = 'ananke'" >> config.toml(VM)
  • $ hugo server -D(VM)
  • ローカルPCで$ ssh -L 8080:localhost:1313 -N fedora(local pc)
    • fedoraVMの1313番ポートで配信しているhugoのwebサイトをローカルpcの8080番ポートで見れるようにポートフォワーディング
  • localhost:8080を見る
  • $ curl localhost:8080 > curl_result.txt(local pc)
    • 最終的にはcurl_result.txtの中身をkono先生のメールに貼り付けて提出する

absible

  • 公開鍵認証方式でのsshログインはLevel 1で既に達成している
  • $ brew install ansible
  • $ echo fedora ansible_ssh_user=ysano ansible_ssh_private_key_file=~/.ssh/id_rsa > hosts
    • ysanoの部分は各自のユーザー名
  • $ ansible fedora -i hosts -m ping
    • ↓が帰ってきたらクリア(でいいでしょ?)
    •   fedora | SUCCESS => {
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python3"
            },
            "changed": false,
            "ping": "pong"
        }
      
  • 11.2の「Ansibleのping」で出てくる作業なので忘れずに!

11.2 ansibleの課題 (2022年度)

問題

このページの問題を解く

fedora に password 抜きで ssh できるようにする
Fedora を akatsuki に登録する
Ansibleのping
Ansibleのplaybook

fedora に password 抜きで ssh できるようにする

11.1で既に行った、「忘れずに!」で文字検索をこの記事にかけると出るかも


Fedora を akatsuki に登録する

11.1で既に行った、「忘れずに!」で文字検索をこの記事にかけると出るかも


Ansibleのping

11.1で既に行った、「忘れずに!」で文字検索をこの記事にかけると出るかも


Ansibleのplaybook

  • ansible を使って nginx を設定すると書いてあるが、すぐそばにあるリンクはwordpressサーバーを立てるAnsibleのplaybookなので、自作する必要がある
  • なので作った
  • README.mdの通りに作業をしたあとに、$ curl http://localhost:8080などを実行してアクセスできることを確認する
    • 作業ログとCurlコマンドの結果をメールに出すといいと思う

11.2(2022年度)の感想

  • ansibleは便利だ、覚えておくと役に立つときが来そう
  • ansible難しかった...

11.3 Containerの課題

問題

このページの問題を解く

daru上で singularity を使う
GPU があるかどうかを確認する
vimを入れる
rust を入れてみる
daru 上で slum を使う
daru 上で podman を使う
podman でなにか動かす

daru 上で singularity を使う

  • ~/.ssh/configに以下の設定を追加する
  • Host daru HostName daru.ie.u-ryukyu.ac.jp User e2057xx ProxyJump chatan
    • すでに設定されているだろうけど一応、chatanの設定はこの記事に書かれている
  • $ ssh daru
  • $ mkdir 11_2
  • $ cd !$
  • $ singularity pull --docker-login ubuntu:20.04
    • ubuntu:20.04のイメージを取得する
    • ubuntu_20.04.sifというイメージを格納しているファイルが生成される
  • $ singularity shell --nv ubuntu_20.04.sif
    • ubuntu:20.04.sifからコンテナを立ててログインする
    • -nコマンドがないとGPUが使えない

GPU があるかどうかを確認する

  • $ nvidia-smi
  •   Sat Feb  5 18:33:06 2022
      +-----------------------------------------------------------------------------+
      | NVIDIA-SMI 450.156.00   Driver Version: 450.156.00   CUDA Version: 11.0     |
      |-------------------------------+----------------------+----------------------+
      | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
      | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
      |                               |                      |               MIG M. |
      |===============================+======================+======================|
      |   0  Tesla V100S-PCI...  Off  | 00000000:3B:00.0 Off |                    0 |
      | N/A   37C    P0    36W / 250W |      0MiB / 32510MiB |      0%      Default |
      |                               |                      |                  N/A |
      +-------------------------------+----------------------+----------------------+
    
      +-----------------------------------------------------------------------------+
      | Processes:                                                                  |
      |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
      |        ID   ID                                                   Usage      |
      |=============================================================================|
      |  No running processes found                                                 |
      +-----------------------------------------------------------------------------+
    
  • Ctrl-dでコンテナからログアウトする

Vimを入れる

  • 中身が以下のようなtest.defというファイルを作る
    • Dockerfileのようなものである
    • BootStrap: docker
      From: ubuntu:20.04
      %post
          apt-get update
          apt-get -y upgrade
          apt-get -y install build-essential vim
      
  • $ singularity build --fakeroot test.sif test.def
    • test.defファイルからtest.sifファイルを生成する
  • $ singularity shell test.sif
    • test.sifからコンテナを生成してログイン
  • $ which vim
  •   /usr/bin/vim
    
    • vimが入っていることを確認できた
  • Ctrl-dでコンテナからログアウトする

rustを入れる

  • $ singularity shell --fakeroot --writable test.sif
    • test.sifからコンテナを作ってログインする
  • $ apt-get update
  • $ apt-get install rustc
  • $ mkdir rust; cd rust ; cargo new hello_world --bin
    • rustというディレクトリが作られてhello_worldというパッケージが作られる
    • rustcパッケージがちゃんと入っていることを確認
  • Ctrl-dでコンテナからログアウトする
    • 一旦ログアウトするとrustcパッケージが消え去り、cargoコマンドなどが使えなくなる
    • Singularityのコンテナは構築後に動的にパッケージが追加できない
  • $ singularity build --sandbox --fakeroot test/ test.def
    • --sandboxオプションは環境を動的に変更できるようにする(Dockerライクになる)
  • $ singularity shell --fakeroot --writable test
  • もう一度rustcをインストールする
    • $ apt-get update
    • $ apt-get install rustc
  • Ctrl-dで抜ける
  • $ singularity shell --fakeroot --writable test
    • 再度ログイン
  • $ which cargo
    • /usr/bin/cargo
  • コンテナからログアウトした後でも、動的に加えたパッケージが残っていることが確認できた

daru上でslumを使う

  • $ singularity pull pytorch.sif docker://pytorch/pytorch:latest
  • $ git clone https://github.com/pytorch/examples.git
  • 中身が以下のmnist.sbatchというファイルを作成する
  •   #!/bin/bash
      #SBATCH --job-name mnist 
      #SBATCH --output logs/%x-%j.log
      #SBATCH --error logs/%x-%j.err
      #SBATCH --nodes 1
      #SBATCH --gpus tesla:1
      date
      singularity exec --nv pytorch_latest.sif python examples/mnist/main.py
      date
    
  • $ sbatch mnist.sbatch
    • slurmでJobを投げる
    • 課題ページでは、ジョブ情報を確認する命令がなかったが一応、squeueコマンドでジョブ情報を表示させたら何もジョブがない状態だった
      • GPUを使っても使わなくても同じ結果になりました

以下はオプション


daru上でpodmanを使う

  • $ rm -rf ~/.local
  • $ sudo podman run hello-world
  •   Hello from Docker!
      This message shows that your installation appears to be working correctly.
    
      To generate this message, Docker took the following steps:
       1. The Docker client contacted the Docker daemon.
       2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
          (amd64)
       3. The Docker daemon created a new container from that image which runs the
          executable that produces the output you are currently reading.
       4. The Docker daemon streamed that output to the Docker client, which sent it
          to your terminal.
    
      To try something more ambitious, you can run an Ubuntu container with:
       $ docker run -it ubuntu bash
    
      Share images, automate workflows, and more with a free Docker ID:
       https://hub.docker.com/
    
      For more examples and ideas, visit:
       https://docs.docker.com/get-started/
    

podmanでなにか動かす(オリジナリティが必要)

  • イメージの作成or取得 -> イメージからコンテナを作成という流れ
  • イメージは「Dockerfileから生成する」または「docker image pull : のようなコマンドを実行する」のが考えられる。
  • 以下のようなDockerfileを用意する
    • マルチステージビルドを使う
    • Stage1でGolangコンパイラがシングルバイナリファイルを生成して、Stage2でGoのファイル、コンパイル環境を持たないubuntu環境でStage1のシングルバイナリファイルを実行する
    • #Stage 1
      FROM golang AS build01
      RUN mkdir workspace
      COPY ./hello_world.go /workspace/
      WORKDIR /workspace
      RUN go build hello_world.go
      #Stage 2
      FROM ubuntu
      RUN mkdir workspace
      COPY --from=build01 workspace/hello_world /workspace
      WORKDIR /workspace
      ENTRYPOINT ["./hello_world"]
      
  • 以下のようなhello_world.goを用意
  •   package main
      import "fmt"
      func main() {
          fmt.Println("Hello, World!")
      }
    
  • $ podman image build -f ./Dockerfile -t ubuntu:golang_hello .
  • $ podman container run -it ubuntu:golang_hello
  • Hello, World!が出力された

11.3の感想

  • Singularityで学科のGPU使えるの神
  • ただ動的にコンテナ内のパッケージを自由に管理するDockerとは違って、sandboxオプションを使わないと動的に追加したパッケージをログアウトした状態で使えないのは初めて知った、あくまで検証環境として使うのが良さそう

11.4 Cloudの課題

このページの問題を解く

OSの10.xの課題

OSの講義の10.xの問題を解く

これが正解だぜ(どや)みたいなノリでは書いてないです、ただの平均的な技術力を持っているB2が解いているだけ

課題の取り組みをブログで書いてもいいという許可をもらった上で記事を書いてます

最新の課題内容であることや、答えが正しいかどうかは保証しません

このブログの情報で読者に何らかの不都合が生じても著者は責任は取らないです


10.1 記述問題

このページの問題を解く

以下の課題に関して1200文字程度で考察せよ。

新しくなった学科システムに付いて調べて考察せよ。

    ダメな点および改良点
    使い方
    有効性
    その他の提案

  シス管を増やす方法  シス管にならないための方法

MindMapで分析する

markdownでMindMapが描けるmarkmapを使って新しくなった学科システムに関するMindMapを描いた

# ダメな点及び改良点
## サービス
### hugoを使った静的サイト
- CMSで管理しないので、ブログをする人が少ない
### イントラネット
- 証明書を自動で取得できない
# 使い方
## ssh
- chatanを踏み台にして、各サーバにsshする
## VM
- amaneでVMを作成する
## GPU
- amaneでSingularityコンテナを作成して使用する
## Webサーバ
- Webサイトを運営することができる
## Mattermost
- 学科の講義やイベントに関する情報を共有することができるチャットアプリ
# 有効性
## GPU
### イメージを作成するだけでGPUが使える環境が手に入る
# その他の提案
## sshして作業しないようにする
### Infrastructure as Code
#### 既存
- Podman(Dockerfile, docker-compose.yml)
- Ansible
#### 提案
- Terraform
# シス管を増やす方法
## 既存のプルファクター
- 技術力がつく
- B1,B2ならば先輩と仲良く慣れる
- OSで有利になる
## 提案するプルファクター
- 扱えるVMのスペックの上限をシス管メンバーだけ一般より高くする
# シス管にならないための方法
## 知能情報コースを退学する

↓作成したマインドマップ mindmap


文章にして書く

    現在の学科システムでは、amaneで学科VMを作成して運用したり、Singularityで今回新たに導入されたGPU資源を使ったりすることができる。また、自分のホームディレクトリにpublic_htmlというディレクトリを作成し、そこにhtmlファイルを置くことでWebサイトをデプロイすることができる。umeでmattermostを運営して、知能情報コースの先生と生徒が情報をチャットベースで共有することができる。その中でも有効性に関しては、GPU環境を構築するSingularityが特に優れている。Singularityのイメージを格納するsifファイルをPodman/Dockerイメージから作成するだけで、GPU環境を簡単に構築することができる。


    現在の学科システムの問題の例として、sshで作業するメンテナンスに頼りすぎていることが挙げられる。sshで作業すると、ログを逐次的に記録する必要がでたり、ログを取ったときの作業環境の相違によって作業中に行った操作の冪等性が保証できない場合がある。sshで作業することはある程度避けられないが、ツールを使用してその作業を減らすことができるなら積極的にツールの活用を考えるべきである。学科システムではサーバ(VM, コンテナ, クラウド)の調達は、手作業で行っている。そこで、ツールを使って調達作業を遂行することで、sshをして作業することが少なくすることができると考える。

    ツールの案として、ansibleとterraformの併用とCIツールの使用を提案する。ansibleとterraformはどちらともコードベースでインフラ管理を行うIaC(Infrastructure as Code)ツールである。コードでインフラを管理できれば、Git、Github、Gitlabなどのツールでインフラ環境ごとバージョン管理することができる上に、環境構築手順の冪等性を担保することができる。terraformは宣言的にインフラ構成を定義していて、サーバの調達、ルーティング設定などに優れている。ansibleは既存のサーバに対して、を手続き型(環境構築の手順を逐次的にコードに記述する)で構成管理を行う。ミドルウェアの構築を行う場合は、ansibleがそれに対応するモジュールを用意しているので、nginxサーバなどを構築したい場合はansibleをすることが推奨される。terraformでインフラ層を構築してから、ansibleでミドルウェア層を構築するという流れでインフラ環境を構築すると、それぞれのツールの長所を活かしながら、サーバ調達などで必要だったsshでの手作業を減らすことができると考えられる。また、その手順もコードに記述してCIツールを使って自動化することが可能である。


    シス管を増やす方法を考える。現在、すでに存在するシス管のプルファクターは「技術力がつく」、「先輩と仲良くなれる」、「OSで有利になる」が存在する。これらのプルファクターは、生徒が全員、技術に対しての向学心があるという仮定によって成り立つが、それだけのインセンティブではどうもシス管を増やすには足りないと考えられる。そこで、提案するのが、「扱えるVMのスペックの上限をシス管メンバーだけ一般より高くすることができるようにすること」である。技術に興味があろうとなかろうと、扱うコンピュータの性能は高い方を望むのは自明であるので、向学心よりも強い動機になると考える。

    シス管にならない方法はシンプルに退学することである。必須科目であるOSの課題は、シス管で培う経験と学科システム独自位の知識がないと攻略が難しい場合が多い。「シス管にならなければ、OSの単位を取得することがほぼ無理」、「OSの単位を取得しなければ卒業できない」から三段論法で「シス管にならなければ卒業できない」という結論にいたる。卒業できないのであれば、時間を無駄にしないように退学をする方が良い。

10.2 アンケート

このページの問題を解く

アンケート

岡崎先生のアンケートに答えてください

解答したことをメールで所定の定式で報告すること。サブジェクトのみで良い。

アンケートに答える

mattermostで岡崎先生からアンケートをしているので、指定されたリンクに飛んで問題を答える

  • メガネをかけた白髪の男性が写っている写真がある
    • 写真の中にある言葉
      • 1 やる気
      • 2 創造性
      • 3 共感性
  • アンケート開始を選択
  • 学科の学籍番号とパスワードを入力
  • 「科目の追加・削除」に飛んで受講している科目を登録する
  • 登録する
    • 学年
      • あなたは現在何年次ですか?
        • 2年次
    • 受講科目の選択
      • 2年次対象科目
        • 自分の受講している講義を選択
  • 追加・削除をクリック
  • トップに戻るに飛ぶ
  • 科目名がアンケート解答リンクになっているので、そこに飛ぶ
  • アンケートの質問
    • Q1. 講義の始めのシラバスの説明はありましたか?
    • Q2. シラバスの内容は役に立ちましたか?
    • Q3. 使用したテキストは、講義をうける上で効果的でしたか?
    • Q4. この講義で補助教材は役に立ちましたか?
    • Q5. 教員の説明はわかりやすかったですか?
    • Q6. 板書やスライドのみやすさはどうでしたか?
    • Q7. 教員の声の聞きやすっさはどうでしたか?
    • Q8. なし
    • Q9. なし
    • Q10. この講義の難易度はどうでしたか?
    • Q11. この講義の課題の難易度はどうでしたか?
    • Q12. この講義のために週平均何時間程度費やししましたか?
    • Q13. この講義で難解欠席しました?
    • Q14. あなたはこの講義の「目的」を把握できましたか?
    • Q15. この講義を選択した理由は何ですか?
    • Q16. この講義中のあなたの受講態度は真面目でしたか?
    • Q17. この講義について、あなたは事前学習を行いましたか?
    • Q18. この講義を理解するために必要な基礎学力をどのくらい持っていましたか?
    • Q19. この講義を受けてよかったと思いますか?
    • Q20. 課題、予習、復習を通して、この講義であなた自身の最終的な理解度はどれくらいですか?
    • Q21. この講義の以下に関することについて改善点があれば、具体的にどのように改善してほしいか書いてください。
    • Q22. この講義の感想を書いてください。
  • 講中の講義のアンケートに全て答えたらkono先生にメールをする

10.2の感想

  • あの写真のおじさんは誰だ...?

OSの9.xの課題

OSの講義の9.xの問題を解く

これが正解だぜ(どや)みたいなノリでは書いてないです、ただの平均的な技術力を持っているB2が解いているだけ

課題の取り組みをブログで書いてもいいという許可をもらった上で記事を書いてます

最新の課題内容であることや、答えが正しいかどうかは保証しません

このブログの情報で読者に何らかの不都合が生じても著者は責任は取らないです


9.1 Backup

このページの問題を解く

情報工学を学ぶものは、自分で運営する Web Service の一つや二つを持っているだろう。

自分自身のファイルのバックアップ戦略について Cloud と 自分の Web Service を含めて、提案せよ。

以下のことを考慮すること。

    最低、3重のコピーを維持する必要があるのは何故か。
    ファイル履歴はどこまで持つべきか。
    Apple の Time Capsule が Web Service に対して使用できないのは何故か。
    バックアップに要するコストは妥当か。

答え

自分は技術ブログを運営している。Markdownで書いてhugoでhtmlとcssを生成して、GitHubのレポジトリにアップロードして、Github Pagesで公開している。

バックアップ戦略に関して、バックアップを失う/取れなくなるリスクが、バックアップの作成、リカバリ、保持の段階で現れるということを考えると最低3重のバックアップは取る必要がある。3重とはいえ、バックアップの作成、リカバリ、保持の手段が同一であると意味がない。例えば、Time Capsuleで3つのNASバイスにバックアップを取っても、リカバリの段階でトラブルが起きてしまった場合に3つのNASバイスのうちどれからもデータを復元することができなるなる。つまり、同一の方法でデータを保持することは何台デバイスを増やしても結局は「1つのバックアップ」でしかない。よって、3つの異なる方法でバックアップを3重に取る必要がある。

上記を踏まえた上で自分が取るバックアップ戦略は以下の通りである。

GitHubMarkdownで書いた記事のレポジトリとhugoで生成したhtmlのレポジトリの2つを持っている。本来GitHubはバージョン管理ツールとして使うのだが、GitHub自体はユーザが削除しない限りパブリックリポジトリを利用できるまま保とうとするのでバックアップの手段として捉えることができる。データを失うリスクとして、レポジトリの中身がDMCAテイクダウンポリシー、コミュニティガイドライン、あるいは利用規約に反するとGitHubに判断されてしまうことでレポジトリを自由に利用できなくなることが考えられる。

学科VMWebサービスを維持するのに必要なファイルを転送する。sshさえできればrsyncコマンドで復元が簡単にできる。問題は学科サーバがシャットダウンしてしまったり、学科サーバ側の問題でVMのデータが失われてしまう場合は復元できなくなることである。

SSDでファイルを保存する。バックアップできなくなるリスクはSSDの故障や紛失である。SSDは1万円ほどするが、自分の運営している技術ブログは未来には一種のポートフォリオとして使えるかもしれないので、データを失ったときの損害の方が大きいので、コストは妥当と言える。


9.1の感想

  • 色々考えてしまった

9.2 素因数分解を使った暗号化

このページの問題を解く


二重鍵暗号には二つの関数EとDを使う。

   E(D(x)) = x 
   D(E(x)) = x

となる。
E/D にはフェルマーの小定理を使った方式がある。mod は剰余類による演算を表す。剰余類では可換則や分配則がそのまま成り立つことを思い出そう。

p が素数で, a が p の倍数でない正の整数のとき

a^(p−1) ≡ 1 mod p

つまり、剰余類で指数乗の計算をすると、素数-1の時に1に戻ることを利用する。
二つの素数3,5を使った以下の関数

E(x)=x^11 mod15
D(x)=x^3 mod15

を二重鍵暗号として使うことができる。15は3と5の積である。11と3は、それを使って求めた鍵ペアである。(鍵ペアの条件は、 (3-1)(5-1) = 8 と互いに素な鍵k1、
 なk2だが、ここでは使わない)
7をメッセージとして、暗号化したら y になったとする。y を求め、以下の式を検算せよ。

   D(E(7)) = 7
   E(D(y)) = y

問題の目的

二重鍵暗号の関数EとDを使用して、メッセージ7を暗号化し、その結果をyとした場合のyの値を求め、さらに D(E(7))とE(D(y)) の関係を検算すること

関数EとDがそれぞれ公開鍵と秘密鍵の役割を果たしていて、

暗号化と復号のプロセスを通じて、これらの鍵がどのように動作するかを理解するというのがこの課題の最終目的

自力で計算するパターン

暗号化関数Eの計算

まず、メッセージ7を暗号化するための関数 E(7) を計算する E(7) = 711 mod 15

この計算を簡単にするために、与えられた情報 72 ≡ 4 mod 15 と 42 ≡ 1 mod 15 を利用する

まず、 72 ≡ 4 mod 15 という情報が与えられている

これは、7を2回掛けると、15で割った余りが4になることを示している

(a ≡ b mod cは本来、aとbをcで割ると同じ余りが出るという意味なので注意)

次に、 42 ≡ 1 mod 15 という情報も与えられている

これは、4を2回掛けると、15で割った余りが1になることを示している

711 は、 (72)4 × 72 × 7 と分解できる

72 ≡ 4 mod 15 なので、 (72)4 ≡ 44 mod 15 となる

さらに、 44 は、 (42)2 として考えることができる そして、 42 ≡ 1 mod 15 なので、 44 ≡ 12 ≡ 1 mod 15 となる

このように、与えられた情報を利用して、大きな指数の計算を簡単な計算に分解することができる

7^11
≡ 7^8 × 7^2 × 7 mod 15
≡ 1 × 4 × 7 mod 15
≡ 28 mod 15
≡ 13 mod 15

E(7) = 13 なる これは、メッセージ7を暗号化した結果yの値である

復号化関数Dの計算

次に、暗号化されたメッセージ13を復号化するための関数 D(13) を計算する D(13) = 133 mod 15

13^3 mod 15
≡ (-2)^3 mod 15 (∵ 13 ≡ -2 mod 15)
≡ -8 mod 15
≡ 7 mod 15

この計算の結果、 D(13) = 7 となる

D(E(7)) = 7 および E(D(y)) = y の関係が成り立つことが確認できた

Perlスクリプトを使うパターン

Perlスクリプトを読む

#!/usr/bin/perl
sub modpow {  #   b^e mod m
    my ($b, $e, $m) = @_;
    my $result = 1;
    while ($e > 0) {
       if (($e & 1) == 1) { $result = ($result * $b) % $m;  }
       $e >>= 1;
       $b = ($b * $b) % $m;
    }
    return $result;
}
print &modpow(@ARGV),"\n";

モジュラ指数計算(be mod m)を行うためのもの 具体的には、整数bのe乗をmで割った余りを計算する

暗号化関数Eの計算

スクリプトをmodpow.plとして保存して以下のコマンドを実行

$ perl modpow.w 7 11 15

コマンドの引数の7がb, 11がe, 15がmに対応し、7の11畳を15で割ったときの余りを計算する

$ perl modpow.pl 7 11 15

13

復号化関数Dの計算

$ perl modpow.pl 13 3 15

7

D(E(7)) = 7 および E(D(y)) = y の関係が成り立つことが確認できた

9.3 セキュリティ対策(2022年度)

このページの問題を解く


ツール

セキュリティ対策にはいろいろあるが、
   Web Service 
   個人のPC
   無線LAN基地局

などが対象になる。
Virus checker, log 監視ツール、システム管理ファイル監視システム などが、これらに対して、どのように有効か、有効でないかを考察せよ。
  • ウイルスチェッカー:
  • 個人のPCにおいては、ウイルスチェッカーは極めて重要である。これは、個人のPCが頻繁にインターネットを介して様々なリスクにさらされるため、マルウェアからシステムを保護する基本的な手段となる。 Webサービスの文脈では、ウイルスチェッカーは直接的な防御手段としては限定的であるが、ユーザーがアップロードするファイルをスキャンすることで二次的な防御線として機能する場合がある。 無線LAN基地局は、ウイルスチェッカーの対象とは一般的にならないが、管理システムにマルウェアが侵入することによるリスクを軽減するため、間接的な保護手段として利用できる。
  • ログ監視ツール:
  • 個人のPCでは、ログ監視は一般的な用途には用いられないが、セキュリティに精通したユーザーやIT専門家が問題の診断やインシデントの調査に用いることがある。 Webサービスにおいては、ログ監視ツールは非常に価値が高く、不正アクセスや異常なデータトラフィックパターンを検出する助けとなる。これにより、インシデント対応の速度と効率が向上する。 無線LAN基地局では、ログ監視は不審なアクティビティを識別し、セキュリティインシデントの早期発見に役立つ。
  • システム管理ファイル監視システム:
  • 個人のPCにおいては、一般ユーザーにとっては必要以上の機能であることが多いが、専門的な使用では重要な変更を追跡し、システムの安全性を維持するのに有効である。 Webサービスでは、この種の監視は不正アクセスや不正変更を迅速に検出する上で欠かせない。特に、構成ファイルやシステムファイルの無許可の変更を監視することは、サービスの安全性を確保するために重要である。 無線LAN基地局の文脈では、システム管理ファイルの監視は、ファームウェアの変更や不正な構成変更を検出するために、ある程度有効であるが、多くの場合、他のセキュリティメカニズムと組み合わせる必要がある。

PPAP

メールで資料を送る時に zip に password を付けて、別便でパスワードを送る方法がある。この方法がどうしてだめかを考察せよ。

PPAPとは、ZIPファイル送付と同じ経路でパスワードを自動で送るメールのやり取りの方式

P:Passwordつきzip暗号化ファイルを送ります
P:Passwordを送ります
A:Aん号化(暗号化)
P:Protocol

内閣府PPAPをファイルの送信方法として採用していたが、令和2年11月24日にPPAPを廃止すると平井内閣府特命担当大臣が発表した。(平井内閣府特命担当大臣記者会見要旨 令和2年11月24日)

  • 同じ経路を通るので同時に盗聴される危険性があり、無意味
    • 暗号化されてないのが原因
  • 暗号化されたzipファイルにウィルスが隠れている可能性がある(参考: Emotetの事例)
    • 送信されたデータの本人性を担保されてない、改ざんが行われていないことがわからない

よって「パスワード付きzipファイルじゃなくて、S/MIME暗号化して盗聴されても大丈夫なようにして、デジタル署名を付けて本人性と改ざんが行われていないことを証明すればいいじゃん。」みたいなことが書かれているといいと思う。


S/MIME

標準的な暗号化メールである S/MIME を使ってみよう
  1 S/MIME用の証明書(二重暗号鍵)を手に入れる (いろいろ方法はあるらしい)

           GMail はデフォルトであるかも

  2 それでサイン(署名)したメールを送る
       3  署名に公開鍵が入っているので、それで返信を暗号化して送信する

サイン付きのメールで課題を提出せよ

1 S/MIME用の証明書(二重暗号鍵)を手に入れる

  • 結論、actalisとthunderbirdを入れると楽で自己証明書でやろうとすると詰むかも、できた人すごい
  • Actalisで証明書を手に入れる
  • Apply for a free S/MIME certificate
    • メールアドレス(ドメインie.u-ryukyu.ac.jpのやつ)を入力して次へ
    • 確認コードがメールアドレスに送信されるのでそれをコピって貼り付け、利用規約的なやつのチェックボックスにチェックを付けてSubmit
    • 成功すると入力したメールアドレスに証明書が送られる、証明書のパスワードがwebページに表示されるのでメモる
    • メールを開いて、zipファイルをダウンロードして展開する
    • PKCS12_Credential_e205723@ie.u-ryukyu.ac.jp.pfxみたいなファイルが出てきたらOK

2 それでサイン(署名)したメールを送る

  • thunderbirdをダウンロードして開く
    • 学科メールアドレスでログイン→ Settings(歯車マーククリック) → Account Settings → End-To-End Encryption → Personal certificate for digital signing → Manage S/MIME Certificates → Your Certificates → Import... → PKCS12_Credential_e205723@ie.u-ryukyu.ac.jp.pfxを選択 → Personal certificate for digital signing の Selectボタンを押す → e205723@ie.u-ryukyu.ac.jp [Serial Number]を選択
  • Personal certificate for encryption も同じ証明書に設定しておく
  • InboxタブからWriteを選択すると、メールのドラフトが書ける、画面上にS/MIMEという項目があるので詳細を設定するために下ボタンをクリックして、Digitally Signにだけチェックを付ける
  • 課題の解答をメールを送信する
    • これでうまくいくかは知らないけど

3 署名に公開鍵が入っているので、それで返信を暗号化して送信する

kono先生から証明書付きのメールはもらったけど、どうやってimportするの...?

公開鍵が先生から直接添付で送られるようにしないとダメ説がある。

一応、「暗号化はなし、署名だけで課題提出OKですか」と聞いたら

そういうこと。そう書いてあるでしょ? 自分で鍵を生成あるいは取得して、それで署名して送る。

と言っていた。

よって、3はしなくてもいいらしい。


9.3 Side Channel Attack(2021年度)

このページの問題を解く

Side Channel Attach とは何かを100文字程度で説明せよ。

例示した Sice Channel Attach に対する対策を示せ。

Side Channel Attackとは

サイドチャネル攻撃とは、プログラムの欠陥を直接狙うのではなく、システムのプログラムを実行したときの処理時間やハードウェアの物理的な変化を観測して得られた情報から解析を行って暗号化されたデータを盗み取る攻撃である。Side Channel Attackの種類は情報の収集・解析方法によって多岐に分かれる。


対策

前述したと通り、Side Channel Attackには攻撃方法によって細かく種類が分かれるので、それぞれの攻撃方法によって対策の仕方が分かれる。今回はメッセージ長を用いるCompression Side Attackを取り上げて、対策を示す。Compression Side Attackは、圧縮が絡む処理で攻撃者が自分のデータを送信して秘密情報と一緒に圧縮して、圧縮の出力の長さから秘密情報の推測を行う攻撃手法である。

データ圧縮を行うとデータの冗長性を排除することである。LZ77という圧縮アルゴリズムを例に説明する。LZ77はデータに含まれる記号列が過去に出現したかどうかを調べ、出現していればその位置と長さの値で置き換えるアルゴリズムである。以下の文字列を圧縮する場合を考える。

「I thought what I’d do was, I’d pretend I was one of those deaf-mutes.」

この文字列には「I」、「'd」、「was」という文字列を複数含む。これらのような同じ文字列に相当するデータを1つだけ残して、そのコピーをどこに配置するかを示す指示を出力してLZ77はデータに圧縮を行う。

攻撃者のデータを「A」、推測したい暗号情報を「B」とし、AとBを一緒に圧縮したときにAとBに同じ部分があれば、データの圧縮が効くということになるので、圧縮の出力の長さからBを予測することができる。このようなBを予測する推測材料のことを圧縮オラクルと呼ぶ。

対策についてだが、TLS1.2の仕様[1]のSrction 6から引用して提示する。

Any protocol designed for use over TLS must be carefully designed to
deal with all possible attacks against it.  As a practical matter,
this means that the protocol designer must be aware of what security
properties TLS does and does not provide and cannot safely rely on
the latter.
Note in particular that type and length of a record are not protected
by encryption.  If this information is itself sensitive, application
designers may wish to take steps (padding, cover traffic) to minimize
information leakage.

Compression Side AttackはTLSプロトコルのレコードの型と長さが暗号化によって保護されない脆弱性を突いている攻撃だが、これはプロトコルの欠陥ではなく、プロトコルの限界である。仕様によれば、アプリケーションの設計者がパディングやトラフィック保護をすることで対策することを推奨されてる。

[1] The Transport Layer Security (TLS) Protocol, https://www.nic.ad.jp/ja/tech/ipa/RFC5246EN.html


9.3の感想

  • 攻撃と対策はイタチごっこになるみたいな話をよく聞くがこのレベルまで議論を深堀りしたら、そりゃイタチごっこになるわー...って思いました

OSの8.xの課題

OSの講義の8.xの問題を解く

これが正解だぜ(どや)みたいなノリでは書いてないです、ただの平均的な技術力を持っているB2が解いているだけ

課題の取り組みをブログで書いてもいいという許可をもらった上で記事を書いてます

最新の課題内容であることや、答えが正しいかどうかは保証しません

このブログの情報で読者に何らかの不都合が生じても著者は責任は取らないです


8.1 fragmentationを実際に見てみる

このページの問題を解く

malloc(メモリのアロケーション)したときのfragmentation(メモリ内でデータが断片化される現象)を測る課題

コードが2つ用意されている。

malloc_test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


long MEMORY   = 16000*1000*100;  // test memory limit
long ALLOCATED   = 0;
long COUNT   = 8000;   // repeat count
long ACTIVE  = 1000;   // number of memroy segment
long MAXSIZE = 400000;  // 
long MINSIZE = 400;
long MIN_UNIT = 16 ;

typedef
struct mem_list {
    void *address;
    long size;
    struct mem_list *next;
} MEM_LIST, *MEM_LIST_PTR;


void
print_mem_list(MEM_LIST_PTR m)
{
    MEM_LIST_PTR n;
    if (!m) return;
    n = m->next;
    for(;n&&n!=m;n=n->next) {
       // some malloc returns very large value. remove it to keep output clear
      //     if (n->address && ! (((long)n->address) & 0x700000000000))
         printf("%p 0x%08lx\n",n->address,n->size);
    }
}

void
die(char *msg)
{
    fprintf(stderr,"%s\n",msg);
    exit(0);
}

void
option(long ac, char *av[])
{
    long i = 1;
    while(ac>1) {
    if (av[i][0] == '-') {
        switch (av[i][1]) {
        case 'c': COUNT = atol(av[++i]); break;
        case 'a': ACTIVE = atol(av[++i]); break;
        case 'u': MIN_UNIT = atol(av[++i]); break;
        case 'm': MAXSIZE = atol(av[++i]); break;
        case 'l': MINSIZE = atol(av[++i]); break;
        case 'M': MEMORY = atol(av[++i]); break;
        }
            ac--;
    }
    ac--; i++;
    }
}

int
main(int ac, char *av[])
{
    MEM_LIST mlist;
    MEM_LIST_PTR last = &mlist;
    MEM_LIST_PTR new;
    long i,size;

    option(ac,av);

    mlist.address = NULL;
    mlist.size = 0;
    for(i=0;i<ACTIVE;i++) {
    new = (MEM_LIST_PTR)malloc(sizeof(MEM_LIST)); 
    if (!new) die("malloc error");
    new->address = NULL;
    new->next = NULL;
    last->next = new;
    last = new;
    }
    last->next = &mlist;

    for(i=0;i++<COUNT;last=last->next) {
    size = ((random()%(MAXSIZE-MINSIZE))+MINSIZE)*MIN_UNIT;
    if (last->address) {
            ALLOCATED -= last->size;
            last->size = 0;
        free(last->address);
        last->address = 0;
    }
        if ( ALLOCATED + size > MEMORY) {
             // printf("%ld %ld size error\n",ALLOCATED , size);
             // continue;
        }
    last->size = size;
    last->address = (void *)malloc(size);
    if (!last->address) die("malloc error");
        memset(last->address, random(), size);
        ALLOCATED += size;
    }
    print_mem_list(&mlist);
    return 0;
}

display_js.pl

#!/usr/bin/perl

use strict;

my $width = 1200;
my $step = $width;
my $m_height = 3;
my $base = 0;


# my $malloc_test = "./malloc_test -m 100000 -l 10";
# my $malloc_test = "./malloc_test -m 10000000 -l 10";
my $malloc_test = "ssh amane ./malloc_test -m 10000000 -l 10";
# my $malloc_test = "ssh amane ./malloc_test -m 10000000 -l 10 -u 16";
# my $malloc_test = "ssh amane ./malloc_test -m 1000000 -l 10 -u 16";

open(TEST,"$malloc_test | sort|") or die("Cannot run $malloc_test $!\n");
my ($min,$max);
my @adr;
my @size;

while(<TEST>) {
    chop;
    my ($adr,$size) = split;
    $adr = eval($adr);
    $size = eval($size);
    $min = $adr if ($min==0||$adr<$min);
    $max = $adr if ($adr>$max);
    push(@adr,$adr);
    push(@size,$size);
}

# print "$width $max $min\n";
my $scale = $step/($max-$min);

# print "$scale $width $max $min\n";
# $m->update;
# exit;

my @data;

for(my $i =0; $i<$#adr;$i++) {
    my $adr = ($adr[$i]-$min)*$scale;
    my $size = $size[$i]*$scale;

    my $y = int($adr/$width) * $m_height + $base;
    my $x = int($adr)%$width;

# print STDERR "$adr $size $x $y\n";
    push(@data,  $x,$size );
}

my $xydata = "[" . join(",",@data) ."]";
my $count = int ($#data / 2);

my $js = << "EOFEOF";
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 World Map</title>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>
        #halfpage{
            background: "white";
            overflow-x: scroll;
        }
    </style>
  </head>
  <body>
  <button type="button" onclick="shrink()" >shrink </button>
  <button type="button" onclick="enlarge()" >enlarge</button>

  <script type="text/javascript">

  var elements = Array.from(Array($count).keys());
  var xydata = $xydata;
  var scale = 1;

  var shrink = function(){
      scale = scale * 0.7 ;
      svgContainer.remove();
      svgUpdate(scale,"green");
  }

  var enlarge = function(){
      scale = scale / 0.7 ;
      svgContainer.remove();
      svgUpdate(scale,"green");
  }

  var svgUpdate = function(scale,color){
     svgContainer = d3.select("body").append("svg")
    .attr("width", 1200 * scale)
    .attr("height", 300)
    .attr("id", "halfpage");

    svgContainer.selectAll("rects1").data(elements).enter().append("rect")
                .attr("width", function(d,i) { return xydata[i*2+1] * scale;} )
                .attr("height", 20)
                .attr("y", 175)
                .attr("x", function(d,i) { return xydata[i*2] * scale; })
                .style("fill", color)
                .style("stroke-width", 1)
                .style("stroke", "black");
  }

   svgUpdate(scale,"green");

    </script>
  </body>
</html>
EOFEOF

print "$js";


# end

さっきのmalloc_test.cのテストファイルを実行したときのfragmentationを可視化したときのhtmlを標準出力で出力するスクリプト


8.2 Page テーブルのGUI demo

このページの問題を解く

[JavaScriptで書いた Page テーブルのGUI demo](https://ie.u-ryukyu.ac.jp/~kono/lecture/os/os08/vm/vm.html) を、もう少し見栄えのするように改良したい。

    わかりやすい表示 (線ぐらいでて欲しいよね?)
    白黒かよ!
    ページテーブルの中身の書き換え(当然...)
    アニメーション
    多段の場合
    仮想記憶が入る場合
    inverted page table

などの改良が考えられる。なんらかの改良をして見よう。

GUIの改善点を見つける

  • JavaScriptで書いた Page テーブルのGUI demoを見てみよう
  • 改良できそうなことを列挙する
    • 矢印を表示していない
    • 白黒だけ
    • ページテーブルの中身の書き換えしていない
    • アニメーションがない
    • 多段の場合の表示がない
    • 仮想記憶が入る場合がない
  • 矢印を表示していないところを改善することにした

コードを編集して矢印を表示させる

  • GUIのhtmlファイルをダウンロードする
  • scriptタグ内にjavascriptのコードが記述されているので実装を見てみる
<script type="application/x-javascript">

var width = 30;
var scale = 2;
var address = 0;
var vmsize = 6;
var psize = 4096;
var pagetableString = "";
var pagetable = new Array(7);

var llow;
var lhigh;
var plow;
var phigh;

for(var i=0;i<vmsize;i++) {
    var v = Math.floor(Math.random()*vmsize);
    pagetable[i] = v;
    pagetableString += v.toString(16) +"\n";
}

function draw() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    canvas.ontouchmove = TouchXY;

    memory(ctx,10,10);
    memory(ctx,250,10);
    pointer(ctx,80,50);
    pointer(ctx,170,50);
    page(ctx, 130,120);

}

function text(name,text,x,y)
{
    // ctx.fillText(text,x*scale,y*scale);
    var X=document.body.scrollLeft+x*scale;
    var Y=document.body.scrollTop+y*scale;
    document.all[name].style.left=X+20;
    document.all[name].style.top=Y-5;
    document.all[name].innerText=text;
}


function MouseXY()
{
    var x = event.x/scale;
    var y = event.y/1;
    XY(x,y);
}

function TouchXY()
{
    var canvas = document.getElementById("canvas");
    var x =  event.pageX - canvas.offsetLeft;
    var y =  event.pageY - canvas.offsetTop
    XY(x,y);
}

function XY(x,y)
{
    if (10<=x&&x<=110&&10<=y&&y<=310) {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    ctx.clearRect(10,10,10+60,410);
    memory(ctx,10,10);
    ctx.clearRect(250*scale,10,250*scale+60,410);
    memory(ctx,250,10);

    strokeRect(ctx,10,y,10+width,y+2);
        address = (y-10)*100;
        physical = translate(address);
    y = physical/100+10;
    strokeRect(ctx,250,y,250+width,y+2);
    update1();
    }
}

function update1() 
{
    text("Logical","logical address = 0x"+address.toString(16),10,150);
    text("Physical","physical address = 0x"+physical.toString(16),250,150);
    text("Pagetable",pagetableString,130,70);
    text("lhigh",lhigh.toString(16),90,32);
    text("llow",llow.toString(16),120,32);
    text("phigh",phigh.toString(16),90+90,32);
    text("plow",plow.toString(16),120+90,32);
}

function translate(adr)
{
    lhigh = Math.floor(adr/psize);
    llow = adr%psize;
    phigh = pagetable[lhigh];
    plow = llow;
    return phigh*psize+plow;
}

function strokeRect(ctx,x,y,x1,y1)
{
    return ctx.strokeRect(x*scale,y,(x1-x)*scale,(y1-y)*scale);
}

function pointer(ctx,x,y) {
    var dy = 10;
    strokeRect(ctx,x,y,x+width,y+dy);
    x += width;
    strokeRect(ctx,x,y,x+width,y+dy);
}

function memory(ctx,x,y) {
    var dy = 40;
    var dx = 40;

    for(var i=0;i<vmsize-1;i++) {
    strokeRect(ctx,x,y,x+width,y+dy);
    y += dy;
    }
}

function page(ctx,x,y) {
    var dy = 20;
    var dx = 40;

    for(var i=0;i<vmsize-1;i++) {
    strokeRect(ctx,x,y,x+width,y+dy);
    y += dy;
    }
    return box;
}


  </script>
  • 実装を見ると基本的にはXY()関数を編集すればいいことがわかる
  • 矢印を表示させるスクリプトを書けば終了なのだが、味気ないのでChapGPTにコードを生成させてみることにした
  • 以下のような命令をChatGPTに投げる
    • javascriptで(fromX, fromY)から(toX, toY)までの矢印をctx上に描画するスクリプトを生成してください
  • ChatGPTは以下の関数を生成する
    • ちなみにまじで生成した関数に対して1ミリも調整や編集することなく課題を解くのに使うことができた
function drawArrow(ctx, fromX, fromY, toX, toY) {
  // 矢印を描画するために必要な変数を計算する
  var headLength = 10;
  var angle = Math.atan2(toY - fromY, toX - fromX);
  // 矢印を描画するために必要な座標を計算する
  var x1 = toX - headLength * Math.cos(angle - Math.PI / 6);
  var y1 = toY - headLength * Math.sin(angle - Math.PI / 6);
  var x2 = toX - headLength * Math.cos(angle + Math.PI / 6);
  var y2 = toY - headLength * Math.sin(angle + Math.PI / 6);
  // 矢印を描画する
  ctx.beginPath();
  ctx.moveTo(fromX, fromY);
  ctx.lineTo(toX, toY);
  ctx.lineTo(x1, y1);
  ctx.moveTo(toX, toY);
  ctx.lineTo(x2, y2);
  ctx.stroke();
}
  • 関数をコピペする
  • XY()関数を以下のように編集した
function XY(x,y)
{
    if (10<=x&&x<=110&&10<=y&&y<=310) {
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        ctx.clearRect(0,0,159,canvas.height);
        ctx.clearRect(0,71,259,canvas.height);
        ctx.clearRect(321,71,canvas.width,canvas.height);
        ctx.clearRect(461,0,canvas.width,canvas.height);
        memory(ctx,10,10);
        memory(ctx,250,10);
        strokeRect(ctx,10,y,10+width,y+2);
        address = (y-10)*100;
        physical = translate(address);
        drawArrow(ctx,85,y+1,150,60);
        drawArrow(ctx,195,75,250,130+(lhigh*20));
        y = physical/100+10;
        drawArrow(ctx,330,130+(lhigh*20),375,75);
        drawArrow(ctx,465,60,495,y+1);
        strokeRect(ctx,250,y,250+width,y+2);
        update1();
    }
}
  • 以上でコードの編集終了、ChatGPT怖すぎる
  • amaneのホームディレクトリにpublic_htmlというディレクトリを作って(すでにあるなら無視)、その中に8-2というディレクトリを作り、index.htmlというファイルとしてhtmlファイルを置けば
  • このような感じで改善したGUIを配信できる
    • URLはこんなかんじ→https://ie.u-ryukyu.ac.jp/~e205723/8-2/

8.3 Perfomance of Demand Paging

このページの問題を解く

(1) TLB miss してpage が実メモリにあった場合の平均page-falut処理時間が1μ sec, memory access 1.0n sec の時に、page fault rate が、1% の時の effective access time を求めよ。

(2) TLB miss してpage が仮想メモリにあった場合の平均page-falut処理時間が1m sec, memory access 1.0n sec の時に、page fault rate が、0.001% の時の effective access time を求めよ。

(3) 実メモリ上のpage fault rate が、0.001% の時の effective access time を求めよ。性能低下を 20%以下にするためには、page fault rate はいくらでなければならないか。

    effective access time = (1-p) x (memory access time) + p x (page fault time)

この結果を200文字程度に簡潔にまとめよ。

(1)

effective access time = (1-p) x (memory access time) + p x (page fault time)

この式に当てはめる変数の中身は(1)の場合

p=0.01、memory access time=1.0n sec、page fault time=1μ sec = 1000.0n secである

式に代入すると

effective access time = (1 - 0.01) x (1.0n sec) + 0.01 x (1000.0n sec) = 10.99n sec

答えは10.99n sec


(2)

(1)では、TLB miss したときにpage が実メモリにあったが、(2)ではpage が実メモリにある。

そのため、平均page-falut処理時間が(1)では1μ secだったのに対し、(2)では1m sec(1μ secの1000倍)である。

effective access timeを求める式に、p=0.00001、memory access time=1.0n sec、page fault time= 1m sec = 1000000.0n sec`を代入すると

effective access time = (1 - 0.00001) x (1.0n sec) + 0.00001 x (1000000.0n sec) = 10.99999n sec

答えは10.99999n sec


(3)

実メモリ上のpage fault rate が、0.001% の時」と書かれているので、「TLB miss してpage が実メモリにある」と考える。

effective access timeを求める式に、p=0.00001 、memory access time=1.0n sec、page fault time=1μ sec = 1000.0n secを代入すると

effective access time = (1 - 0.00001) x (1.0n sec) + 0.00001 x (1000.0n sec) = 1.00999n sec

effective access timeは1.00999n secになる。

memory access time=1.0n sec、page fault time=1μ sec = 1000.0n secを固定して、性能低下(率)とpage fault rateの関係を考える。

性能低下率を1 - (effective access time/page fault rate=0のeffective access time)と定義する。

性能低下率が20%(=0.2)以下であることを数式で表現すると、

0.2 >= 1 - {(1-p) x (memory access time) + p x (page fault time)}/memory access timeである

値を代入すると

0.2 >= 1 - {(1-p) x (1.0n sec) + p x (1000.0n sec)}/1.0n sec = -999 x p

となる。

0.2 >= -999 x pという不等式をこねくり回してp <= 2/9990 = 0.0002002002 = 0.02002002%に変形する。

結果、性能低下を20%以下に抑えるには、page fault rateは 0.02002002%以下でなければならないという結論になる。

有効数字とか全然意識せずに端数エグいことになっている計算ですが…ヨシザウルスの愛嬌に免じてということで…


8.4 mlock

このページの問題を解く


8.5 mmap によるコピー

このページの問題を解く


OSの7.xの課題

OSの講義の7.xの問題を解く

これが正解だぜ(どや)みたいなノリでは書いてないです、ただの平均的な技術力を持っているB2が解いているだけ

課題の取り組みをブログで書いてもいいという許可をもらった上で記事を書いてます

最新の課題内容であることや、答えが正しいかどうかは保証しません

このブログの情報で読者に何らかの不都合が生じても著者は責任は取らないです


7.1 Busy Waitの問題

このページの問題を解く

Java FX を用いて、mouse の動きを real-time で記録し、再現するGUIアプリケーションを作成せよ。

(a) CPU を消費するができるだけ、細かくmouseの動きを再現する

(b) CPU を消費しないが、mouseの動きを再現は荒い

この二つの version を作成せよ。

application を動かしながら、top で cpu usage 測り、その動きを調べよ。top のオプションを使う。

環境構築

  • JavaのダウンロードサイトからJavaをダウンロードし、インストールする
  • JavaFXのダウンロードサイトからSDKを選択してダウンロードする。
    • ファイルを展開して/Library/Java/JavaVirtualMachinesに置く
      • $ sudo mv /Users/yoshisaur/Downloads/javafx-sdk-19.0.2.1 /Library/Java/JavaVirtualMachines/
  • $ javac --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media *.javaコンパイルできて、
  • $ java --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media *.javaでファイルが実行できる

aを実装する

  • aをMouseRecorderA.javaというファイルで実装する
    • Timelineクラスを使ってアニメーションの割り込み間隔を1msくらいの細かさで設定してcpuの消費を上げている
  • $ javac --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderA.javaコンパイル
  • $ java --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderA.javaで実行
  • uiの見た目は、以下のスクショのような感じになる、青い点はカーソルを再現してるときの点である、bのほうも同じになる
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.List;

public class MouseRecorderA extends Application {
    private List<double[]> coordinates = new ArrayList<>();
    private boolean recording = false;
    private int timelineIndex = 0;
    private int frameIndex = 0;
    private Canvas canvas;
    private GraphicsContext gc;
    private double prevX = 0;
    private double prevY = 0;
    private double currentX = 0;
    private double currentY = 0;

    public static void main(String[] args) {
        launch(args);
    }

    Timeline timeline = new Timeline(
            new KeyFrame(Duration.millis(1), new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    if (timelineIndex < coordinates.size()) {
                        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
                        double[] point = coordinates.get(timelineIndex);
                        gc.setFill(Color.BLUE);
                        gc.fillOval(point[0], point[1], 5, 5);
                        timelineIndex++;
                    } else {
                        timeline.stop();
                    }
                }
            })
    );

    @Override
    public void start(Stage primaryStage) {
        canvas = new Canvas(800, 600);
        gc = canvas.getGraphicsContext2D();

        Button startButton = new Button("Start Recording");
        startButton.setOnAction(event -> {
            recording = true;
            coordinates.clear();
            timelineIndex = 0;
        });

        Button stopButton = new Button("Stop Recording");
        stopButton.setOnAction(event -> recording = false);

        Button replayButton = new Button("Replay");
        replayButton.setOnAction(event -> {
            timelineIndex = 0;
            timeline.setCycleCount(coordinates.size());
            timeline.play();
        });

        canvas.setOnMouseMoved(event -> {
            if (recording) {
                frameIndex = 0;
                currentX = event.getX();
                currentY = event.getY();
                if (prevX == 0 && prevY == 0) {
                    for (frameIndex = 0; frameIndex < (16); frameIndex++) {
                        coordinates.add(new double[]{currentX, currentY});
                    }
                } else {
                    for (frameIndex = 0; frameIndex < (16); frameIndex++) {
                        coordinates.add(new double[]{((currentX - prevX) * ((frameIndex + 1) / (16))) + prevX, ((currentY - prevY) * ((frameIndex + 1) / (16))) + prevY});
                    }
                }
                prevX = currentX;
                prevY = currentY;
            }
        });

        VBox root = new VBox();
        root.getChildren().addAll(canvas, startButton, stopButton, replayButton);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

bを実装する

  • bをMouseRecorderB.javaというファイルで実装する
    • AnimationTimerクラスを使っている
    • 60fpsで動作する、aの実装よりも低いフレームレート
  • $ javac --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderB.javaコンパイル
  • $ java --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderB.javaで実行
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

public class MouseRecorderB extends Application {
    private List<double[]> coordinates = new ArrayList<>();
    private boolean recording = false;
    private int index = 0;
    private Canvas canvas;
    private GraphicsContext gc;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        canvas = new Canvas(800, 600);
        gc = canvas.getGraphicsContext2D();

        Button startButton = new Button("Start Recording");
        startButton.setOnAction(event -> {
            recording = true;
            coordinates.clear();
            index = 0;
        });

        Button stopButton = new Button("Stop Recording");
        stopButton.setOnAction(event -> recording = false);

        Button replayButton = new Button("Replay");
        replayButton.setOnAction(event -> {
            index = 0;
            AnimationTimer timer = new AnimationTimer() {
                @Override
                public void handle(long now) {
                    if (index < coordinates.size()) {
                        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
                        double[] point = coordinates.get(index);
                        gc.setFill(Color.BLUE);
                        gc.fillOval(point[0], point[1], 5, 5);
                        index++;
                    } else {
                        stop();
                    }
                }
            };
            timer.start();
        });

        canvas.setOnMouseMoved(event -> {
            if (recording) {
                coordinates.add(new double[]{event.getX(), event.getY()});
            }
        });

        VBox root = new VBox();
        root.getChildren().addAll(canvas, startButton, stopButton, replayButton);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

topコマンドでcpu usageを測る + グラフにする

  • 以下のようなbashスクリプトを用意した
    • 名前はget_cpu_usage.shにした
    • 30秒間、1秒ごとにtopコマンドで表示される、user、sys、idleの項目の数値を出力する
#!/bin/bash

echo 'sec,user,sys,idle'

second=0

while [ $second -lt 30 ]
do
    echo -n "$second,"
    top -l 1 -n 5 -s 0 | grep "CPU usage" | sed 's/.* usage: \(.*\) user, \(.*\) sys, \(.*\) idle.*/\1,\2,\3/' | sed 's/%//g'
    sleep 1
    second=$((second+1))
done
  • terminal以外のアプリケーションを終了しておく
  • $ bash get_cpu_usage.sh > cpu_usage_a.csv$ bash get_cpu_usage.sh > cpu_usage_b.csvを実行する
    • その前に$ java --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderA.javaや- $ java --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-19.0.2.1/lib --add-modules=javafx.controls,javafx.fxml,javafx.media MouseRecorderB.javaを実行して、Start Recordingボタンを押しておく
    • bashスクリプトが実行されたら、なるべく激しめにマウスをcanvas領域内で動かしてマウスの動きを記録する、15秒くらいしたらStop Recordingボタンを押して、Replayをする
  • csvファイルをグラフ化するPythonスクリプトを書く、ファイル名を cpu_usage_plot.pyにした
  • $ python3 cpu_usage_plot.py cpu_usage_a.csv$ python3 cpu_usage_plot.py cpu_usage_b.csvを実行してグラフ化する
import argparse
import pandas as pd
import matplotlib.pyplot as plt


def plot_bars(file_name):
    df = pd.read_csv(file_name)
    sec = df['sec']
    user = df['user']
    sys = df['sys']
    idle = df['idle']

    fig, ax = plt.subplots()
    ax.bar(sec, user, label='user')
    ax.bar(sec, sys, bottom=user, label='sys')
    ax.bar(sec, idle, bottom=user+sys, label='idle')

    ax.set_xlabel('sec')
    ax.set_ylabel('Usage %')
    ax.legend()
    plt.title(file_name)

    plt.show()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('file_name', help='CSV file name')
    args = parser.parse_args()

    plot_bars(args.file_name)
  • グラフ化したら以下のようになった
    • aのcpu使用状況
    • bのcpu使用状況
  • aとbの実装ではさほどcpu使用率に差がなかった
    • フレームレートの頻度の差はcpu使用率の変化に影響がないということになる
    • 課題のキーワードである「busy wait」の観点からすると、aとbはどちらもcanvas.setOnMouseMovedでイベントを待っているでcpuの使用率の推移に違いが見当たらなかったと考えることができる
    • ちなみに、マウスの座標を取るapicanvas.setOnMouseMovedしかなかったので、これ以上の工夫はできなかった

7.2 getaddinfo

このページの問題を解く

Perl/C/Java のどれかを用いて、

getaddinfo (または、それに相当するAPI) を用いて、コンピュータが使用可能なアドレスをすべて取得するプログラムを作成せよ。

その結果を、ifconfig の出力と比較せよ。

Javaでコンピュータが使用可能なアドレスをすべて取得するプログラムを作成する

Program7_2.javaというコードを作成した。

コードはGitHubにある。

import java.util.Enumeration;
import java.net.NetworkInterface;
import java.net.InetAddress;

public class Program7_2{
    public static void main(String[] args) {
        try {
            Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface intf = (NetworkInterface)interfaces.nextElement();
                System.out.println("The name of this network interface is " + intf.getName() + ".");
                System.out.println("  All the IP addresses of this network interface are: ");
                Enumeration addresses = intf.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress address = (InetAddress)addresses.nextElement();
                    System.out.println("    " + address.getHostAddress());
                }
            }
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

JDK 1.4からネットワークインターフェースにアクセスするAPIが追加された。

ネットワークアドレスを表すJavaのクラスは以下の通りである

クラス名 説明
InetAdress IPアドレスまたはそれを「解決」したホストネームを表す
・リモートアドレスを指定するために使う
InetSocketAddress extends SocketAddress ソケットのアドレス、{IPアドレス, ポート}または{ホストネーム, ポート}というペアを表す
NetworkInterface ・ローカルのネットワークインターフェースを表現する
・インターフェースの名前とインターフェースに結びついているIPアドレスのリストで表現される
マルチキャストにおいて、ローカルキャストを識別するために使う

getNetworkInterfacesメソッドは、そのホスト上の複数のNetworkInterfaceを収めたEnumerationを返す。

ifconfigの出力と比較する

$ ifconfigを実行したのちinactiveなアドレスを取り省いた結果が以下の通りである。

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    inet 127.0.0.1 netmask 0xff000000 
    inet6 ::1 prefixlen 128 
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
XHC0: flags=0<> mtu 0
XHC1: flags=0<> mtu 0
XHC20: flags=0<> mtu 0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=400<CHANNEL_IO>
    ether dc:a9:04:88:a5:cb 
    inet6 fe80::1889:4afc:1e8b:355e%en0 prefixlen 64 secured scopeid 0x8 
    inet 192.168.43.25 netmask 0xffffff00 broadcast 192.168.43.255
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active
en1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=460<TSO4,TSO6,CHANNEL_IO>
    ether 82:d0:e2:40:3c:01 
    media: autoselect <full-duplex>
    status: inactive
en3: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=460<TSO4,TSO6,CHANNEL_IO>
    ether 82:d0:e2:40:3c:05 
    media: autoselect <full-duplex>
    status: inactive
en2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=460<TSO4,TSO6,CHANNEL_IO>
    ether 82:d0:e2:40:3c:00 
    media: autoselect <full-duplex>
    status: inactive
en4: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    options=460<TSO4,TSO6,CHANNEL_IO>
    ether 82:d0:e2:40:3c:04 
    media: autoselect <full-duplex>
    status: inactive
bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=63<RXCSUM,TXCSUM,TSO4,TSO6>
    ether 82:d0:e2:40:3c:01 
    Configuration:
        id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
        maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
        root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
        ipfilter disabled flags 0x0
    member: en1 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 9 priority 0 path cost 0
    member: en2 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 11 priority 0 path cost 0
    member: en3 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 10 priority 0 path cost 0
    member: en4 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 12 priority 0 path cost 0
    nd6 options=201<PERFORMNUD,DAD>
    media: <unknown type>
    status: inactive
p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
    options=400<CHANNEL_IO>
    ether 0e:a9:04:88:a5:cb 
    media: autoselect
    status: inactive
awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484
    options=400<CHANNEL_IO>
    ether 72:4a:a7:9a:13:14 
    inet6 fe80::704a:a7ff:fe9a:1314%awdl0 prefixlen 64 scopeid 0x10 
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active
llw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=400<CHANNEL_IO>
    ether 72:4a:a7:9a:13:14 
    inet6 fe80::704a:a7ff:fe9a:1314%llw0 prefixlen 64 scopeid 0x11 
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active
utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380
    inet6 fe80::c6e:6de5:847c:da6%utun0 prefixlen 64 scopeid 0x12 
    nd6 options=201<PERFORMNUD,DAD>
utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000
    inet6 fe80::5eb0:a477:8359:1b3e%utun1 prefixlen 64 scopeid 0x13 
    nd6 options=201<PERFORMNUD,DAD>
utun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1000
    inet6 fe80::ce81:b1c:bd2c:69e%utun2 prefixlen 64 scopeid 0x14 
    nd6 options=201<PERFORMNUD,DAD>
en6: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=6467<RXCSUM,TXCSUM,VLAN_MTU,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
    ether 04:ab:18:3d:b9:47 
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (none)
    status: inactive
en5: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether ac:de:48:00:11:22 
    inet6 fe80::aede:48ff:fe00:1122%en5 prefixlen 64 scopeid 0x7 
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

Program7_2.javaを実行した結果は以下の通りである。

The name of this network interface is en5.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:aede:48ff:fe00:1122%en5
The name of this network interface is utun2.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:ce81:b1c:bd2c:69e%utun2
The name of this network interface is utun1.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:5eb0:a477:8359:1b3e%utun1
The name of this network interface is utun0.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:c6e:6de5:847c:da6%utun0
The name of this network interface is llw0.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:704a:a7ff:fe9a:1314%llw0
The name of this network interface is awdl0.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:704a:a7ff:fe9a:1314%awdl0
The name of this network interface is en0.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:1889:4afc:1e8b:355e%en0
    192.168.43.25
The name of this network interface is lo0.
  All the IP addresses of this network interface are: 
    fe80:0:0:0:0:0:0:1%lo0
    0:0:0:0:0:0:0:1%lo0
    127.0.0.1

2つの出力の違い

Javaのプログラムが出力が示したアドレスでは表示するネットワーク・インターフェースは、activeなものだけを限定して表示しているのに対し、ifconfigコマンドはinactiveなものも表示している


7.3 bloadcast と multicastの問題

このページの問題を解く

Perl/C/Java のどれかを用いて、

マルチキャストとブロードキャストを行うプログラムを作成せよ

送信可能な最大のパケットの大きさはいくつか。無線LANと有線LAN、マルチキャストとブロードキャストのそれぞれについて調べよ。

マルチキャストアドレス・ブロードキャストアドレスとは

マルチキャストアドレスは複数のアドレスが参加しているグループで、そのグループに個々のアドレスが参加(join)したり去ったり(leave)できるので「動的グループ」として扱われる。一定のルーティングポリシーに従って、通信をグループの全メンバが受信する。

ブロードキャストアドレスは、1つのネットワークやサブネットワークのすべてのIPアドレスを集めたグループで、メンバが一定なので「静的グループ」として扱われる。通信はルーティングポリシーに従って、一定のアドレス範囲の全メンバへ送られる。

アドレスタイプの概要を以下の表[1]にまとめる

タイプ IPv4 IPv6 TCP UDP 送信先 受信者
マルチキャスト オプション Yes No Yes 集合 集合の全員
ブロードキャスト Yes No No Yes 全員 全員

UDPプロトコルとは

ネットワークの通信で利用するOSI参照モデルのレイヤ4に該当するトランスポート層プロトコルの1つ

通信相手の存在を確認しないコネクションレス型である

Javaマルチキャストを行うプログラムを作成する

マルチキャストの送信をするプログラム

こちらが今回作成したコード

import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.MulticastSocket;

public class Source {
    public static void main(String[] args) {
        try {
            int port = 8888;
            String dataToSend = "Hello, World!";
            byte[] data = dataToSend.getBytes("UTF-8");
            InetAddress address = InetAddress.getByName("239.1.14.126");
            DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
            MulticastSocket socket = new MulticastSocket();
            socket.send(packet);
            socket.close();
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

マルチキャストの受信をするプログラム

こちらが今回作成したコード

マルチキャストを受信するには、MulticastSocketオブジェクトが必要になり、次の3つの操作が必要になる

  • MulticastSocketを初期化する
  • マルチキャストグループにjoinする
  • データグラムを受信する

このコードはこれら3つの操作をしてマルチキャストを受信している

import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.DatagramPacket;
import java.util.Arrays;

public class Receiver {
    public static void main(String[] args) {
        try {
            int port = 8888;
            MulticastSocket socket = new MulticastSocket(port);
            InetAddress multicastAddress = InetAddress.getByName("239.1.14.126");
            socket.joinGroup(multicastAddress);
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            socket.receive(packet);
            System.out.println(new String(Arrays.copyOf(packet.getData(), packet.getLength()), "UTF-8"));
            socket.close();
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

実行結果

$ java Receiver.javaを実行する $ java Source.javaを別のターミナルで実行する

出力結果は以下の通りになった

Hello, World!

Javaでブロードキャストを行うプログラムを作成する

ブロードキャストの送信をするプログラム

こちらが今回作成したコード

import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class Source2 {
    public static void main(String[] args) {
        try {
            int port = 8888;
            String dataToSend = "Hello, World!";
            byte[] data = dataToSend.getBytes("UTF-8");
            InetAddress address = InetAddress.getByName("255.255.255.255");
            DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
            DatagramSocket socket = new DatagramSocket();
            socket.send(packet);
            socket.close();
    } catch (Exception e) {
            System.err.println(e);
    }
    }
}

ブロードキャストの受信をするプログラム

こちらが今回作成したコード

import java.net.InetAddress;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.util.Arrays;

public class Receiver2 {
    public static void main(String[] args) {
        try {
            int port = 8888;
            DatagramSocket socket = new DatagramSocket(port);
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            socket.receive(packet);
            System.out.println(new String(Arrays.copyOf(packet.getData(), packet.getLength()), "UTF-8"));
            socket.close();
    } catch (Exception e) {
            System.err.println(e);
    }
    }
}

実行結果

$ java Receiver2.javaを実行する $ java Source2.javaを別のターミナルで実行する

出力結果は以下の通りになった

Hello, World!

送信可能な最大のパケットの大きさを調査する

UDPヘッダ + データ」の通信単位を「UDPデータグラム」と呼び、これは一般的にUDPパケットとも呼ばれる。

IPv4プロトコルでは、65507バイトまでで、実際に8KBに制限している実装が多い。一方、IPv6では2の31乗-1バイトのUDPデータグラムが可能である。[2] しかし、それは65575オクテットより大きいリンクに接続できるIPv6ノードにとってのみに有効であり、通信に参加する全員がそのような大きなリンクに接続しているホストではないと意味がない。

UDPの制約として、データグラムが小分けにされたときの残りの断片を再送信できない。よってIPv4UDPではこの問題を防ぐためにデータグラムのサイズを512バイトに制限することもよくある。512バイトよりも、大きなサイズの候補を探す場合、イーサネットの公称MTUである1500バイトからIPとUDPのヘッダを大きさである1472バイトが有効であると考えられる。

参考文献

[1] Unix Network Programming, 3rd edition, Vol.1, https://mathcs.clarku.edu/~jbreecher/cs280/UNIX%20Network%20Programming(Volume1,3rd).pdf

[2] RFC 2675, https://datatracker.ietf.org/doc/html/rfc2675


7.4 サーバとクライアント

このページの問題を解く


7.5 様々なAPI

このページの問題を解く


7.6 様々なAPI

このページの問題を解く