NAME
pthreads - POSIX threads
DESCRIPTION
POSIX.1 指定了一组叫做POSIX线程或Pthreads的编程接口(函数,头文件)。单个进程可以包含多个线程,所有线程执行相同的程序。这些线程共享相同的全局存储空间(数据段和堆),但每个线程有其自己的栈(自动变量)。
POSIX.1 也要求共享一些其它属性(例如,进程范围内而不是每个线程的属性)
- 进程ID
- 父进程ID
- 进程组ID和会话ID
- 控制终端
- 用户和组IDs
- 打开的文件描述符
- 记录锁(参考fcntl(2))
- 信号处理
- 文件创建掩码模式(umask(2))
- 当前目录(chdir(2))和root目录(chroot(2))
- 定时器(settimer(2))和POSIX定时器(timer_create(2))
- 优先级值(setpriority(2))
- 资源限制(setrlimit(2))
- CPU使用时间(times(2))和资源使用情况的测量值
和栈一样,POSIX.1指出,对于各个线程其它各属性是不同的,包括:
- 线程ID(pthread_t数据类型)
- 信号掩码(pthread_sigmask(3))
- errno变量
- 替换信号处理栈
- 实时调度策略和优先级(shed_setscheduler(2)和sched_setparam(2))
下面是只是Linux系统的特点,对于每个线程都不同:
- capabilities(capabilities(7))
- CPU affinity(sched_setaffinity(2))
线程函数返回值
大多数线程函数成功后返回0,失败后返回错误码。注意线程函数不会设置errno值,对每个能返回错误码的线程函数,POSIX.1指定这种函数对于错误EINTR永远也不会失败。
线程ID
在一个进程中每个线程都有唯一一个线程ID(保存在pthread_t类型中),这个ID通过调用pthread_create(3)得到,一个线程可以通过pthread_self()得到其自身线程ID,线程ID仅在进程范围内是唯一的,一个线程ID还能被重新利用,例如,在一个终止线程在被回收后,或者一个分离的线程终止后,这个线程ID可能被新的线程使用。在所有接收线程ID作为参数的线程函数中,这个ID实际指向相同调用进程中的线程。
线程安全函数
一个线程安全函数可以同时被多个线程安全地调用(例如,不管是什么都能给出相同的结果)。
POSIX.1-2001 和 POSIX.1-2008 要求所有在标准中指定的函数是线程安全的, 除了下面的函数:
asctime()
basename()
catgets()
crypt()
ctermid() if passed a non-NULL argument
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 only (function removed in POSIX.1-2008)]
encrypt()
endgrent()
endpwent()
endutxent()
fcvt() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
ftw()
gcvt() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
getc_unlocked()
getchar_unlocked()
getdate()
getenv()
getgrent()
getgrgid()
getgrnam()
gethostbyaddr() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
gethostbyname() [POSIX.1-2001 only (function removed in 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()
getgrent()
getgrgid()
getgrnam()
gethostbyaddr() [POSIX.1-2001 only (function removed in POSIX.1-2008)]
gethostbyname() [POSIX.1-2001 only (function removed in 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()
localeconv()
localtime()
lrand48()
mrand48()
nftw()
nl_langinfo()
ptsname()
putc_unlocked()
putchar_unlocked()
putenv()
pututxline()
rand()
readdir()
setenv()
setgrent()
setkey()
setpwent()
setutxent()
strerror()
strsignal() [Added in POSIX.1-2008]
strtok()
system() [Added in POSIX.1-2008]
tmpnam() if passed a non-NULL argument
ttyname()
unsetenv()
wcrtomb() if its final argument is NULL
wcsrtombs() if its final argument is NULL
wcstombs()
wctomb()
异步撤销安全函数
一个异步撤销安全函数是当异步撤销能力被启用后,在应用中可以被安全地调用。
仅仅下面的函数在POSIX.1-2001和POSIX.1-2008要求是异步线程安全的:
pthread_cancel()
pthread_setcancelstate()
pthread_setcanceltype()
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() [Added in 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 only (function removed in POSIX.1-2008)]
wait()
waitid()
waitpid()
write()
writev()
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() [Added in 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 only (function removed in POSIX.1-2008)]
wait()
waitid()
waitpid()
write()
writev()
下面的函数根据POSIX.1-2001和/或POSIX.1-2008或许可能是撤销点:
access()
asctime()
asctime_r()
catclose()
catgets()
catopen()
chmod() [Added in POSIX.1-2008]
chown() [Added in POSIX.1-2008]
closedir()
closelog()
ctermid()
ctime()
ctime_r()
dbm_close()
dbm_delete()
dbm_fetch()
dbm_nextkey()
dbm_open()
dbm_store()
dlclose()
dlopen()
dprintf() [Added in POSIX.1-2008]
endgrent()
endhostent()
endnetent()
endprotoent()
endpwent()
endservent()
endutxent()
faccessat() [Added in POSIX.1-2008]
fchmod() [Added in POSIX.1-2008]
fchmodat() [Added in POSIX.1-2008]
fchown() [Added in POSIX.1-2008]
fchownat() [Added in POSIX.1-2008]
fclose()
fcntl() (for any value of cmd argument)
fflush()
fgetc()
fgetpos()
fgets()
fgetwc()
fgetws()
fmtmsg()
fopen()
fpathconf()
fprintf()
fputc()
fputs()
fputwc()
fputws()
fread()
freopen()
fscanf()
fseek()
fseeko()
fsetpos()
fstat()
fstatat() [Added in POSIX.1-2008]
ftell()
ftello()
ftw()
futimens() [Added in POSIX.1-2008]
fwprintf()
fwrite()
fwscanf()
getaddrinfo()
getc()
getc_unlocked()
getchar()
getchar_unlocked()
getcwd()
getdate()
getdelim() [Added in POSIX.1-2008]
getgrent()
getgrgid()
getgrgid_r()
getgrnam()
getgrnam_r()
gethostbyaddr() [SUSv3 only (function removed in POSIX.1-2008)]
gethostbyname() [SUSv3 only (function removed in POSIX.1-2008)]
gethostent()
gethostid()
gethostname()
getline() [Added in POSIX.1-2008]
getlogin()
getlogin_r()
getnameinfo()
getnetbyaddr()
getnetbyname()
getnetent()
getopt() (if opterr is nonzero)
getprotobyname()
getprotobynumber()
getprotoent()
getpwent()
getpwnam()
getpwnam_r()
getpwuid()
getpwuid_r()
gets()
getservbyname()
getservbyport()
getservent()
getutxent()
getutxid()
getutxline()
getwc()
getwchar()
getwd() [SUSv3 only (function removed in POSIX.1-2008)]
glob()
iconv_close()
iconv_open()
ioctl()
link()
linkat() [Added in POSIX.1-2008]
lio_listio() [Added in POSIX.1-2008]
localtime()
localtime_r()
lockf() [Added in POSIX.1-2008]
lseek()
lstat()
mkdir() [Added in POSIX.1-2008]
mkdirat() [Added in POSIX.1-2008]
mkdtemp() [Added in POSIX.1-2008]
mkfifo() [Added in POSIX.1-2008]
mkfifoat() [Added in POSIX.1-2008]
mknod() [Added in POSIX.1-2008]
mknodat() [Added in 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() [Added in POSIX.1-2008]
psignal() [Added in 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() [Added in POSIX.1-2008]
readlinkat() [Added in POSIX.1-2008]
remove()
rename()
renameat() [Added in POSIX.1-2008]
rewind()
rewinddir()
scandir() [Added in POSIX.1-2008]
scanf()
seekdir()
semop()
setgrent()
sethostent()
setnetent()
setprotoent()
setpwent()
setservent()
setutxent()
sigpause() [Added in POSIX.1-2008]
stat()
strerror()
strerror_r()
strftime()
symlink()
symlinkat() [Added in POSIX.1-2008]
sync()
syslog()
tmpfile()
tmpnam()
ttyname()
ttyname_r()
tzset()
ungetc()
ungetwc()
unlink()
unlinkat() [Added in POSIX.1-2008]
utime() [Added in POSIX.1-2008]
utimensat() [Added in POSIX.1-2008]
utimes() [Added in POSIX.1-2008]
vdprintf() [Added in POSIX.1-2008]
vfprintf()
vfwprintf()
vprintf()
vwprintf()
wcsftime()
wordexp()
wprintf()
wscanf()
asctime()
asctime_r()
catclose()
catgets()
catopen()
chmod() [Added in POSIX.1-2008]
chown() [Added in POSIX.1-2008]
closedir()
closelog()
ctermid()
ctime()
ctime_r()
dbm_close()
dbm_delete()
dbm_fetch()
dbm_nextkey()
dbm_open()
dbm_store()
dlclose()
dlopen()
dprintf() [Added in POSIX.1-2008]
endgrent()
endhostent()
endnetent()
endprotoent()
endpwent()
endservent()
endutxent()
faccessat() [Added in POSIX.1-2008]
fchmod() [Added in POSIX.1-2008]
fchmodat() [Added in POSIX.1-2008]
fchown() [Added in POSIX.1-2008]
fchownat() [Added in POSIX.1-2008]
fclose()
fcntl() (for any value of cmd argument)
fflush()
fgetc()
fgetpos()
fgets()
fgetwc()
fgetws()
fmtmsg()
fopen()
fpathconf()
fprintf()
fputc()
fputs()
fputwc()
fputws()
fread()
freopen()
fscanf()
fseek()
fseeko()
fsetpos()
fstat()
fstatat() [Added in POSIX.1-2008]
ftell()
ftello()
ftw()
futimens() [Added in POSIX.1-2008]
fwprintf()
fwrite()
fwscanf()
getaddrinfo()
getc()
getc_unlocked()
getchar()
getchar_unlocked()
getcwd()
getdate()
getdelim() [Added in POSIX.1-2008]
getgrent()
getgrgid()
getgrgid_r()
getgrnam()
getgrnam_r()
gethostbyaddr() [SUSv3 only (function removed in POSIX.1-2008)]
gethostbyname() [SUSv3 only (function removed in POSIX.1-2008)]
gethostent()
gethostid()
gethostname()
getline() [Added in POSIX.1-2008]
getlogin()
getlogin_r()
getnameinfo()
getnetbyaddr()
getnetbyname()
getnetent()
getopt() (if opterr is nonzero)
getprotobyname()
getprotobynumber()
getprotoent()
getpwent()
getpwnam()
getpwnam_r()
getpwuid()
getpwuid_r()
gets()
getservbyname()
getservbyport()
getservent()
getutxent()
getutxid()
getutxline()
getwc()
getwchar()
getwd() [SUSv3 only (function removed in POSIX.1-2008)]
glob()
iconv_close()
iconv_open()
ioctl()
link()
linkat() [Added in POSIX.1-2008]
lio_listio() [Added in POSIX.1-2008]
localtime()
localtime_r()
lockf() [Added in POSIX.1-2008]
lseek()
lstat()
mkdir() [Added in POSIX.1-2008]
mkdirat() [Added in POSIX.1-2008]
mkdtemp() [Added in POSIX.1-2008]
mkfifo() [Added in POSIX.1-2008]
mkfifoat() [Added in POSIX.1-2008]
mknod() [Added in POSIX.1-2008]
mknodat() [Added in 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() [Added in POSIX.1-2008]
psignal() [Added in 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() [Added in POSIX.1-2008]
readlinkat() [Added in POSIX.1-2008]
remove()
rename()
renameat() [Added in POSIX.1-2008]
rewind()
rewinddir()
scandir() [Added in POSIX.1-2008]
scanf()
seekdir()
semop()
setgrent()
sethostent()
setnetent()
setprotoent()
setpwent()
setservent()
setutxent()
sigpause() [Added in POSIX.1-2008]
stat()
strerror()
strerror_r()
strftime()
symlink()
symlinkat() [Added in POSIX.1-2008]
sync()
syslog()
tmpfile()
tmpnam()
ttyname()
ttyname_r()
tzset()
ungetc()
ungetwc()
unlink()
unlinkat() [Added in POSIX.1-2008]
utime() [Added in POSIX.1-2008]
utimensat() [Added in POSIX.1-2008]
utimes() [Added in POSIX.1-2008]
vdprintf() [Added in POSIX.1-2008]
vfprintf()
vfwprintf()
vprintf()
vwprintf()
wcsftime()
wordexp()
wprintf()
wscanf()
具体实现或许会标记其它非标准指定的撤销函数,特别地,具体实现可能会标记出其它任何阻塞的非标准函数作为撤销点。(这包括大多数可能生成文件的函数)
在Linux上编译多线程程序
在Linux上,程序使用了Phtreads API应该使用 cc -pthread来编译。
POSIX线程的Linux实现
随着时间的推移,在GNU C库中提供两种线程的实现
- LinuxThreads: 这是原始的Pthreads实现,从glibc 2.4以后,这种实现不再被支持。
- NPTL: (Native POSIX Threads Library)这种实现比较新,与LinuxThreds相比,NPTL提供了与POSIX.1规范更近的一致性,当创建大量线程时也有更好的性能,NPTL从glibc 2.3.2以后都是可用的,并且需要在Linux 2.6 内核中提供的特性。
这两种实现被称之为1:1实现,意思是每个线程映射到内核调度实体,两种实现都是通过Linux clone(2)系统调用来实现,在NPTL,线程同步原语(mutexes,线程终止等待)使用futex(2)系统调用实现。
LinuxThreads
这种实现值得注意的特性如下:
- 除了主(初始)线程,使用pthread_create(3)创建的线程,在实现中创建了一个“管理者”线程。这个线程处理线程创建和终止。(如果这个线程被不经意地杀死后,可能会导致问题出现)
- 在实现的内部使用了信号。在Linux 2.2以后,使用了前三个实时信号,在更旧的Linux内核中,SIGUSR1和SIGUSR2被使用,应用程序要避免使用被线程实现使用的信号。
- 线程不共享进程IDs。(实际上,LinuxThreads线程实现为比常用进程共享更多信息的进程,但是不共享通用进程ID。)LinuxThreads(包括管理线程)在使用ps(1)是都能作为分离的进程被看到。
LinuxThreads实现在很多方面偏离了POSIX.1规范,包括如下:
- 调用getpid(2)返回每个线程的不同值。
- 在线程中而不是主线程中调用getppid(2)返回管理线程的进程ID;在这些线程中getppid(2)的返回值应该返回与主线程中getppid(2)相同的值。
- 当一个线程使用fork(2)创建一个子进程时,任何线程应该能能够在其子进程上wait(2)。然而,实现仅仅允许创建了子进程的那个线程执行wait(2)。
- 当一个线程调用execve(2),所有其它线程被终止(POSIX.1要求)。然而,最终的进程使用了与与调用execve(2)线程相同的PID,应该是与主线有相同的PID。
- 线程不共享用户和组IDs,如果应用通过使用seteuid(2)或其它类似函数改变了其相关内容,这就会对设置用户ID程序造成问题,在线程函数中会导致失败。
- 线程不共享公用的会话ID和进程组ID。
- 线程不共享使用fcntl(2)创建的记录锁。
- times(2)和getrusage(2)返回的信息是每个线程级别的而不是进程级别的。
- 线程不共享信号量撤销值(参考semop(2))。
- 线程不共享间隔定时器。
- 线程不共享公用优先级值。
- POSIX.1 区分指向整个进程的信号和单个线程的信号。根据POSIX.1,一个指向进程的信号(使用kill发送)应该被进程中单个选定的线程来处理。LinuxThreads不支持指向进程进程的信号:信号只能发送给特定的线程。
- 线程有不同的替换信号栈设置,一个新的线程的替换信号栈设置从创建了线程的线程中拷贝而来,所以线程初始时是共享替换信号栈的。(一个新的线程应该使用未定义的替换信号栈启动,如果两个线程在同一时刻处理信号使用共享的替换信号栈,不可预测的程序失败可能出现)。
NPTL
使用NPTL,在进程中的所有线程放在相同的线程组中;线程组中的所有成员共享相同的PID,NPTL不使用管理者线程,NPTL在内部使用前两个实时信号(参看signal(7));这些信号不能被应用程序使用。
NPTL至少还与POSIX.1有下面的不一致:
- 线程不共享公用的优先级值
一些其它的NPTL不一致仅仅出现在老版本内核中:
- 使用times(2)和getrusage(2)返回的信息是线程级的而不是进程范围内的(在内核2.6.9中修复)。
- 线程不共享资源限制(在内核2.6.10中修复)。
- 线程不共享间隔定时器(在内核2.6.12中修复)。
- 仅仅主线程被允许使用setsid(2)来开启一个新的会话(在内核2.6.16中修复)。
- 仅仅主线程被允许使用setpgid(2)使得一个进程成为进程组leader(在内核2.6.16中修复)。
- 线程拥有不同的替换信号栈设置,然而,一个新线程的替换信号栈设定是从创建它的线程中拷贝而来,所以初始时共享替换信号处理栈(在内核2.6.16中修复)。
注意下面的更多关于NPTL实现:
- 如果栈大小软资源限制(查看setrlimit(2)中RLIMIT_STACK中的描述)被设置成除了unlimited之外的值,这个值定义了新线程的默认栈大小。为了生效,这个限制必须在程序运行前设置,也许可以使用ulimit -s这个shell内置的命令。
确定线程实现
从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
egrep -i 'threads|nptl'
Native POSIX Threads Library by Ulrich Drepper et al
选择线程实现: LD_ASSUME_KERNEL
在支持LinuxThreads和NPTL(例如,glibc 2.3.x)的系统上,LD_ASSUME_KERNEL环境变量可以被用来重写动态链接器的线程实现的默认选项,这个变量指示动态链接器用来给出其运行在特殊内核版本之上。通过指定不支持NPTL的内核版本,可以强制使用LinuxThreads。(这样做常见的原因就是就是为了运行依赖于在LinuxThreads中一些不一致的特性(不工作)应用。)例如:
SEE ALSO
clone(2), futex(2), gettid(2), proc(5), futex(7), sigevent(7), signal(7), and various Pthreads manual pages, for example: 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)
COLOPHON
This page is part of release 3.35 of the Linux man-pages project. A description of the project, and information about reporting bugs, can be found at http://man7.org/linux/man-pages/.