Linuxでは、strace
ユーティリティを使用すると、プログラムによって行われたシステムコールを確認できます。だから、このようなプログラムを取る
int main(){
printf( "x");
0を返します。
}
たとえば、次のようにコンパイルしてからprintx
、strace printx
execve( "./ printx"、["./printx"]、[/ * 49 vars * /])= 0
brk(0)= 0xb66000
access( "/ etc / ld.so.nohwcap"、F_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)
mmap(NULL、8192、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0x7fa6dc0e5000
access( "/ etc / ld.so.preload"、R_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)
open( "/ etc / ld.so.cache"、O_RDONLY | O_CLOEXEC)= 3
fstat(3、{st_mode = S_IFREG | 0644、st_size = 119796、...})= 0
mmap(NULL、119796、PROT_READ、MAP_PRIVATE、3、0)= 0x7fa6dc0c7000
close(3)= 0
access( "/ etc / ld.so.nohwcap"、F_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)
open( "/ lib / x86_64-linux-gnu / libc.so.6"、O_RDONLY | O_CLOEXEC)= 3
read(3、 "\ 177ELF \ 2 \ 1 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 3 \ 0> \ 0 \ 1 \ 0 \ 0 \ 0 \ 200 \ 30 \ 2 \ 0 \ 0 \ 0 \ 0 \ 0 "...、832)= 832
fstat(3、{st_mode = S_IFREG | 0755、st_size = 1811128、...})= 0
mmap(NULL、3925208、PROT_READ | PROT_EXEC、MAP_PRIVATE | MAP_DENYWRITE、3、0)= 0x7fa6dbb06000
mprotect(0x7fa6dbcbb000、2093056、PROT_NONE)= 0
mmap(0x7fa6dbeba000、24576、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_FIXED | MAP_DENYWRITE、3、0x1b4000)= 0x7fa6dbeba000
mmap(0x7fa6dbec0000、17624、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS、-1、0)= 0x7fa6dbec0000
close(3)= 0
mmap(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0x7fa6dc0c6000
mmap(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0x7fa6dc0c5000
mmap(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0x7fa6dc0c4000
arch_prctl(ARCH_SET_FS、0x7fa6dc0c5700)= 0
mprotect(0x7fa6dbeba000、16384、PROT_READ)= 0
mprotect(0x600000、4096、PROT_READ)= 0
mprotect(0x7fa6dc0e7000、4096、PROT_READ)= 0
munmap(0x7fa6dc0c7000、119796)= 0
fstat(1、{st_mode = S_IFCHR | 0620、st_rdev = makedev(136、0)、...})= 0
mmap(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0x7fa6dc0e4000
write(1、 "x"、1x)= 1
exit_group(0)=?
ゴムは、トレースの最後から2番目の呼び出しで道路に出会う(分類、以下を参照)write(1,"x",1x)
。この時点で、コントロールはユーザーランドprintx
から残りを処理するLinuxカーネルに渡されます。write()
で宣言されたラッパー関数ですunistd.h
extern ssize_t write(int __fd、__const void * __ buf、size_t __n)__wur;
ほとんどのシステムコールはこのようにラップされます。ラッパー関数は、その名前が示すように、引数を正しいレジスターに配置し、ソフトウェア割り込み0x80を実行する薄いコードレイヤーにすぎません。カーネルは割り込みをトラップし、残りは履歴です。または、少なくともそれはそれが機能していた方法です。明らかに、割り込みトラップのオーバーヘッドは非常に高く、以前の投稿で指摘されているように、最新のCPUアーキテクチャはsysenter
アセンブリ命令を導入しました。これにより、同じ結果が高速で実現されます。このページのシステムコールには、システムコールがどのように機能するかについての非常に優れた要約があります。
私と同じように、あなたはおそらくこの答えに少しがっかりするだろうと思います。明らかに、ある意味で、これは誤った底ですwrite()
。グラフィックカードのフレームバッファは、実際には「x」の文字が画面に表示されるように変更されています。カーネルに飛び込むことによって(「道路に対するゴム」のアナロジーにとどまるために)接点にズームインすることは、時間がかかる努力であれば教育的であることは確実です。バッファリングされた出力ストリーム、文字デバイスなど、抽象化のいくつかのレイヤーを移動する必要があると思います。これをフォローアップすることにした場合は、必ず結果を投稿してください:)