連載
» 2017年07月13日 05時00分 UPDATE

main()関数の前には何があるのか(9):エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (3/3)

[坂井弘亮,著]
前のページへ 1|2|3       

exit()の処理を読む

 exit()で行われている処理も見てみよう。

 ソースコードはどこにあるだろうか。man 3 exitを参照すると、以下のようになっている。

EXIT(3)			Linux Programmer's Manual		EXIT(3)
 
NAME
	exit - cause normal process termination
 
SYNOPSIS
	#include <stdlib.h>
 
	void exit(int status);
...

 #includeの部分を見てほしい。exit()はstdlib.hでプロトタイプ宣言がされているようだ。

 そしてglibcのトップディレクトリを見ると、stdlibというディレクトリがある。ここにexit()の本体があるのではないだろうか。

[user@localhost ~]$ cd glibc-2.21
[user@localhost glibc-2.21]$ ls stdlib/*exit*
stdlib/at_quick_exit.c		stdlib/exit.h
stdlib/atexit.c			stdlib/old_atexit.c
stdlib/cxa_at_quick_exit.c	stdlib/on_exit.c
stdlib/cxa_atexit.c		stdlib/quick_exit.c
stdlib/cxa_thread_atexit_impl.c	stdlib/tst-tls-atexit-lib.c
stdlib/exit.c			stdlib/tst-tls-atexit.c
[user@localhost glibc-2.21]$

 exit.cというファイルがある。見てみると、exit()が以下のように定義されていた。

101:void
102:exit (int status)
103:{
104: __run_exit_handlers (status, &__exit_funcs, true);
105:}

 __run_exit_handlers()という関数が呼ばれるようだ。そして__run_exit_handlers()は同一ファイル内で以下のように定義されていた。

28:/* Call all functions registered with `atexit' and `on_exit',
29:	in the reverse of the order in which they were registered
30:	perform stdio cleanup, and terminate program execution with STATUS. */
31:void
32:attribute_hidden
33:__run_exit_handlers (int status, struct exit_function_list **listp,
34:			bool run_list_atexit)
35:{
...
42:	/* We do it this way to handle recursive calls to exit () made by
43:	   the functions registered with `atexit' and `on_exit'. We call
44:	   everyone on the list and use the status value in the last
45:	   exit (). */
46:	while (*listp != NULL)
47:	 {
48:	   struct exit_function_list *cur = *listp;
49:
50:	 while (cur->idx > 0)
51:	   {
52:	     const struct exit_function *const f =
53:	       &cur->fns[--cur->idx];
54:	     switch (f->flavor)
55:	       {
56:		 void (*atfct) (void);
57:		 void (*onfct) (int status, void *arg);
58:		 void (*cxafct) (void *arg, int status);
...
77:	      case ef_cxa:
78:		 cxafct = f->func.cxa.fn;
79:#ifdef PTR_DEMANGLE
80:		 PTR_DEMANGLE (cxafct);
81:#endif
82:		 cxafct (f->func.cxa.arg, status);
83:		 break;
84:	       }
...
97:	 _exit (status);
98:}

 最後に_exit()が呼ばれていることが確認できる。

 またwhile()でループしているのは、おそらくatexit()の処理だろう。atexit()により登録された関数が、ここで順次呼ばれていくわけだ。

atexit()の処理を読む

 atexit()のソースコードも見ておこう。man atexitではstdlib.hをインクルードするように書いてあるので、stdlibというディレクトリに本体がありそうだ。

 見てみるとstdlib/atexit.cというファイルがある。そしてその中でatexit()は以下のように定義されている。

43:/* Register FUNC to be executed by `exit'. */
44:int
45:#ifndef atexit
46:attribute_hidden
47:#endif
48:atexit (void (*func) (void))
49:{
50:	 return __cxa_atexit ((void (*) (void *)) func, NULL,
51:			 &__dso_handle == NULL ? NULL : __dso_handle);
52:}

 __cxa_atexit()という関数を呼んでいるようだ。これはどこにあるだろうか。

[user@localhost glibc-2.21]$ grep __cxa_atexit */*
...
stdlib/cxa_atexit.c:__cxa_atexit (void (*func) (void *), void *arg, void *d)
...

 stdlib/cxa_atexit.cを見ればいいようだ。

52:/* Register a function to be called by exit or when a shared library
53:	is unloaded. This function is only called from code generated by
54:	the C++ compiler. */
55:int
56:__cxa_atexit (void (*func) (void *), void *arg, void *d)
57:{
58: return __internal_atexit (func, arg, d, &__exit_funcs);
59:}

 今度は_ _internal_atexit()が呼ばれている。そして_ _internal_atexit()は同一ファイル内で、以下のように定義されている。

30:int
31:attribute_hidden
32:__internal_atexit (void (*func) (void *), void *arg, void *d,
33:			struct exit_function_list **listp)
34:{
35:	struct exit_function *new = __new_exitfn (listp);
36:
37:	if (new == NULL)
38:	  return -1;
39:
40:#ifdef PTR_MANGLE
41:  PTR_MANGLE (func);
42:#endif
43:  new->func.cxa.fn = (void (*) (void *, int)) func;
44:  new->func.cxa.arg = arg;
45:  new->func.cxa.dso_handle = d;
46:  atomic_write_barrier ();
47:  new->flavor = ef_cxa;
48:  return 0;
49:}

 第4引数で渡されるlistpに対して新たなエントリとして、第1引数で渡される関数を登録するようだ。呼び出し元の__cxa_atexit()を見ると、これには&__exit_funcsというアドレスが渡されている。これがatexit()の関数のリストだ。

 もう一度「exit()の処理を読む」の項のexit()を見直してみよう。

 __exit_funcsを第2引数にして、__run_exit_handlers()を呼んでいる。これが__run_exit_handlers()に渡され、登録された関数を順次実行しているわけだ。

書籍紹介

ハロー“Hello,World” OSと標準ライブラリのシゴトとしくみ

ハロー“Hello, World” OSと標準ライブラリのシゴトとしくみ

坂井弘亮著
秀和システム 3,200円

C言語の入門書では、"Hello, World"と出力するプログラムを最初に作るのが定番です。"Hello, World"は、たった7行の単純なプログラムですが、printf()の先では何が行われているのか、main()の前にはいったい何があるのか、考えてみると謎だらけです。本書は、基礎中の基礎である"Hello, World"プログラムを元に、OSと標準ライブラリの仕組みをあらゆる角度からとことん解析します。資料に頼らず、自分の手で調べる方法がわかります。


注文ページへ


前のページへ 1|2|3       

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。