はじめに
OpenAIによって開発されたChatGPTが話題になっています。特に、2023年3月14日に公開された最新モデルであるGPT-4は、これまで私達がAIに持っていた認識を根底から覆したのではないでしょうか?
私は、「普通に人間とやりとりしているのと変わらんやん…」と率直に驚きました。
エンジニアの業務の中でも、自動コーディング支援、技術的課題の壁打ち相手、ドキュメント自動生成、学習支援等さまざまな用途に活用する可能性を皆さん検証している段階です。私もその波に乗っている真っ最中です。
そこで今回は、ソースコードリーディングをChatGPTをメンターにしたら爆速にできるのではないか?という仮説を検証してみました。
そしてどうせやるなら、ソースコードリーディングのハードルの高さナンバーワン(個人調べ)の「Linuxカーネル」を題材にしてみました。
なお、使用したモデルはGPT-4です。
結論
結論だけ知りたい方向けに、最初にまとめてしまいます。
コードリーディングに二ヶ月かかっていたボリュームが、ChatGPTを活用する事により一日で完了するほど爆速で実施する事ができました。
ただ、現時点では注意点もあるので以下に箇条書きしておきます。
- 最初から英語での入力を推奨します。英語で質問した場合に得られる回答は日本語より正確な確率が高く、提供される情報も豊富です。新規のチャットを開いて最初の質問から終始一貫英語で質問するのがポイントです
- 概要レベルの正確さは信じて良いです。ただ、ソースコード内の具体的な識別子や処理まで含めた正確さを求める場合は、必ず実際のソースコードを貼り付けて解説を求める必要があります。それによって回答が段違いに詳細になりますし、仮に回答が間違っていたとしても、自分で正誤判断がしやすくなります
- 説明の中でソースコードのGitのコミットIDを提示してくれる事がありますが、自分調べでは100%間違っているので信じない方が良いです。確認したい場合は自分で確認すると良いでしょう
- 2021年9月までの情報しか学習していない事を留意しておきましょう
- 今こそChatGPTで子供の頃の好奇心を取り戻しましょう👍
また、今回の例に限らずなのですが、ChatGPTの言うことを信じていいのか不安になる人もいると思います。その場合は、情報の根拠となるソースを求めると提供してくれるので、セットで確認してみると安心して活用できると思います。
ソースコードリーディング対象
Linuxカーネルは非常に複雑で巨大なソースコードを持っており、さまざまなバージョンが存在します。よって今回の検証では範囲を絞り、Linuxカーネル2.6.11のopenシステムコールの処理の流れを調べました。
検証の目的は、ChatGPTがソースコードリーディングの効率化に寄与するかどうかですが、単純な時間短縮だけではなく、正確である事も指標になってきます。このため、私がある程度理解している処理の流れを対象に選ぶのが望ましいのですが、その点においてopenシステムコールの処理の流れが適切でした。以前「Linuxのファイルディスクリプタをハックする」という記事を書いた事があるので、それを参考にしながら確認しています。
ソースコードリーディング開始
ChatGPTとともに、ソースコードリーディングを開始します。
以下、「入力」の見出しは私がChatGPTに投げた入力を意味し、「回答」の見出しはChatGPTからの回答を表すものとします。なお、所感は回答を受けての私の所感を表しています。
まずは日本語でやってみた
最初、日本語で入力した時に思うような回答が得られず失敗してしまいました。この例も最初にのせておきます。成功例だけ確認したい方は、後半の「英語でやってみた」例まで飛ばしても構いません。
これは、私がChatGPTと技術的なやりとりをする場合、基本的には英語にしようと思ったキッカケになりました。
もちろん、本質的には時間が解決する可能性のある話ではあると思います。
入力-1
Linuxカーネルのバージョン2.6.11内で、openシステムコールに関する処理はどのように実装されていますか?
回答-1
私の知識は2021年9月までのものであり、2023年3月26日時点での具体的な情報を提供することはできませんが、Linuxカーネル2.6.11は、2005年にリリースされたバージョンであることから、その時点での実装について説明できます。
openシステムコールは、ファイルを開いて操作する際に使用されます。Linuxカーネル2.6.11内のopenシステムコールに関する処理は以下のように実装されています。
- システムコール番号 (__NR_open) を通じて、ユーザーモードからカーネルモードに移行します。
- システムコールの実装は、fs/open.c にあり、sys_open 関数が定義されています。この関数は、do_sys_open 関数を呼び出します。
- do_sys_open 関数は、まず与えられたファイル名の文字列をカーネル空間にコピーし、getname_kernel 関数を使ってパス名を検証します。
- 次に、do_filp_open 関数が呼び出され、ファイルを開くための実際の処理が行われます。
- do_filp_open 関数は、nameidata 構造体を初期化し、ファイルのルックアップを行い、最終的に vfs_open 関数を呼び出して、ファイルを開きます。
- vfs_open 関数は、ファイルオペレーション構造体 (file_operations) に基づいて、具体的なファイルシステムドライバの open 関数を呼び出します。
- 成功した場合、do_sys_open 関数は、ファイルディスクリプタをユーザーモードに返します。
この概要は、Linuxカーネル2.6.11のopenシステムコールの実装に関するものです。Linuxカーネルは進化し続けており、新しいバージョンでは実装が変更される可能性があります。最新の実装を確認するには、対応するカーネルのソースコードを参照してください。
所感-1
ぱっと見正しそうな感じがして、おお〜!と思いました。しかし冷静になって眺めてみると、「Linuxカーネル2.6.11は、2005年にリリースされたバージョン」がまず本当なのか気になって調べたら・・・そこはその通りで間違いありませんでした😌
次にopenシステムコールの流れで気になったのが、do_sys_open
という関数名が出てきた事です。確かにdo_sys_open
という関数はLinuxカーネル内で定義されている関数ではあるのですが、Linuxカーネル2.6.11時点ではまだ存在していない認識だからです。
Gitのコミットログを愚直に追っていくと、do_sys_open
は2.6.14で登場していました。
やはりソースコードの現物を自分の目で確認しながら進めていく必要性を感じたので、ソースコードを入手する事にしました。
入力-2
Linuxカーネルのバージョン2.6.11はどうやって入手すれば良いですか?シェルでのコマンドも教えてください。
回答-2
Linuxカーネルのバージョン2.6.11のソースコードは、公式のLinuxカーネルアーカイブ(kernel.org)から入手できます。シェルで以下のコマンドを実行して、2.6.11のソースコードをダウンロードして展開できます。
wget https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.tar.gz
tar xvf linux-2.6.11.tar.gz
まず、wget
コマンドを使用して、指定されたURLからlinux-2.6.11.tar.gzファイルをダウンロードします。次に、tar
コマンドを使用して、ダウンロードしたアーカイブファイルを展開します。展開が完了すると、カレントディレクトリにlinux-2.6.11
という名前のディレクトリが作成され、その中にソースコードが格納されます。
なお、wget
がインストールされていない場合は、以下のコマンドでインストールできます(UbuntuやDebianベースのディストリビューションの場合)。
sudo apt-get install wget
また、wget
が利用できない環境の場合は、curl
コマンドを使用してダウンロードすることもできます。
curl -O https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.tar.gz
tar xvf linux-2.6.11.tar.gz
所感-2
Linuxカーネルのソースコードを読みたいと思っても、初心者にはソースコードがどこにあるのか調べたりの最初の一歩のハードルがそこそこ高いように思うので、そこすらChatGPTでクリアしようという試みです。
wgetでの入手方法だけでなくそのインストール方法、代替案としてcurlでの入手方法も返答してくれるホスピタリティが素晴らしいと思いました。
ただ、提示されたcurlのコマンドを実行してみるとうまく解凍できませんでした。理由はわかっているのですが、あえて知らないフリをして聞いてみます。
入力-3
curlでやるとソースコードダウンロードがされませんでした。どうやってデバッグすれば良いでしょうか?
回答-3
すみません、curlコマンドのオプションに誤りがありました。-L
オプションを追加してリダイレクトに対応するようにしてください。以下のコマンドを試してください。
curl -L -O https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.tar.gz
tar xvf linux-2.6.11.tar.gz
L
オプションは、リダイレクトがある場合、自動的にリダイレクト先のURLに追従するようにcurlに指示します。これで、正常にソースコードがダウンロードされるはずです。
ダウンロードが正常に完了しなかった場合、以下の方法でデバッグできます。
-
curl
コマンドにv
オプションを追加して、詳細な情報を表示します。curl -v -L -O https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.tar.gz
-
インターネット接続やプロキシ設定が正しいことを確認してください。問題がある場合は、接続や設定を修正し、再度試行してください。
-
サーバー側で一時的な問題が発生している可能性があります。しばらく待ってから再度試行するか、別のミラーサイトを探してダウンロードしてみてください。
所感-3
デバッグの方法だけ聞こうと思ったのですが、リダイレクト追従オプションをつければ問題なくダウンロードしたファイルを解凍できる事まで察してくれました。これには驚きました。
入力-4
あなたが以前説明してくれた「システムコール番号 (__NR_open) を通じて、ユーザーモードからカーネルモードに移行します。」に関して、この処理に関連しているファイルを教えてください。
回答-4
Linuxカーネル2.6.11でシステムコール番号(__NR_open
)を通じてユーザーモードからカーネルモードへの移行が行われる処理は、主にアーキテクチャ依存のコードで実装されています。具体的なファイルは、使用しているプロセッサーアーキテクチャによって異なります。
x86アーキテクチャの場合、次のファイルが関連しています。
-
arch/x86/kernel/entry.S
- アセンブリ言語で書かれた、システムコールのエントリポイントとなるコードが含まれています。システムコール番号がeax
レジスタに格納されており、それに基づいて適切なシステムコール関数が呼び出されます。 -
include/asm-x86/unistd.h
- システムコール番号の定義が含まれています。__NR_open
はこのファイルで定義されており、具体的な番号が割り当てられています。 -
arch/x86/kernel/syscall_table.S
- システムコールテーブルが定義されています。このテーブルは、システムコール番号をインデックスとして、対応するシステムコール関数へのポインタを格納しています。__NR_open
に対応する関数(sys_open
)へのポインタも、このテーブルに格納されています。
この他にも、システムコールの仕組みに関連するファイルがいくつか存在しますが、__NR_open
によるユーザーモードからカーネルモードへの移行に関連する主要なファイルは、上記の3つです。
他のアーキテクチャに関しても、同様のファイル構成がありますが、ファイル名やディレクトリ構造が異なる場合があります。
所感-4
確かにLinuxカーネルはCPUアーキテクチャごとに処理がわかれているので、とりあえずx86アーキテクチャの例に絞って説明してくれるのは親切だなと思いました。
入力-5
ありがとうございます。以降の説明は、x86アーキテクチャ前提にしていただいて構いません。
回答-5
承知しました。以降の説明ではx86アーキテクチャを前提にして回答いたします。x86アーキテクチャに関連するLinuxカーネルやその他の技術的な質問があれば、お気軽にお聞きください。喜んでお手伝いします!
所感-5
人間とやりとりしている時と同様に、自然と感謝の言葉が出てきました。
ここで、改めて最初の回答1の結果を確認すると、arch/x86/kernel/entry.S
というファイルが存在しない事に気づきました。
入力-6
arch/x86/kernel/entry.Sというパスにファイルがないのですが、間違いでしょうか?似たようなディレクトリとしてarch/x86_64というディレクトリはありますが。
回答-6
申し訳ありません。私の説明が不正確でした。実際には、Linuxカーネル2.6.11では、32ビットx86アーキテクチャのコードはarch/i386
ディレクトリにあり、64ビットx86アーキテクチャのコードはarch/x86_64
ディレクトリにあります。
32ビットx86アーキテクチャの場合、以下のファイルがシステムコールに関連しています。
-
arch/i386/kernel/entry.S
- システムコールのエントリポイントとなるコードが含まれています。 -
include/asm-i386/unistd.h
- システムコール番号の定義が含まれています。 -
arch/i386/kernel/syscall_table.S
- システムコールテーブルが定義されています。
64ビットx86アーキテクチャの場合、以下のファイルがシステムコールに関連しています。
-
arch/x86_64/kernel/entry.S
- システムコールのエントリポイントとなるコードが含まれています。 -
include/asm-x86_64/unistd.h
- システムコール番号の定義が含まれています。 -
arch/x86_64/kernel/sys_call_table.S
- システムコールテーブルが定義されています。
以前の説明で誤ったパスを示してしまい、申し訳ありませんでした。上記のファイルが、x86およびx86_64アーキテクチャのシステムコールに関連する正しいパスです。
所感-6
以後x86アーキテクチャで説明して下さいと指定しましたが、32ビットか64ビットかは明記してなかったので混乱させてしまったのだと思いました。よって、64ビットである事も明示します。
入力-7
承知しました。では、以降は64ビットx86アーキテクチャ前提で説明をお願いします。
回答-7
了解しました。以降の説明では、64ビットx86アーキテクチャ(x86_64)を前提にして回答いたします。x86_64アーキテクチャに関連するLinuxカーネルやその他の技術的な質問があれば、お気軽にお聞きください。喜んでお手伝いします!
入力-8
あなたが以前説明してくれた「システムコール番号 (__NR_open) を通じて、ユーザーモードからカーネルモードに移行します。」に関して、この処理に関連しているファイルを教えてください。
上記の質問を訂正をふまえて再度教えてください。
回答-8
64ビットx86アーキテクチャ(x86_64)を前提にした場合、システムコール番号(__NR_open
)を通じてユーザーモードからカーネルモードへの移行が行われる処理に関連する主要なファイルは以下のとおりです。
-
arch/x86_64/kernel/entry.S
- アセンブリ言語で書かれた、システムコールのエントリポイントとなるコードが含まれています。システムコール番号がrax
レジスタに格納されており、それに基づいて適切なシステムコール関数が呼び出されます。 -
include/asm-x86_64/unistd.h
- システムコール番号の定義が含まれています。__NR_open
はこのファイルで定義されており、具体的な番号が割り当てられています。 -
arch/x86_64/kernel/sys_call_table.S
- システムコールテーブルが定義されています。このテーブルは、システムコール番号をインデックスとして、対応するシステムコール関数へのポインタを格納しています。__NR_open
に対応する関数(sys_open
)へのポインタも、このテーブルに格納されています。
これらのファイルが、x86_64アーキテクチャにおいて__NR_open
を通じたユーザーモードからカーネルモードへの移行に関連する主要なファイルです。
所感-8
また、存在しないファイルが出てきたので指摘しようと思いました。
入力-9
arch/x86_64/kernel/sys_call_table.Sというファイルがないのですが、間違いですか?
回答-9
申し訳ありません、ファイル名に誤りがありました。正しいファイル名はarch/x86_64/kernel/syscall_64.tbl
です。
x86_64アーキテクチャにおいて、システムコールテーブルは次のファイルに定義されています:
-
arch/x86_64/kernel/syscall_64.tbl
- このファイルには、システムコール番号と対応するシステムコール関数名が定義されています。__NR_open
に対応する関数はsys_open
です。
私の説明で誤ったファイル名を示してしまい、申し訳ありませんでした。上記のファイルが、x86_64アーキテクチャのシステムコールテーブルに関連する正しいファイルです。
所感-9
arch/x86_64/kernel/syscall_64.tblも存在しなかったので、変な状態になってしまったと思いました😇
これはChatGPTの能力の問題だけではなく、私のプロンプトエンジニアリングスキルの問題もあると思います。
とはいえ日本語に限界を感じはじめたので、新規でチャットを開き、最初から英語でやり直す事にしました。
ちょっと補足しておきます。
実はこの後、いくつか修正を指摘するイタチごっこが続いてしまいChatGPTをマイクロマネジメントしているような状態になってしまいました。
日本語で正しい深堀りをしようとすると間違いが多発してしまい、いわゆる「これなら俺がやった方が早い」ような状態になってしまいがちなので、英語に切り替えたという経緯があります。
英語でやってみた
以下入力文に関しては英文を載せておきますが、ChatGPTの回答文に関しては意訳したものを載せておきます。最初の反省を活かし、最初から前提情報を与えておきます。
入力-1
I will now do a source code reading of the Linux kernel version 2.6.11.
The CPU architecture targeted in this source code reading assumes x86 64-bit architecture.
I need your assistance.
(意訳)これから、Linuxカーネルバージョン2.6.11のソースコードリーディングを行います。
今回のソースコードリーディングで対象とするCPUのアーキテクチャは、x86 64ビットアーキテクチャを前提としています。あなたの協力が必要です。
回答-1
(意訳)Linux kernel version 2.6.11 for x86 64-bit architectureのソースコード読解をお手伝いさせていただきます。Linuxカーネルは大規模で複雑なコードベースですので、どの部分に焦点を当てたいかを指定していただけると助かります。
以下は、一般的な領域から始めるためのものです:
- ブートプロセスと初期化
- プロセス管理・スケジューリング
- メモリ管理
- ファイルシステムとI/O
- ネットワーキング
- デバイスドライバとハードウェアサポート
- システムコール
どのエリアから始めたいのか、具体的な質問やテーマがあればお知らせください。
所感-1
最初にカテゴライズしてくれているのが大変ありがたいです。今回はシステムコールを対象にしているのでさっそく本題にうつっていきたいと思います。
入力-2
How is the open system call implemented?
(意訳)openシステムコールはどのように実装されていますか?
回答-2
(意訳)open
システムコールは、Linuxのファイル操作の重要な部分です。Linuxカーネルに実装され、ユーザー空間のプログラムがファイルやデバイスを開いて読み書き、またはその両方を行うことができます。オープンシステムコールの実装は、主にカーネルのVFS(仮想ファイルシステム)層にあり、ファイル関連の操作を処理し、異なるファイルシステムの抽象化を提供します。
Linuxカーネル2.6.11では、open
システムコールの実装は、ファイルfs/open.c
にあります。該当する関数はdo_sys_open()
で、sys_open()
とsys_openat()
のシステムコールによって呼ばれます。
ここでは、open
システムコールがどのように実装されるかを簡略化して説明します:
- ユーザー空間プログラムは、必要な引数(ファイルパス、フラグ、モード(新規ファイルを作成する場合))を指定して
open
システムコールを呼び出します。 - カーネルは、この呼び出しを、
AT_FDCWD
フラグ(相対パスを示す)の使用有無に応じて、sys_open()
またはsys_openat()
システムコールに翻訳します。 - 次に、
sys_open()
またはsys_openat()
関数は、与えられた引数でdo_sys_open()
を呼び出します。 -
do_sys_open()
は、有効なフラグとモードをチェックし、getname()
を呼び出してユーザー空間のメモリからファイル名を取得します。 -
do_filp_open()
関数は、ファイル名、フラグ、モードを指定して呼び出され、次にVFS関数path_openat()
を呼び出します。 -
path_openat()
は、ディレクトリエントリーキャッシュ(dentry cache)とinode cacheでファイルを検索したり、必要に応じてファイルシステムから読み込んだりする処理を行います。 - ファイルが見つかったり作成されたりした場合、そのファイルに対して新しいファイルディスクリプタが割り当てられ、要求されたアクセスモードとパーミッションでファイルがオープンされます。
- ファイルディスクリプタはユーザ空間プログラムに戻されます。
このプロセスを通じて、カーネルは特定のファイルシステム実装(ext4、XFSなど)と対話し、inodeやデータブロックの割り当てなど、ファイルシステム固有の詳細を処理します。
これは簡略化された概要であり、実際の実装には追加のチェック、最適化、エラー処理が含まれている可能性があることに留意してください。完全な理解を得るには、fs/open.c
と関連ファイルのソースコードに飛び込むのが一番です。
所感-2
openシステムコールの概要と、ファイルがfs/open.c
に存在しているところまで教えてくれました。実際にfs/open.c
に定義されているsys_open
関数を見てみると、概ねあっているのですがさきほどの説明と食い違うところがありました。よって、そこを指摘してみる事にします。
入力-3
asmlinkage long sys_open(const char __user * filename, int flags, int mode)
{
char * tmp;
int fd, error;
#if BITS_PER_LONG != 32
flags |= O_LARGEFILE;
#endif
tmp = getname(filename);
fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
fd = get_unused_fd();
if (fd >= 0) {
struct file *f = filp_open(tmp, flags, mode);
error = PTR_ERR(f);
if (IS_ERR(f))
goto out_error;
fd_install(fd, f);
}
out:
putname(tmp);
}
return fd;
out_error:
put_unused_fd(fd);
fd = error;
goto out;
}
The above is the sys_open function defined in fs/open.c.
Please correct me as it differs from your description.
(意訳)上記は、fs/open.cで定義されているsys_open関数です。
説明と異なるので訂正をお願いします。
回答-3
(意訳)前回の回答で混乱を招いたことをお詫びします。ご指摘の通り、sys_open()
関数はご指摘の方法で実装されています。前回の回答は、処理の概要を説明したものでしたが、実際のコードについては、もっと具体的に説明するべきだったようです。コードスニペットを提供していただき、ありがとうございます。以下、ご投稿いただいたsys_open()
関数のコードについて説明します:
-
flags |= O_LARGEFILE;
: この行は、システムが大きなファイルをサポートしている場合(つまり、BITS_PER_LONG != 32
の場合)、O_LARGEFILE
フラグが設定されることを保証します。このフラグは、ラージファイル(2GBを超えるファイル)をサポートしているファイルを開くべきであることを示すために使用されます。 -
tmp = getname(filename);
: この行は、getname()
関数を呼び出すことによって、ユーザー空間のメモリからファイル名を取得します。tmp
にはファイル名が含まれています。 -
fd = PTR_ERR(tmp);
: この行は、tmp
にエラーがあると仮定して、fd
をエラーコードで初期化します。 -
if (!IS_ERR(tmp)) { ... }
: このブロックは、tmp
にエラーが含まれているかどうかをチェックします。もしそうでなければ、次のステップに進みます:-
fd = get_unused_fd();
: この行は、get_unused_fd()
を呼び出して、新しいファイルディスクリプタ(fd)を割り当てます。 -
if (fd >= 0) { ... }
: このブロックは、ファイルディスクリプタの割り当てが成功したかどうかをチェックします。-
struct file *f = filp_open(tmp, flags, mode);
: この行は、filp_open()
を呼び出して、与えられたフラグとモードでファイルを開き、f
にファイルポインタを格納します。 -
error = PTR_ERR(f);
: この行は、f
にエラーがあると仮定して、error
をエラーコードで初期化します。 -
if (IS_ERR(f)) goto out_error;
: この行は、ファイルを開く際にエラーがあるかどうかをチェックする。エラーがあれば、out_error
ラベルにジャンプします。 -
fd_install(fd, f);
: エラーがなければ、fd_install()
関数を用いて、ファイルポインタf
と割り当てられたファイルディスクリプタfd
を関連づけます。
-
-
-
putname(tmp);
:putname()
を呼び出すことにより、ファイル名用に確保されたメモリを解放する。 -
return fd;
: 最後に、この関数はファイルディスクリプタfdを返します。
エラーの場合(out_error
ラベル):
-
put_unused_fd(fd);
: この行は、put_unused_fd()
を呼び出すことにより、割り当てられたファイルディスクリプタを解放します。 -
fd = error;
:fd
にエラーコードを設定します。 -
goto out;
: この行は、out
ラベルにジャンプし、ファイル名のメモリを解放してエラーコードを返します。
繰り返しになりますが、前回の回答で混乱させてしまったことをお詫びします。上記の説明は、あなたが提供したコードと一致するはずです。
所感-3
詳細さと正確さがグッと上がりました。私がこのコードの処理の流れを知っているから判断できるというのはありますが、知らない場合も一つずつ要素を同様に深ぼっていけば正しさを検証できると思います。
ここから突き進む前に、最初の概要の説明を訂正してもらおうと思います。
入力-4
Please give me a general overview of the process of the open system call you taught me before.
(意訳)以前教えていただいたオープンシステムコールの処理の概要を教えてください。
回答-4
(意訳)承知しました!ここでは、先に提供したコード・スニペットのsys_open()
関数に基づき、open
システム・コール・プロセスの概要を説明します:
- ユーザー空間のプログラムは、必要な引数(ファイルのパス、フラグ、モード(新しいファイルを作成する場合))を指定して、
open
システムコールを呼び出します。 - カーネルは、この呼び出しを
sys_open()
システムコールに変換します。 -
sys_open()
関数は、システムが大容量ファイルをサポートしているかどうかをチェックし、それに応じてO_LARGEFILE
フラグを設定します。 -
sys_open()
関数は、getname()
を用いてユーザー空間のメモリからファイル名を取得します。
5.sys_open()
関数は、get_unused_fd()
を用いて新しいファイルディスクリプタを割り当てます。 -
sys_open()
関数は、filp_open()
を呼び出して、与えられたフラグとモードでファイルをオープンし、ファイルオープンが成功したかどうかチェックします。 - ファイルオープンに成功した場合、
sys_open()
関数はfd_install()
を用いて、ファイルポインタと割り当てられたファイルディスクリプタを関連付けます。 -
sys_open()
関数は、putname()
を用いてファイル名用に確保されたメモリを解放します。 -
sys_open()
関数は、ファイルディスクリプタをユーザ空間プログラムに返します。
ファイルオープン処理中にエラーが発生した場合、sys_open()
関数はput_unused_fd()
を用いて割り当てられたファイルディスクリプタを解放し、適切なエラーコードを返します。
この概要は、提供されたsys_open()関数のコードにもっと沿ったものであるべきです。
所感-4
私が提供したコードに基づき、概要の説明が正しくアップデートされました。
入力-5
In which file is the getname function defined?
(意訳)getname関数はどのファイルで定義されていますか?
回答-5
(意訳)getname()
関数は、Linuxカーネルソースコードのfs/namei.c
ファイルで定義されています。この関数は、ユーザー空間のメモリからファイル名を取得し、そのカーネル空間のコピーを作成する役割を担っています。これは、潜在的な問題やセキュリティ脆弱性を防ぐために、カーネルコードがユーザー空間のメモリに直接アクセスしないようにする必要があるためです。
所感-5
getname関数の処理を深堀りしたいと思い、処理が書いてあるファイルの場所を質問してみました。自分で探しても良いのですがChatGPTに聞いてしまう事で時短になります。
入力-6
static inline int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
len = TASK_SIZE - (unsigned long) filename;
}
retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
if (retval < len)
return 0;
return -ENAMETOOLONG;
} else if (!retval)
retval = -ENOENT;
return retval;
}
char * getname(const char __user * filename)
{
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
tmp = __getname();
if (tmp) {
int retval = do_getname(filename, tmp);
result = tmp;
if (retval < 0) {
__putname(tmp);
result = ERR_PTR(retval);
}
}
if (unlikely(current->audit_context) && !IS_ERR(result) && result)
audit_getname(result);
return result;
}
The implementation of the getname function is above. Please explain the process flow based on this.
(意訳)getname関数の実装は上記の通りです。これを元に処理フローを説明してください。
回答-6
(意訳)ここでは、提供されたコードをもとに、getname()
関数とdo_getname()
ヘルパー関数を説明します:
getname()
関数:
-
tmp = __getname();
: この行は、__getname()
関数を呼び出し、カーネル空間にファイル名のための一時的なバッファを確保するものである。割り当てられたバッファのサイズはPATH_MAX
(4096バイト)であり、CPUごとのメモリプールから取得されます。 -
if (tmp) { ... }
: このブロックは、バッファの割り当てが成功したかどうかをチェックする。-
int retval = do_getname(filename, tmp);
: この行は、do_getname()
ヘルパー関数を呼び出して、ユーザー空間メモリから割り当てられたバッファtmpにファイル名をコピーしています。do_getname()
の戻り値は、retval
に格納されます。 -
result = tmp;
: この行は、result
を割り当てられたバッファtmp
のアドレスで初期化します。 -
if (retval < 0) { ... }
: このブロックは、do_getname()
関数がエラーを返したかどうかをチェックします。エラーがあった場合-
__putname(tmp);
: この行は、__putname()
関数を呼び出すことによって確保されたバッファを解放しています。 -
result = ERR_PTR(retval);
:result
にretval
のエラーコードを含むエラーポインタを設定します。
-
-
-
if (unlikely(current->audit_context) && !IS_ERR(result) && result)
: この行は、現在のタスクに監査コンテキストがあるかどうか、resultがエラーポインターでなく、NULLポインターでもないかをチェックします。すべての条件が満たされた場合-
audit_getname(result);
: この行は、監査用にファイル名を記録するために、audit_getname()
関数を呼び出します。
-
-
return result;
: 最後に、getname()
関数は、ファイル名のカーネル空間コピーかエラーポインタを返します。
do_getname()
ヘルパー関数:
- この関数は、現在のアドレス制限が
KERNEL_DS
であるかどうかをチェックします。そうでない場合、ファイル名のユーザ空間アドレスに基づいて、ファイル名の最大許容長(len
)を調整します。 -
retval = strncpy_from_user(page, filename, len);
: この行は、strncpy_from_user()
関数を呼び出して、ユーザー空間のメモリ(filename
)から、割り当てられたカーネル空間のバッファ(page
)に、最大長lenでファイル名をコピーします。戻り値は retval に格納されます。 - この関数は、
strncpy_from_user()
の戻り値を確認します:-
retval > 0
の場合:この関数は、ユーザー空間からretvalバイトをコピーしたことを意味します。-
retval < len
の場合:これは、コピーが成功したことを意味し、関数は0を返します。 -
retval >= len
の場合:これは、ファイル名が長すぎることを意味し、関数は-ENAMETOOLONG
を返します。
-
-
retval == 0
の場合:これは、バイトがコピーされなかった(空文字列)ことを意味するので、この関数は-ENOENTを返します。 -
retval < 0
の場合:コピー中にエラーが発生したことを意味し、この関数はエラーコードを直接返します。
-
getname()
関数は、do_getname()
関数の助けを借りて、潜在的なエラーやエッジケースを処理しながら、ユーザー空間のメモリからファイル名を取得し、カーネル空間のコピーを作成します。
所感-6
ここでの個人的コツは、getname関数だけでなくその中から呼ばれているdo_getname関数も入力として提供した事です。それによって、ChatGPTから回答されるgetname関数の処理の説明がより詳細になりました。
同様に、後続の関数の処理内容もさくさくと聞いていきます。
入力-7
int get_unused_fd(void)
{
struct files_struct * files = current->files;
int fd, error;
error = -EMFILE;
spin_lock(&files->file_lock);
repeat:
fd = find_next_zero_bit(files->open_fds->fds_bits,
files->max_fdset,
files->next_fd);
/*
* N.B. For clone tasks sharing a files structure, this test
* will limit the total number of files that can be opened.
*/
if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
goto out;
/* Do we need to expand the fd array or fd set? */
error = expand_files(files, fd);
if (error < 0)
goto out;
if (error) {
/*
* If we needed to expand the fs array we
* might have blocked - try again.
*/
error = -EMFILE;
goto repeat;
}
FD_SET(fd, files->open_fds);
FD_CLR(fd, files->close_on_exec);
files->next_fd = fd + 1;
#if 1
/* Sanity check */
if (files->fd[fd] != NULL) {
printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
files->fd[fd] = NULL;
}
#endif
error = fd;
out:
spin_unlock(&files->file_lock);
return error;
}
The implementation of the get_unused_fd function is above. Please explain the process flow based on this.
(意訳)get_unused_fd関数の実装は上記の通りです。これを元に処理フローを説明してください。
回答-7
(意訳)get_unused_fd()
関数は、現在のプロセスで利用可能な(未使用の)ファイルディスクリプタを見つける役割を担っています。提供されたコードは、この関数の実装を概説しています。以下、処理の流れを説明します:
-
struct files_struct * files = current->files;
: この行は、現在のプロセスのfiles_struct
へのポインタを取得します。この構造体には、開いているファイルディスクリプタの情報、ファイルディスクリプタテーブル、その他のファイル関連データが含まれています。 -
int fd, error;
: これらの変数は、ファイルディスクリプタとエラーコードを格納するために宣言されています。 -
error = -EMFILE;
: この行は、error変数を-EMFILE
エラーコードで初期化します。これは、「開いているファイルが多すぎる」エラーを表しています。 -
spin_lock(&files->file_lock);
: この行は、ファイルディスクリプタテーブルが現在のプロセスによってのみアクセスされることを保証し、レース状態を防止するために、file_lock
スピンロックを取得します。 -
repeat:
: ファイルディスクリプターテーブルを拡張する必要がある場合に、空きファイルディスクリプターを見つけるプロセスを繰り返すために使用されるラベルです。 -
fd = find_next_zero_bit(...);
: この行は、files->next_fd
の位置から、open_fds
ビットマップの次に利用可能な(ゼロ)ビットを見つけるためにfind_next_zero_bit()
関数を呼び出します。これは、次に利用可能なファイルディスクリプタを表します。 -
if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) goto out;
: この行は、見つかったファイルディスクリプタがプロセスのオープンファイル制限(RLIMIT_NOFILE)を超えているかどうかをチェックする。もし超えていたら、関数はout
ラベルにジャンプし、スピンロックを解除してエラーコードを返します。 -
error = expand_files(files, fd);
: この行は、expand_files()
関数を呼び出して、新しいファイルディスクリプタを収容するためにファイルディスクリプタテーブルまたはopen_fds
ビットマップを拡張する必要があるかをチェックします。拡張が必要な場合、この関数はテーブルまたはビットマップのためにより多くのメモリを割り当てようとします。-
expand_files()
が負の値を返す場合、それはエラーを示し、関数はスピンロックを解放してエラーコードを返すためにout
ラベルにジャンプします。 -
expand_files()
が正の値を返す場合、テーブルまたはビットマップが正常に拡張されたことを意味します。この関数は、エラー変数を-EMFILE
に設定し、repeat
ラベルにジャンプして、空きファイルディスクリプタを見つける処理を再試行します。
-
-
FD_SET(fd, files->open_fds);
: この行は、open_fds
ビットマップの見つかったファイルディスクリプタに対応するビットを設定し、それを使用済みとしてマークします。 -
FD_CLR(fd, files->close_on_exec);
: この行は、close_on_exec
ビットマップで見つかったファイルディスクリプタに対応するビットをクリアし、exec()
呼び出し中にファイルディスクリプタを閉じてはならないことを示します。 -
files->next_fd = fd + 1;
: この行は、files_struct
のnext_fd
の値を、先ほど見つかったファイルディスクリプタの次のファイルディスクリプタに更新します。 - サニティチェック: このブロックは、ファイルディスクリプタテーブルのファイルディスクリプタエントリーがNULLでないかどうかをチェックする。NULLでない場合は、警告メッセージを表示し、そのエントリをNULLに設定します。
-
error = fd;
: この行は、error変数に見つかったファイルディスクリプタの値を設定します。 -
out:
: これは、終了パスのラベルです。 -
spin_unlock(&files->file_lock);
: この行は、file_lock
スピンロックを解放します。 -
return error;
: 最後に、見つかったファイルディスクリプタかエラーコードを返します。
get_unused_fd()
関数は、現在のプロセスで利用可能なファイルディスクリプタを見つけ、プロセスのオープンファイル制限を超えないことを確認し、必要に応じてファイルディスクリプタテーブルまたはビットマップを拡張します。このファイルディスクリプタは、使用済みとしてマークされ、プロセスと関連付けられ、exec()
呼び出し中に閉じるのを防ぐためにclose_on_exec
フラグがクリアされます。最後に、この関数は、見つかったファイルディスクリプタ、またはエラーコードを返します(該当する場合)。
所感-7
一瞬で概要を理解できる説明を提供してくれました。ChatGPTの説明がなければ、この処理の流れを把握するのに地道な調査時間を要するでしょう。
ただ、この処理においてcurrent
という変数が突然使用されている事に戸惑う人がいるかもしれません。よって、current
という変数が何なのか率直に聞いてみます。
入力-8
In the above source code, a variable named current is referenced. What is this?
(意訳)上記のソースコードではcurrentという変数が参照されています。これは何ですか?
回答-8
(意訳)Linuxカーネルにおいて、current
は、CPU上で現在実行中のプロセス(またはタスク)のtask_struct
へのポインタを返すマクロまたは関数です。task_struct
は、プロセスの状態、優先度、開いているファイル、シグナル処理、その他のプロセスに関する様々な情報を含むデータ構造です。
提供されたコードの文脈では、current
は現在実行中のプロセスのfiles_struct
とsignal
フィールドにアクセスするために使用されます。これらのフィールドには、プロセスのオープンファイルディスクリプタとリソース制限に関する情報がそれぞれ含まれています。current
を使用することで、カーネルコードはtask_struct
ポインタを明示的に渡さなくても、プロセス固有の情報にアクセスして操作することが容易になります。
所感-8
current
についても詳しい説明を得る事ができました。また、current
について説明しただけでなく、これまでの文脈からget_unused_fd
関数における役割まで説明してくれています。
task_struct
構造体というワードが新しく登場したので、そちらの方面へ質問を広げていく事もできます。
入力-9
Please describe the problems that can occur when the close_on_exec flag is not cleared, along with sample source code.
(意訳)close_on_execフラグがクリアされていない場合に発生する問題を、サンプルソースコードとともに教えてください。
回答-9
(意訳)close_on_exec
フラグは、exec()
ファミリーのシステムコールの1つを使用して新しいプログラムが実行されたときに、ファイルディスクリプタを自動的に閉じるかどうかを決定するために使用します。このフラグが設定されている場合、新しいプログラムの実行時にファイルディスクリプタが閉じられます。設定されていない場合、ファイルディスクリプタはexec()
呼び出しの間、開いたままであり、新しいプログラムに継承される。
close_on_exec
フラグが必要なときにクリアされない場合、exec()
呼び出し中にファイルディスクリプタが意図せず閉じられ、新しいプログラムが関連ファイルへのアクセスを失う可能性があります。このため、ファイルからのデータの読み書きができなくなったり、プログラム自身がファイルディスクリプタを閉じようとするとエラーになるなど、予期せぬ動作が発生する可能性があります。
以下に、この問題を説明する簡単な例を示します:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd = open("file.txt", O_CREAT | O_RDWR, 0666);
if (fd < 0) {
perror("open");
return 1;
}
// Assume the close_on_exec flag is NOT cleared for the file descriptor.
// This can be simulated by uncommenting the following line:
// fcntl(fd, F_SETFD, FD_CLOEXEC);
// Execute a new program (a simple "cat" command) that reads from the opened file descriptor.
char fd_str[16];
snprintf(fd_str, sizeof(fd_str), "%d", fd);
execl("/bin/cat", "cat", "--file-descriptor", fd_str, (char *)NULL);
// The program should never reach this point.
perror("execl");
close(fd);
return 1;
}
この例では、ファイルを開き、開いたファイルディスクリプタから読み取るためにcat
コマンドを実行しようとします。close_on_exec
フラグがクリアされていない場合(fcntl()
行のコメントを解除してシミュレーション)、execl()
が呼ばれたときにファイルディスクリプタは閉じられ、cat
コマンドがファイルディスクリプタからの読み込みに失敗する原因になります。
所感-9
get_unused_fd
関数内でFD_CLR(fd, files->close_on_exec);
を実行する事によってclose_on_exec
フラグをクリアしている処理があります。
これまでの説明で「exec()
呼び出し中に閉じるのを防ぐため」とありましたが、この対策がない事により実際どういう問題が起きるのか?はイメージしにくいです。そんな場合「サンプルのソースコードをもとに教えて下さい」と聞くと上記のようにユーザーレベルのコードをもとにわかりやすく教えてくれるので、理解が深まります。
入力-10
There is a filp_open function, what does the name represent?
(意訳)filp_open関数がありますが、これは何を意味していますか?
回答-10
(意訳)filp_open
という名前は、"file pointer open "の略称です。この関数は、LinuxカーネルのVFS(仮想ファイルシステム)レイヤーの一部で、ファイルを開き、開いているファイルオブジェクトを表すfile
構造体(ポインタ)を取得するために使用されます。file
構造体には、ファイルの位置、フラグ、関連するファイル操作など、開いているファイルに関する情報が含まれています。
filp_open
関数は、ファイルのパス、オープンフラグ、モードを引数として受け取ります。file
構造体ポインタを返す前に、パスの解決、ファイルシステム固有のオープン操作、アクセスチェックなど、さまざまなタスクを実行する責任があります。open()
やopenat()
などのシステムコールが実行されると、カーネルは最終的にfilp_open
を呼び出して実際のファイルオープン処理を行います。
所感-10
filp_open
関数のような、一見これは何の言葉の略なんだろう?という質問にも回答してくれるのは助かります(この例はわかりやすい方ではありますが…)。この情報を自分で調べようと思ったら、意外と時間がかかる事があります。
さて、ここで日本語で聞いた時は間違いの沼にハマってしまった、システムコールからsys_open
が呼ばれるまでの処理を聞いてみます。
入力-11
How is the process from the system call to the above sys_open being called realized?
(意訳)システムコールから上記のsys_openまでの処理はどのように実現されているのでしょうか。
回答-11
(意訳)ここでは、システムコールからsys_open
関数への処理がどのように実現されるのか、より焦点を絞って説明します:
-
ユーザー空間での関数呼び出し: ユーザー空間での関数呼び出し:ユーザー空間では、Cライブラリ(例:glibc)が提供する
open()
関数を呼び出します。この関数は、実際のシステムコールをラップするものです。 -
システムコールをトリガーする: Cライブラリの関数は、
open()
システムコールの適切なシステムコール番号(例えば、x86-64アーキテクチャでは__NR_open
または__NR_openat
)とその引数(ファイル名、フラグ、モード)をCPUレジスタにセットします。その後、特殊なCPU命令(x86-64のsyscallなど)を実行してカーネルモードに切り替え、制御をカーネルに移します。 -
システムコールハンドラ: カーネルでは、システムコールハンドラが実行されます。このハンドラは、CPUレジスタからシステムコール番号を読み出し、システムコールテーブルで調べて、対応するカーネル関数(この場合は
sys_open
)のアドレスを探します。 -
sys_openを呼び出す: システムコールハンドラは、CPUレジスタまたはスタックに渡された値を用いて
sys_open
関数の引数を設定し、sys_open
を呼び出します。
このプロセスにより、システムコールの実行中にユーザー空間からカーネル空間への制御された安全な移行が保証され、カーネルはsys_open
などの適切な関数を呼び出して要求を処理できるようになります。
所感-11
最初にLinuxカーネルのバージョンを2.6.11に指定した前提が抜けてしまっているような不安感に襲われたので、念の為バージョンを再度明示しつつ深堀りしてみます。
入力-12
Please let me know with the specific file name. the Linux version is 2.6.11.
(意訳)具体的なファイル名を教えてください。Linuxのバージョンは2.6.11です。
回答-12
(意訳)先ほどは混乱を招いたことをお詫びします。以下は、Linuxカーネルバージョン2.6.11のソースコードの文脈で、特定のファイル名を持つsys_open
関数へのシステムコールからの処理です:
-
ユーザ空間での関数呼び出し: ユーザー空間では、プログラムはCライブラリ(例:glibc)が提供する
open()
関数を呼び出します。open()
関数の実装は、glibcのソースコードにあります(カーネルのソースコードにはありません)。実装を含むファイルは、glibcのバージョンやアーキテクチャによって異なる場合があります。 -
システムコールのトリガーをかける:Cライブラリの関数で、システムコール番号とその引数を設定し、特別なCPU命令を実行してカーネルモードに切り替えます。この部分は通常、glibcのアセンブリコードを使って実装されており、アーキテクチャに依存します。x86-64の場合、glibcのソースコード中の
sysdeps/unix/sysv/linux/x86_64/syscall.S
にあります。 -
システムコールハンドラ: Linuxカーネルバージョン2.6.11のソースコードでは、システムコールを処理するためのエントリーポイントは、アーキテクチャに依存するものです。x86-64の場合、
arch/x86_64/kernel/entry.S
にあります(system_call
ラベル)。システムコールハンドラは、CPUレジスタからシステムコール番号を読み出し、システムコールテーブルでそれを調べます。 -
システムコール・テーブル: x86-64用のシステムコール表は、Linuxカーネルバージョン2.6.11のソースコード中の
arch/x86_64/kernel/syscall.c
にある。このテーブルは、システムコール番号を適切なカーネル関数にマッピングしています。open()
の場合、__NR_open
のようなエントリーがsys_open
にマップされていることがわかります。 -
sys_openを呼び出す: システムコールハンドラは、適切なカーネル関数を見つけると、引数を設定してその関数を呼び出します。
sys_open
関数は、Linuxカーネルバージョン2.6.11のソースコードのファイルfs/open.c
に実装されています。
まとめると、ユーザー空間のライブラリ(glibc)とカーネルのソースコードの間で相互作用があり、ユーザー空間の部分がシステムコールのトリガーを担当し、カーネルの部分がその処理と適切なカーネル関数の呼び出しを担当するというプロセスです。
所感-12
日本語の時は訂正の指摘が必要でちょっと頼りなかった感じですが、今回はバッチリの説明をしてくれました。これで、ユーザー空間からカーネル空間へのつなぎ込みの部分が、ソースコードレベルでわかりましたね。
もちろん、もっと深堀りたい人はここからいくらでも深堀りが可能です。ぜひ試してみて下さい😇
入力-13(※おまけ)
I often see the identifier NR in relation to a system call. What does this mean?
(意訳)システムコールに関連して、NRという識別子をよく見かけます。これはどういう意味なのでしょうか?
回答-13
(意訳)Linuxカーネルのシステムコールの文脈では、__NR_open
や__NR_read
などの識別子のNR
は、"Number "または "Number of the syscall "を意味します。これらの識別子は、各システムコールに割り当てられた固有のシステムコール番号を表す整数定数です。これらは、システムコール・テーブルで適切なカーネル関数を検索するためのインデックスとして使用されます。
これらのシステムコール番号を示すために、一般的に__NR_
という接頭辞が使用されます。ユーザースペースプログラムがシステムコールを行う場合、CPUレジスタに対応するシステムコール番号を設定し、カーネルが関連するカーネル関数を検索して呼び出すために使用します。この規約は、カーネルのソースコード内でシステムコールを管理し、参照するための一貫した組織的な方法です。
所感-13
ChatGPTに気軽に質問できる今だからこそ、質問しようというアクションに繋げる事ができました。今までは疑問に思ったとしても本題とそこまで関係ないので、一旦スルーしていたレベルの事柄です。子供の頃の好奇心が自分の中に再度芽生えた気がして、何だか嬉しかったです。これは最後の「まとめ」でも触れておきます。
まとめ
openシステムコールの処理を追いかける中で、本当にカーネルのソースコードリーディングが爆速になるのか?を見てきました。今回の記事では、sys_openの処理の途中で打ち切ってしまいましたが、同様の要領で行っていけばスイスイとカーネルの処理の理解を深めていけるイメージは伝えられたと思います。実際、私は最後まで深堀りをしていきましたが、半日程度で終える事ができました。
もちろん、以前リーディングした事がある内容を再度リーディングしたという事で単純比較はできませんが、以前はよくわからなくて一旦飛ばしてしまったところの理解を深められたところが沢山あったのは事実です。
以前私が休日の合間に今回の箇所のソースコードリーディングを行っていた時は、なんだかんだ二ヶ月くらい時間を費やしてしましました。二ヶ月毎日ずっと読んでいたわけではないのですが、進みが悪いと集中力が切れがちで時間が過ぎていってしまいます。日をまたぐとモチベーションも続きにくいです。
それが半日で終わってしまうのであれば、これは爆速と言っても良いのではないでしょうか?
特に画期的なのは、処理やソースコードの流れの解説だけでなく「この処理がユーザー空間のプログラムにどう影響するのかサンプルのソースコードとともに教えて!」や、「この識別子の役割や由来を教えて!」といった、ソースコードだけでは読み取りが難しいオーダーにも瞬時に答えてくれる事です。
私はよくソースコードリーディングを行うタイプなのですが、読んでいてどうしてもわからない時は膠着状態に入ってしまう事があります。OSSのかなりニッチな実装を読んでいたりするので、質問して答えてくれそうな相手は近くになかなかいません。たとえいたとしても、他人の時間を頂戴してしまう事に対する抵抗感は少なからずあります。
しかしながら、これからはChatGPTがそのような質問に答えてくれます。人間相手ではないので抵抗感は一ミリもありません😁。どんな質問にも24時間いつでも答えてくれるスーパーメンターが一人ひとりにつくのかと思うと、それはもう素晴らしい時代が到来したなと思います。
私達はきっと、子供の頃は好奇心旺盛で「なぜ?なぜ?」と大人へ質問していた事でしょう。ですが、その質問に答えてくれる大人がいないんだと悟るうちに、そうした何故?への問いをやめてしまったのかもしれません。
私は最近、子供の頃に戻ったようにChatGPTに様々な問いを投げているのですが、答えが返ってくる事が楽しくて仕方ありません。
AIが代わりにやってくれて便利という側面に目が行きがちですが、今回は人間自体への教育や育成、あるいは知的好奇心を満たす可能性が大幅に広がった側面にも光を当てたいと思い記事を書いてみました。
紹介したプロンプトや手法は、ソースコードリーディングを爆速にするためのごくごく一部です。ぜひ皆さんも、もっともっと爆速にできる独自の方法を模索していただければと思っております。
そして、これからのWith AIの世の中に適応し、自分の人生をもっともっと豊かなものにしていきたいですね。