PTHREADS(7) | Руководство программиста Linux | PTHREADS(7) |
pthreads - нити POSIX
В POSIX.1 определён набор интерфейсов (функции, заголовочные файлы) для работы с нитями, более известными как нити POSIX или Pthreads. В одном процессе может быть несколько нитей, которые выполняют одну программу. Эти нити работают с общей глобальной памятью (сегментами данных и кучи), но у каждой нити есть собственный стек (автоматические переменные).
Также, в POSIX.1 требуется, чтобы нити имели общий диапазон других атрибутов (т. е., эти атрибуты процесса, а не нити):
Как и для стека, в POSIX.1 определены другие атрибуты, которые уникальны в каждой нити:
Следующие свойства есть только в Linux и также уникальны в каждой нити:
Большинство функций pthreads при успешном выполнении возвращает 0 или номер ошибки в противном случае. Заметим, что функции pthreads не изменяют errno. Для каждой функции pthreads, которая может вернуть ошибку, в POSIX.1-2001 определено, что функция никогда не может завершиться с ошибкой EINTR.
Каждой нити процесса назначается уникальный идентификатор нити (имеет тип pthread_t). Этот идентификатор возвращается вызывающему pthread_create(3), а в самой нити её идентификатор можно получить с помощью pthread_self(3).
Внутри процесса гарантируется уникальность идентификаторов нитей (во всех функциях pthreads, которые принимают аргумент идентификатора нити, подразумевается, что указана нить из процесса вызывающего).
Система может повторно использовать идентификатор нити после объединения завершённой нити или отсоединения завершённой нити. В POSIX сказано: «Если приложение пытается использовать идентификатор нити, у который закончился срок жизни, то поведение не предсказуемо».
Нитебезопасная функция — это функция, которую можно безопасно (т. е., это приведёт к единым результатам независимо от окружения) вызывать из нескольких нитей одновременно.
В 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 function) — это функция, которую можно безопасно вызывать в приложении, в котором разрешено асинхронная отмена (смотрите pthread_setcancelstate(3)).
Согласно POSIX.1-2001 и POSIX.1-2008 только следующие функции должны быть безопасными асинхронными отменяемыми:
pthread_cancel() pthread_setcancelstate() pthread_setcanceltype()
В POSIX.1 определено, что некоторые функции должны, а несколько других могут было точками отмены. Если нить отменяема, её тип отменяемости откладывается, и ожидается запрос отмены нити, затем нить отменяется, когда она вызывает функцию, которая является точкой отмены.
Следующие функции должны быть точками отмены согласно POSIX.1-2001 и/или POSIX.1-2008:
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 (перемещена в список «может» в 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:
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() [Added in 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()
Реализация также может помечать другие функции, не указанные в стандарте, как точки отмены. В частности, реализация, вероятно, пометит как точку отмены любую нестандартную функцию, которая может блокироваться (большинство функций, работающих с файлами).
В Linux, программы, использующие программный интерфейс pthreads, должны компилироваться с помощью cc -pthread.
За всё время в библиотеке GNU C было две реализации нитей для Linux:
Обе реализации являются, так называемыми реализациями 1:1, то есть каждая нить отображается в планируемый элемента ядра. Обе реализации используют системный вызов Linux clone(2). В NPTL примитивы синхронизации нитей (мьютексы, объединение нитей и т .п.) реализованы с помощью системного вызова Linux futex(2).
Отличительные свойства данной реализации:
Реализация LinuxThreads отклоняется от спецификации POSIX.1 в нескольких местах, а именно:
В NPTL все нити процесса помещаются в одну группу нитей; все члены группы нитей имеют один PID. В NPTL нет управляющей нити.
Внутри NPTL используются первые два сигнала реального времени; эти сигналы нельзя использовать в приложениях. Подробности смотрите в nptl(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|nptl' Native POSIX Threads Library by Ulrich Drepper et al
В системах с glibc, которая поддерживает и LinuxThreads и NPTL (например, 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), fork(2), futex(2), gettid(2), proc(5), attributes(7), futex(7), nptl(7), sigevent(7), signal(7)
Различные справочные страницы pthreads, например: pthread_atfork(3), pthread_attr_init(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_mutexattr_destroy(3), pthread_mutexattr_init(3), pthread_once(3), pthread_spin_init(3), pthread_spin_lock(3), pthread_rwlockattr_setkind_np(3), pthread_setcancelstate(3), pthread_setcanceltype(3), pthread_setspecific(3), pthread_sigmask(3), pthread_sigqueue(3) и pthread_testcancel(3)
2019-03-06 | Linux |