PTHREADS(7) | Linux Programmer's Manual | PTHREADS(7) |
pthreads - POSIX スレッド
POSIX.1 は、一般に POSIX スレッドや Pthreads として知られる スレッドプログラミングのインターフェース群 (関数、ヘッダーファイル) を規定している。一つのプロセスは複数のスレッドを持つことができ、 全てのスレッドは同じプログラムを実行する。 これらのスレッドは同じ大域メモリー (データとヒープ領域) を共有するが、 各スレッドは自分専用のスタック (自動変数) を持つ。
POSIX.1 はスレッド間でどのような属性を共有するかについても定めている (つまり、これらの属性はスレッド単位ではなくプロセス全体で共通である):
スタックについても、POSIX.1 はどのような属性が 個々のスレッドで独立に管理されるかを規定している:
以下の Linux 特有の機能もスレッド単位である:
ほとんどの pthreads 関数は成功すると 0 を返し、 失敗した場合エラー番号を返す。 pthreads 関数は errno をセットしない点に注意すること。 POSIX.1-2001 では、 エラーを返す可能性のある pthreads 関数がエラー EINTR で失敗することは決してないと規定している。
あるプロセス内の各スレッドは (pthread_t 型の) 一意なスレッド識別子を持つ。 この識別子は、 pthread_create(3) の呼び出し元に返される。また、スレッドは自身のスレッド識別子を pthread_self(3) を使って取得できる。 スレッド ID の一意性が保証されるのは、一つのプロセス内においてのみである。 終了したスレッドが join された後では、スレッド ID は再利用される可能性がある。 スレッド ID を引き数に取る全てのスレッド関数において、 その ID は呼び出し元と同じプロセス内の一つのスレッドを参照する。
スレッドセーフな関数は、複数のスレッドから同時に呼び出しても安全な (すなわち、同時に呼び出されたかに関わらず、同じ結果を返す) 関数のことである。
POSIX.1-2001 と
POSIX.1-2008では、一部の例外を除き、
標準で規定されている全ての関数がスレッドセーフであることを要求している。
以下の関数が例外である。
asctime() basename() catgets() crypt() ctermid() (NULL でない引き数を渡された場合) ctime() dbm_clearerr() dbm_close() dbm_delete() dbm_error() dbm_fetch() dbm_firstkey() dbm_nextkey() dbm_open() dbm_store() dirname() dlerror() drand48() ecvt() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] encrypt() endgrent() endpwent() endutxent() fcvt() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] ftw() gcvt() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] getc_unlocked() getchar_unlocked() getdate() getenv() getgrent() getgrgid() getgrnam() gethostbyaddr() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] gethostbyname() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] gethostent() getlogin() getnetbyaddr() getnetbyname() getnetent() getopt() getprotobyname() getprotobynumber() getprotoent() getpwent() getpwnam() getpwuid() getservbyname() getservbyport() getservent() getutxent() getutxid() getutxline() gmtime() hcreate() hdestroy() hsearch() inet_ntoa() l64a() lgamma() lgammaf() lgammal() localeconv() localtime() lrand48() mrand48() nftw() nl_langinfo() ptsname() putc_unlocked() putchar_unlocked() putenv() pututxline() rand() readdir() setenv() setgrent() setkey() setpwent() setutxent() strerror() strsignal() [POSIX.1-2008 で追加された] strtok() system() [POSIX.1-2008 で追加された] tmpnam() (NULL でない引き数を渡された場合) ttyname() unsetenv() wcrtomb() (最後の引き数が NULL の場合) wcsrtombs() (最後の引き数が NULL の場合) wcstombs() wctomb()
async-cancel-safe 関数は、 非同期キャンセル機能が有効になっているアプリケーションで 安全に呼び出すことができる関数のことである (pthread_setcancelstate(3) を参照)。
以下の関数だけが、POSIX.1-2001
と POSIX.1-2008 で async-cancel-safe で
なければならないとされている。
pthread_cancel() pthread_setcancelstate() pthread_setcanceltype()
POSIX.1 の規定では、特定の関数は取り消しポイントでなければならず、 他の特定の関数は取り消しポイントであってもよいとされている。 あるスレッドが取り消し可能で、その取り消し種別 (cancelability type) が延期 (deferred) で、そのスレッドに対する取り消し要求が処理待ちの場合、 取り消しポイントである関数を呼び出した時点で、そのスレッドのキャンセルが 行われる。
POSIX.1-2001 と POSIX.1-2008 の両方、もしくはいずれか一方では、 以下の関数は、取り消しポイント (cancellation points) で あることが必須となっている。
accept() aio_suspend() clock_nanosleep() close() connect() creat() fcntl() F_SETLKW fdatasync() fsync() getmsg() getpmsg() lockf() F_LOCK mq_receive() mq_send() mq_timedreceive() mq_timedsend() msgrcv() msgsnd() msync() nanosleep() open() openat() [POSIX.1-2008 で追加された] pause() poll() pread() pselect() pthread_cond_timedwait() pthread_cond_wait() pthread_join() pthread_testcancel() putmsg() putpmsg() pwrite() read() readv() recv() recvfrom() recvmsg() select() sem_timedwait() sem_wait() send() sendmsg() sendto() sigpause() [POSIX.1-2001 only (moves to "may" list in POSIX.1-2008)] sigsuspend() sigtimedwait() sigwait() sigwaitinfo() sleep() system() tcdrain() usleep() [POSIX.1-2001 のみ (POSIX.1-2008 で削除された)] wait() waitid() waitpid() write() writev()
POSIX.1-2001 と POSIX.1-2008 の両方、もしくはいずれか一方では、 以下の関数は、取り消しポイント (cancellation points) で あってもよいことになっている。
access() asctime() asctime_r() catclose() catgets() catopen() chmod() [POSIX.1-2008 で追加された] chown() [POSIX.1-2008 で追加された] closedir() closelog() ctermid() ctime() ctime_r() dbm_close() dbm_delete() dbm_fetch() dbm_nextkey() dbm_open() dbm_store() dlclose() dlopen() dprintf() [POSIX.1-2008 で追加された] endgrent() endhostent() endnetent() endprotoent() endpwent() endservent() endutxent() faccessat() [POSIX.1-2008 で追加された] fchmod() [POSIX.1-2008 で追加された] fchmodat() [POSIX.1-2008 で追加された] fchown() [POSIX.1-2008 で追加された] fchownat() [POSIX.1-2008 で追加された] fclose() fcntl() (cmd 引き数が何であっても) fflush() fgetc() fgetpos() fgets() fgetwc() fgetws() fmtmsg() fopen() fpathconf() fprintf() fputc() fputs() fputwc() fputws() fread() freopen() fscanf() fseek() fseeko() fsetpos() fstat() fstatat() [POSIX.1-2008 で追加された] ftell() ftello() ftw() futimens() [POSIX.1-2008 で追加された] fwprintf() fwrite() fwscanf() getaddrinfo() getc() getc_unlocked() getchar() getchar_unlocked() getcwd() getdate() getdelim() [POSIX.1-2008 で追加された] getgrent() getgrgid() getgrgid_r() getgrnam() getgrnam_r() gethostbyaddr() [SUSv3 のみ (この関数は POSIX.1-2008 で削除されている)] gethostbyname() [SUSv3 のみ (この関数は POSIX.1-2008 で削除されている)] gethostent() gethostid() gethostname() getline() [POSIX.1-2008 で追加された] getlogin() getlogin_r() getnameinfo() getnetbyaddr() getnetbyname() getnetent() getopt() (opterr が 0 以外の場合) getprotobyname() getprotobynumber() getprotoent() getpwent() getpwnam() getpwnam_r() getpwuid() getpwuid_r() gets() getservbyname() getservbyport() getservent() getutxent() getutxid() getutxline() getwc() getwchar() getwd() [SUSv3 のみ (この関数は POSIX.1-2008 で削除されている)] glob() iconv_close() iconv_open() ioctl() link() linkat() [POSIX.1-2008 で追加された] lio_listio() [POSIX.1-2008 で追加された] localtime() localtime_r() lockf() [POSIX.1-2008 で追加された] lseek() lstat() mkdir() [POSIX.1-2008 で追加された] mkdirat() [POSIX.1-2008 で追加された] mkdtemp() [POSIX.1-2008 で追加された] mkfifo() [POSIX.1-2008 で追加された] mkfifoat() [POSIX.1-2008 で追加された] mknod() [POSIX.1-2008 で追加された] mknodat() [POSIX.1-2008 で追加された] mkstemp() mktime() nftw() opendir() openlog() pathconf() pclose() perror() popen() posix_fadvise() posix_fallocate() posix_madvise() posix_openpt() posix_spawn() posix_spawnp() posix_trace_clear() posix_trace_close() posix_trace_create() posix_trace_create_withlog() posix_trace_eventtypelist_getnext_id() posix_trace_eventtypelist_rewind() posix_trace_flush() posix_trace_get_attr() posix_trace_get_filter() posix_trace_get_status() posix_trace_getnext_event() posix_trace_open() posix_trace_rewind() posix_trace_set_filter() posix_trace_shutdown() posix_trace_timedgetnext_event() posix_typed_mem_open() printf() psiginfo() [POSIX.1-2008 で追加された] psignal() [POSIX.1-2008 で追加された] pthread_rwlock_rdlock() pthread_rwlock_timedrdlock() pthread_rwlock_timedwrlock() pthread_rwlock_wrlock() putc() putc_unlocked() putchar() putchar_unlocked() puts() pututxline() putwc() putwchar() readdir() readdir_r() readlink() [POSIX.1-2008 で追加された] readlinkat() [POSIX.1-2008 で追加された] remove() rename() renameat() [POSIX.1-2008 で追加された] rewind() rewinddir() scandir() [POSIX.1-2008 で追加された] scanf() seekdir() semop() setgrent() sethostent() setnetent() setprotoent() setpwent() setservent() setutxent() sigpause() [POSIX.1-2008 で追加された] stat() strerror() strerror_r() strftime() symlink() symlinkat() [POSIX.1-2008 で追加された] sync() syslog() tmpfile() tmpnam() ttyname() ttyname_r() tzset() ungetc() ungetwc() unlink() unlinkat() [POSIX.1-2008 で追加された] utime() [POSIX.1-2008 で追加された] utimensat() [POSIX.1-2008 で追加された] utimes() [POSIX.1-2008 で追加された] vdprintf() [POSIX.1-2008 で追加された] vfprintf() vfwprintf() vprintf() vwprintf() wcsftime() wordexp() wprintf() wscanf()
実装時に、標準規格で規定されていないその他の関数を取り消しポイント とすることも認められている。 特に、停止 (block) する可能性がある非標準の関数を取り消しポイントと する実装はあり得ることだろう (ファイルを扱う可能性のあるほとんどの関数がこれに含まれる)。
Linux では、Pthreads API を用いたプログラムは cc -pthread でコンパイルすべきである。
これまで、2つのスレッドの実装が Linux の GNU C ライブラリにより 提供されてきた。
どちらの実装もいわゆる 1:1 実装、すなわち個々のスレッドが カーネルのスケジューリング実体にマッピングされる。 どちらのスレッドの実装も Linux の clone(2) システムコールを利用している。 NPTL では、スレッド同期の基本機構 (mutex や スレッドの join 等) は Linux の futex(2) システムコールを使って実装されている。
この実装の大きな特徴は以下の通りである:
LinuxThreads の実装では POSIX.1 仕様から逸脱している点が いくつかある。以下に示すような点がある:
NPTL では、一つのプロセスの全てのスレッドは同じスレッドグループ に属する; スレッドグループの全メンバーは同じ PID を共有する。 NPTL は管理スレッド (manager thread) を利用しない。 NPTL は内部でリアルタイムシグナルのうち最初の 2つの番号を使用しており (signal(7) 参照)、これらのシグナルはアプリケーションでは使用できない。
NPTL にも POSIX.1 に準拠していない点が少なくとも一つある:
NPTL の標準非準拠な点のうちいくつかは以前のカーネルでのみ発生する:
NPTL の実装では以下の点についても注意すること:
glibc 2.3.2 以降では、 getconf(1) コマンドを使って、 システムのスレッド実装を判定することができる。 以下に例を示す:
bash$ getconf GNU_LIBPTHREAD_VERSION NPTL 2.3.4
ぞれ以前の glibc のバージョンでは、以下のようなコマンドで デフォルトのスレッド実装を判定することができる。
bash$ $( ldd /bin/ls | grep libc.so | awk '{print $3}' ) | \
egrep -i 'threads|ntpl'
Native POSIX Threads Library by Ulrich Drepper et al
LinuxThreads と NPTL の両方をサポートしている glibc (glibc 2.3.x) があるシステムでは、 LD_ASSUME_KERNEL 環境変数を使うことで、動的リンカーがデフォルトで 選択するスレッド実装を上書きすることができる。 この変数により、動的リンカーが特定のバージョンのカーネル上で 動作していると仮定するように指定する。 NPTL が必要とするサポート機能を提供していないカーネルバージョンを 指定することで、強制的に LinuxThreads を使うことができる (このようなことをする最もありそうな場面は、 LinuxThreads の標準非準拠な振舞いに依存する (壊れた) アプリケーション を動作させる場合だろう)。 以下に例を示す:
bash$ $( LD_ASSUME_KERNEL=2.2.5 ldd /bin/ls | grep libc.so | \
awk '{print $3}' ) | egrep -i 'threads|nptl'
linuxthreads-0.10 by Xavier Leroy
clone(2), futex(2), gettid(2), futex(7), sigevent(7), signal(7),
pthreads の各種マニュアルページ、例えば: pthread_attr_init(3), pthread_atfork(3), pthread_cancel(3), pthread_cleanup_push(3), pthread_cond_signal(3), pthread_cond_wait(3), pthread_create(3), pthread_detach(3), pthread_equal(3), pthread_exit(3), pthread_key_create(3), pthread_kill(3), pthread_mutex_lock(3), pthread_mutex_unlock(3), pthread_once(3), pthread_setcancelstate(3), pthread_setcanceltype(3), pthread_setspecific(3), pthread_sigmask(3), pthread_sigqueue(3), and pthread_testcancel(3)
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-05-21 | Linux |