はじめに
図書館で借りた、「c言語によるスーパーlinuxプログラミング」という本の返却期限が近づいているのでさっさとまとめておきたいと思う
1章:c言語の位置づけ
cを勉強することでプログラムの動きをマシンレベルでイメージすることができるからいいよねと。ただし、記述は非常に具体的になる。それでは面倒なことが多いと。そういう理由から「高級言語」が開発されたと。スクリプト言語ともいう。どの言語を使うかは何を開発したいか?に依存するわけですね。システムだったらc/c++,webだったら、jsとかね。適材適所が大事ってこと。
strace python
だけでもシステムコールがたくさん呼び出されることからも、pythonがインタープリタ言語であることがわかる。
この辺の続きを詳しく知りたければ、「linuxの仕組み」という本を読むことをお勧めします。
2章:ubuntuの導入
ubuntuはまだ一般的になっていません。と。いえ、もう一般的になっています。既知の事実がつらつらと書かれているだけですね。
3章:大規模プログラミングとライブラリの利用
複数の開発者による並行開発を実現するには、アプリケーションの分割は必須の技術。ライブラリの利用は分割プログラミングのゴール。
改めてc/c++プログラムのビルド工程
- プリプロセッサによるファイルのインクルードやマクロの展開(プログラム中で#でかかれている所)
- アセンブラへの展開とアセンブル作業によるオブジェクトコードへの変換
- オブジェクトファイルの結合やライブラリとのリンクを実施
で、コンパイルってのは「実行ファイルを作る」って意味で使われるけど、どっちかっていうと、「オブジェクトファイルを作る」って意味だな。で、ビルドが「実行ファイルを作る」だな。うん、 だから、「分割コンパイル」ってのは、どっちかというと、分割ビルド ≒ 分割したファイルをコンパイルしてリンクするってかんじ。
ライブラリの種類についてもここで説明されているので、チョットやります。
ライブラリの種類とリンク
ビルド ≒ コードをコンパイルしてオブジェクトファイルの生成 + ライブラリのリンク なわけだけれど、ライブラリにはスタティックリンクとシェアードリンクの2種類があると。で、共有ライブラリのリンクはさらに、
実行形式のメモリへのロード時にリンクされる、動的リンクライブラリ
必要に応じてメモリへロード、リンクされる、動的ローディング の二つがある。
ldd ./binary
で調べられるんですね。便利ですね。 んで、ライブラリをリンクする時ってのは、ライブラリが提供する関数を使いたいときだと思うんですけど、この、関数の仕様(インターフェース)がどうなっているか?を知る方法が、ヘッダーファイルなんですよねー。そうなんですよ、まさに。 だからヘッダーファイルには定義はかかないんですよね。引数と返り値だけなんです。
自分で共有ライブラリを作る方法
- まずは、mainがない、関数だけの.cppと.hppを作る。
- -fPICオプションをつけてコンパイルする。この時、.oファイルができる。 e.g., mylib.o
- g++ -shared -o libmylib.so mylib.o でshared-libraryができる。
- ヘッダーファイルと作ったshared-libraryを/libにおいて完了。あとは、コンパイル時に-lmylibってつけることを忘れずに。ライブラリのリンクでよく忘れるやつね。大事だから。
g++ -o myapp myapp.cpp -L/path/to/library -lmylib
でコンパイルできるよーって話ね。あと大事なの。
-L (Library Search Path):
The -L option followed by a directory path tells the linker where to look for libraries when it's resolving symbols during the linking phase. It allows you to specify directories where libraries are located, making it easier to link against shared or static libraries that are not in the default library search paths.
-I (Header File Search Path):
The -I option followed by a directory path tells the compiler where to look for header files during the preprocessing phase. It allows you to specify directories where header files are located, making it easier to include headers from non-standard locations.
ヘッダーファイルのインクルードは、同じディレクトリ上であれば"" 共有ライブラリのインクルードであれば、<>でインクルードするんだよね。OK?
デフォルトでのヘッダーファイルとライブラリーのインクルードバス
In Linux, the default search paths for libraries and header files are defined by the system's compiler and linker settings. These paths can vary depending on the distribution and configuration of your Linux system. However, there are common default locations:
Default Library Search Paths:
/lib: This directory contains essential system libraries needed for the basic functioning of the operating system. It typically contains shared libraries for the system's C runtime and other low-level libraries.
/usr/lib: This directory is the primary location for system-wide libraries. It contains many commonly used shared libraries required by various applications.
/usr/local/lib: This directory is used for locally installed libraries that are not part of the system's standard libraries. It's commonly used for libraries installed by the system administrator or third-party software.
/lib64 and /usr/lib64 (on 64-bit systems): These directories are counterparts to their 32-bit equivalents and store 64-bit libraries.
Default Header File Search Paths:
/usr/include: This directory contains system-wide header files that are needed for compiling programs. It includes headers for standard C libraries and system-specific libraries.
/usr/local/include: Similar to /usr/include, this directory is used for locally installed header files that are not part of the system's standard headers.
Directories specified by the compiler: The compiler (e.g., g++ for C++) may include additional default header file search paths based on its configuration and the version of the compiler. You can see the list of these paths by running the g++ -v -E -x c++ - command.
大事なのは、ヘッダーとライブラリーはセットだってこと。なにかヘッダーをインクルードしたら、その時は、ライブラリーをリンクするとうまくいかないよって話。c/c++触らないから、すぐに忘れてしまうんですよね、その大事な点をね。まあ仕方がない。
4章:プログラムの移植性
プログラムの移植性とは、システムの環境が変わってもソフトウェアをビルド出来、適切な動作を保証できること、そのための配慮のこと。ポータビリティともいわれる。 ポータビリティのことを考えるとき、以下の点に気を配る必要がある。
- osによる違い
- 環境設定による違い
以上の問題をソースコードレベルに落とし込むと次の3点に集約できる。
- cpuアーキテクチャの違い
- 各種ライブラリの活用状況。そもそもそのライブラリがosで提供されているのか?等
- cコンパイラ特有の機能
以上の問題の解決にマクロがりよされるわけですね。 たとえば
#if defined(WIN32)
#elif defined(linux)
とかで、osによる違いを考慮したプログラムをかくことができるわけですねー。うん。
ここでautotoolsetっていうmakeの上位互換的なビルドツールが説明されているけど、割愛します。 で、cmakeもここで説明されていますね。いいですね。
5章:ライブラリの特徴と活用時の注意点
静的リンクライブラリは
/usr/lib
や
/usr/local/li
に格納されていて、.aという拡張子を持っていると。
gccではデフォルトで動的リンクが適応されると。ほう、これは面白い。つまり、実行時になって初めてリンクされるんだね。静的リンクライブラリなのにね。 たとえば、この前解いた、atcoderの問題、
gcc d.cpp
でビルドすると、 27432 byte
-staticを付けると、ビルド時に実行ファイルにライブラリを組み込むんですね。
gcc d.cpp -static
でビルドすると 1774364 byte と跳ね上がっているね。 面白い。64倍にも跳ね上がってしまった。
しかし、多くのプログラムが使うような静的リンクライブラリってのは結局メモリ上に何個も同じようなbinaryが配置されてメモリスペースがもったいないっていうんで、shareライブラリが発明されたわけですね。 これは、さっきも書いたけど、メモリ上に該当するライブラリを一個置いておいて、これを使いたいプログラムは全部同じ個所を参照するって話だな。shared objectの頭文字をとって.soという拡張子をもらうわけだ。 なるほど。デフォルトでは、soもaも実行時にリンクされるライブラリである。で、動的、静的の違いは、メモリ上でのライブラリの扱われ方、って話。
うん。 ビルド時には、 -llibraryでリンクするわけですね。 これを忘れると、リンカーから怒られたりするわけですね。 ちなみに、ldconfigは共有ライブラリの置き場所を認識させるに使うコマンドです。
6章:オプション解析
ここから第二部で、実際に様々なライブラリを使ってみましょう!って話になってくるわけですね。 オプション解析、という話ですが、割愛。
7章:データの取り扱い
この章では、Glibで提供されているキューとスタックを使って簡単なプログラムを書いているわけですね。 cでもキューやスタックを提供しているライブラリがあるんですね。まずはそこが驚き。標準ライブラリではないので、自動でリンクはされないんですね。
だから、ヘッダーのインクルードパスとライブラリの名前-lをコンパイラに渡す必要があるってことですね。
8章:cプログラムからデータベースを利用する
はい、c++からmysqlを利用したことがあるのでわかります。 リンクしないといけなんですよね。-lmysqlclientてきなのでね。 ここではsqliteの利用方法がのっています。 ほう、sqliteではサーバ・クライアント方式によるアクセスではないと。なんとプログラムから直接使うみたいですね。なるほど。これはなかなか面白い。 ただ、直接利用するってのがどういうことなのか、実はピンと来ていないんすよね。データベースの実体はファイルってのは知っているんだけど、SQliteを使うプロセスが二次ファイルへの書き出しと読み出しも請け負うってこと?マルチスレッド、もしくは非同期で実行されないとかなりオーバヘッドがもったいない気がするが。まあそういう実装が内部でされているのだろうね。チョットソースコードを見てみましょうか?はい、普通にpthreadが使われています。
あとは、インターフェースの仕様を公式で確認して下さい。 sqliteを使って何かしたいね。うん、今度作るアプリはmysqlじゃなくて、sqliteを使ってやってみよう。
第9章:ネットワーク
telnetコマンドが対話的実行を可能にするやつ。まあ、正直需要はないよね。shellに入れないし。 それよりも今回勉強になったのはtcpudumpかな。 tcpdumpとは、linuxでよく利用されるパケットキャプチャツール。 詳しい使い方は
ここを見るのがいいと思う。 使用例は、次の通り。
# 192.168.0.100 から送信されるパケットをキャプチャする
# tcpdump src host 192.168.0.100
# 192.168.0.100 が送受信する 8080番ポートのパケットをキャプチャする
# tcpdump host 192.168.0.100 and port 8080
# 16進ダンプ付きでキャプチャする
# tcpdump -X host 192.168.0.100
例えば、つぎのtcpdumpで得られるログはこんな感じです。このコマンドではzetaのeth1のnicで172.20.2.3(slave1)から送信されるパケットを捕捉します。
sudo tcpdump src host 172.20.2.3 -i eth1
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:53:03.513417 IP slave1.865 > zeta.nfs: Flags [.], ack 2592335955, win 501, options [nop,nop,TS val 2174564426 ecr 73958715], length 0
10:53:08.633456 ARP, Request who-has zeta tell slave1, length 46
10:53:08.697794 ARP, Reply slave1 is-at b8:27:eb:49:c6:84 (oui Unknown), length 46
10:53:34.233399 IP slave1.865 > zeta.nfs: Flags [.], ack 1, win 501, options [nop,nop,TS val 2174595146 ecr 73989434], length 0
10:53:34.233745 IP slave1.865 > zeta.nfs: Flags [P.], seq 1:121, ack 1, win 501, options [nop,nop,TS val 2174595146 ecr 73989434], length 120: NFS request xid 1316864979 116 getattr fh 0,2/53
10:53:34.234790 IP slave1.865 > zeta.nfs: Flags [.], ack 85, win 501, options [nop,nop,TS val 2174595147 ecr 74020154], length 0
10:53:34.965040 IP slave1.6818 > zeta.35756: Flags [S.], seq 3725686467, ack 62209134, win 65160, options [mss 1460,sackOK,TS val 2174595877 ecr 74020884,nop,wscale 7], length 0
10:53:34.967140 IP slave1.6818 > zeta.35756: Flags [.], ack 5, win 510, options [nop,nop,TS val 2174595879 ecr 74020887], length 0
10:53:34.967986 IP slave1.6818 > zeta.35756: Flags [.], ack 175, win 509, options [nop,nop,TS val 2174595880 ecr 74020888], length 010:53:34.970994 IP slave1.6818 > zeta.35756: Flags [P.], seq 1:5,
ack 175, win 509, options [nop,nop,TS val 2174595883 ecr 74020888], length 4
10:53:34.971455 IP slave1.6818 > zeta.35756: Flags [FP.], seq 5:187, ack 175, win 509, options [nop,nop,TS val 2174595883 ecr 74020888], length 182
10:53:34.973960 IP slave1.6818 > zeta.35756: Flags [.], ack 176, win 509, options [nop,nop,TS val 2174595885 ecr 74020893], length 0
10:53:40.185783 ARP, Reply slave1 is-at b8:27:eb:49:c6:84 (oui Unknown), length 46
10:54:04.953406 IP slave1.865 > zeta.nfs: Flags [.], ack 85, win 501, options [nop,nop,TS val 2174625866 ecr 74020154], length 0
10:54:09.894678 IP slave1.49796 > x.ns.gin.ntt.net.ntp: NTPv4, Client, length 48
10:54:10.073399 ARP, Request who-has zeta tell slave1, length 46
10:54:35.673583 IP slave1.865 > zeta.nfs: Flags [.], ack 85, win 501, options [nop,nop,TS val 2174656586 ecr 74050874], length 0
10:54:35.673926 IP slave1.865 > zeta.nfs: Flags [P.], seq 121:241, ack 85, win 501, options [nop,nop,TS val 2174656586 ecr 74050874], length 120: NFS request xid 1333642195 116 getattr fh 0,2/53
10:54:35.674946 IP slave1.865 > zeta.nfs: Flags [.], ack 169, win
501, options [nop,nop,TS val 2174656587 ecr 74081595], length 0
10:54:40.793634 ARP, Request who-has zeta tell slave1, length 46
10:54:40.857756 ARP, Reply slave1 is-at b8:27:eb:49:c6:84 (oui Unknown), length 46
10:55:06.393811 IP slave1.865 > zeta.nfs: Flags [.], ack 169, win
501, options [nop,nop,TS val 2174687306 ecr 74081595], length 0
これで得られるログの見方を簡単に説明しましょうかね? まずは、時間。milisecond単位までわかるのはすごいよね? 次にsrcのipとポート番号。そして、dstのipとポート番号。 その後、Flagsっていうのが来て、seq、ack、win,options,lengthが続くわけだけど、これの見方はわからないね。 以下、知恵袋の回答
とりあえず、主だったものだけ書いておきますね。
手順(仕組み)
1.通信を始めるときは、[SYN]を使ってお互いに知らせあう。
2.パケットを受け取るたびに、[ACK]で受取を知らせる。
3.通信を終了するときは、[FIN]を使ってお互いに知らせあう。
4.送り手は、データに番号を振り、送信しているデータの先頭番号(Seq)とデータの個数(Len)を相手に伝える。
5.受け手は、次に受信したいデータの番号(Ack)を相手に伝える。
最初の SYN 以外は全て ACKビットが立っています。
シーケンス番号はユーザのデータだけでなく、仮想的に、SYNとFINのフラグにも割り当てられています。
[SYN](開始):同期、伝送制御信号
[ACK](O.K.):確認、データが正常に到達したことを知らせる
[FIN](終了):終了
Seq = 1バイト送信するごとに1づつ進めていく番号
Ack = 1バイト受信するごとに1づつ進めていく番号
Win = 受け取ることができる情報量
Len = データの情報量
MSS = 1度に送信できる情報量
ただ見てわかる通り、パケットの中身までは見られていないんですよね。(いや、「-X」オプションを指定するとパケットの中身も確認できる。ただしこの場合パケットの内容が16進数で表示されるだけで、人間が解読できるものではないよね、というお話ですわ。)srcとdstしかわからないんですね。あと、通信のメタ情報。 slave.sshとかね。じゃあパケットの中身を見たい場合はどうするかって話だよね? そうです、wiresharkをインストールインストールしましょう。
sudo apt install wireshark
残念、wiresharkはcli環境では使えません。で、コマンドラインベースでパケットキャプチャできるのがtsharkだって。
sudo apt install tshark
tsharkの使い方(オプション)
- -D : キャプチャ対象として指定できるネットワークインターフェース一覧を表示して終了。
1. eth0
2. eth1
3. any
4. lo (Loopback)
5. wlan0
6. bluetooth0
7. bluetooth-monitor
8. nflog
9. nfqueue
10. dbus-system
11. dbus-session
12. ciscodump (Cisco remote capture)13. dpauxmon (DisplayPort AUX channel monitor capture)
14. randpkt (Random packet generator)
15. sdjournal (systemd Journal Export)
16. sshdump (SSH remote capture)
17. udpdump (UDP Listener remote capture)
- -V : キャプチャしたパケットの詳細を標準出力に表示する
まあ、この辺極めたくなったらこれを見てください。
10章:科学技術計算
現在ではコンピュータは高度情報化社会の基礎を支える情報処理機械としての側面が強調されているが、本来は、シミュレーションのために発展を遂げてきたものであると。その通り!まさにHPCですよ。私の専門ですね!!って言えるほどHPCに強くないのが残念なところなんですよね…うん、インターネットは道具だからさ、インターネットに強いだけじゃあだめなのよね、ってマジで思いますよ。 最初に コンピュータの歴史を見ていこうと。 その後、実際にライブラリを見ていきましょうと。 BLASやLAPACKといったライブラリは昔から存在する枯れた技術のひとつです!と。いろんなところでつかわれています、だって。
科学技術計算の歴史と応用
1946年にENIACがペンシルベニア大学で開発された。これが世界で一番最初のコンピュータらしい。まあ、ご存じのとおり、弾道計算を目的として作られたコンピュータなわけですね。最初は軍事目的だったんだね。 んで、有限要素法が出てくるわけですね。ボーイングの技術者が考え出した手法なんだけどね、どうやらこれ考えた人は天才らしくて、我々凡人には有限要素法が何なのか、真に理解するにはかなりの時間を要すようですが。まあとにかく、有限要素法っていうのも膨大な計算量を要するわけですわ。 他には多体問題にも使われるわけですね。惑星シミュレーションで、2体問題であれば解析的に解くことが可能なんですね。しかし、多体になると、基本的にはシミュレーションじゃないと解けなくなります。
科学技術計算の集大成ともいえるライブラリがGSLですね。まあ、大学で習った数学でやれることはほとんど全部できると思います。時間があったら触ってみる程度でいいと思いますよ。
11章:画像データ処理
うーん、飛ばし!!
12章:GUIプログラミング
はっきり言って、cでGUIプログラミングをすることはないと思うんだよね。バックエンドにcを使う時でも、フロントエンドにはやっぱりモダンなjavascriptとかを使うことが多くなると思う。ということでとばします。 一点だけメモっておくと、GTK+っていうライブラリを使ってcからGUIプログラミングができるみたいです。
13章:コンピュータグラフィックス
openGLを使って遊びましょう!という章。これに関しては学部2年の時にカーナビでopenGL使ったし飛ばしていいかなって感じ。まあ軽く読み流す程度で。 コンピュータによる2次元グラフィックスにはベクターグラフィックスとラスターグラフィックスの2種類がある。ベクターグラフィックスとは、画像を構成する画像データを点や線の座標情報として内部で保持し、画面への描画を最後に行う方式。ラスターグラフィックスは画像データを画素データの集積として格納する。11章で開設した画像データ処理は据えてラスターグラフィックスのデータ。 前者の方が処理が複雑になる。 ベクターグラフィックスライブラリとしてはcairo ってのがよく使われるらしいです。 3次元ではopenGLだね。
olcPixelGameEngine を激おししたい。まあこれを使っていれば問題なさすぎると思う。
14章:デバイスのアクセス
デバイスにはめっちゃアクセスしたくなるんですよね。 というのもね、ラズパイを搭載した気球を宇宙まで飛ばして、ネット配信をするときにどうにかして地上と無線通信をしないといけないんですよね。しかもtpcプロトコルに乗っ取った無線通信をね。しかしね、これがなかなか難しいと思っていて。自前の通信デバイスを作るしかないのではないかと今考えているのですが。 いや、長距離無線LAN(FWA)というのがあるらしい。
FWA なかなか面白い。 FWA : fixed wireless access これ、自分で作れたら最高なんやけどね。そんな簡単にはいかないかね。購入したほうが早いか。 USBポートに接続するタイプの超長距離無線LANを作れたらいいな。 でもこれって、ただ単純に電波をamplifyすればいいってわけではないのか?トランジスタを使って。そんな簡単な話ではないのか。
libimobiledeviceiphoneのストレージにアクセスするためのライブラリ。cで書かれていますね。これを使ったら、例えば水没してしまったiphoneからデータを復元させるとかできるのかね?まあ、記憶装置が死んでいなかったらできるのか?いや、iphoneの電源が入っている必要があるなおそらく。まあ、画面が割れて映らなくなった程度なら簡単にデータを移せるかも。
15章:エンコードとデコード
まあ飛ばし。と思ったけど、parserの話が意外と役に立ちそうだからチョット待ってくれ。 dotconf.htっていうライブラリが設定ファイルをいい感じに読み込んでくれるものになっている。
これ あまり有名なものではなさそうですが…
16章:テスト手法
cのプログラミングで役に立つテスト技法として、cプログラム向けのユニとテストを簡単に実施するライブラリや、cのプログラムでありがちなメモリ関連のバグを発見するためのテストツール、テスト用ライブラリの紹介。 まず、プログラムのテストで最も基礎的なテスト方法として、ユニットテストっていうのがある。今後は俺もユニットを意識して開発したいなと思っているところですが、、、 で、cのプログラミングにおけるユニットテストを支援するつーるとして、cunitが用意されているみたいです。
cunit c++むけのCppUnitってのもあります。 CppUnit ですね。
って流れだね。まあ、意識はしなくとも今までもこの流れでやっていたよね。うん。普通に。
17章:凸包問題のアプリを作るってはなし。
c++向けに内容を書き換えてやってみるのは面白そうだね。ってことで後ほどやります。