pthread_create(3) Linux 手册页
一、pthread_create(3)Linux 手册页man7.org Linux man-pagesLinux/UNIX 系统编程培训pthread_create(3) — Linux 手册页名称 (NAME) | 库 (LIBRARY) | 概要 (SYNOPSIS) | 描述 (DESCRIPTION) | 返回值 (RETURN VALUE) | 错误 (ERRORS) | 属性 (ATTRIBUTES) | 标准 (STANDARDS) | 历史 (HISTORY) | 注解 (NOTES) | 缺陷 (BUGS) | 示例 (EXAMPLES) | 参见 (SEE ALSO) | 跋 (COLOPHON)名称 (NAME)pthread_create- 创建一个新线程库 (LIBRARY)POSIX 线程库 (libpthread, -lpthread)概要 (SYNOPSIS)#includepthread.hintpthread_create(pthread_t*restrict thread,constpthread_attr_t*_Nullable restrict attr,typeof(void*(void*_Nullable))*start_routine,void*_Nullable restrict arg);描述 (DESCRIPTION)pthread_create()函数在调用它的进程中启动一个新线程。新线程通过调用start_routine()函数开始执行arg作为唯一参数传递给start_routine()。新线程将以下列方式之一终止它调用pthread_exit(3)并指定一个退出状态值。同一进程中调用pthread_join(3)的另一个线程可以获取此值。它从start_routine()中返回。这等同于使用return语句中提供的值来调用pthread_exit(3)。它被取消参见pthread_cancel(3)。进程中的任何线程调用了exit(3)或者主线程从main()中返回。这会导致进程中的所有线程终止。attr参数指向一个pthread_attr_t结构体该结构体的内容在创建线程时用于决定新线程的属性此结构体使用pthread_attr_init(3)及相关函数进行初始化。如果attr为 NULL则使用默认属性创建线程。在成功返回之前pthread_create()会将新线程的 ID 存储在由thread指向的缓冲区中此标识符在后续调用其他 pthreads 函数时用于指代该线程。新线程会继承创建它的线程的信号掩码副本 (pthread_sigmask(3))。新线程的挂起信号集为空 (sigpending(2))。新线程不会继承创建者的备用信号栈 (sigaltstack(2))。新线程会继承调用线程的浮点环境 (fenv(3))。新线程的 CPU 时间时钟初始值为 0参见pthread_getcpuclockid(3)。Linux 专有细节新线程继承调用线程的权能集合capability sets参见capabilities(7)和 CPU 亲和力掩码CPU affinity mask参见sched_setaffinity(2)的副本。返回值 (RETURN VALUE)成功时pthread_create()返回 0出错时它返回一个错误编号error number并且*thread的内容处于未定义状态。错误 (ERRORS)EAGAIN系统资源不足无法创建另一个线程。EAGAIN遇到了系统对线程数量施加的限制。可能触发此错误的限制包括达到了由setrlimit(2)设置的实际用户 ID 所能拥有的进程和线程数的软限制 (RLIMIT_NPROC)达到了内核系统级最大进程和线程数限制/proc/sys/kernel/threads-max参见proc(5)或达到了 PID 的最大数量限制/proc/sys/kernel/pid_max参见proc(5)。EINVALattr中的设置无效。EPERM没有权限设置attr中指定的调度策略和参数。属性 (ATTRIBUTES)有关本节中使用的术语解释请参见attributes(7)。接口属性值pthread_create()线程安全性 (Thread safety)MT-Safe (多线程安全)标准 (STANDARDS)POSIX.1-2008.历史 (HISTORY)POSIX.1-2001.注解 (NOTES)有关pthread_create()返回在*thread中的线程 ID 的进一步信息请参见pthread_self(3)。除非使用了实时调度策略否则在调用pthread_create()之后接下来哪个线程是调用者还是新线程将被执行是不确定的。线程可以是可连接的 (joinable)或分离的 (detached)。如果一个线程是可连接的那么另一个线程可以调用pthread_join(3)来等待该线程终止并获取其退出状态。只有当已终止的可连接线程被连接 (joined) 后其最后剩下的系统资源才会被释放回系统。当一个分离的线程终止时其资源会自动释放回系统无法连接分离的线程来获取其退出状态。将线程设为分离状态对于某些后台守护 (daemon) 线程非常有用因为应用程序不需要关心它们的退出状态。默认情况下新线程是在可连接状态下创建的除非attr被设置为在分离状态下创建线程使用pthread_attr_setdetachstate(3)。在 NPTL 线程实现下如果在程序启动时RLIMIT_STACK软资源限制的任何值非 “unlimited”无限制那么它将决定新线程的默认栈大小。通过使用pthread_attr_setstacksize(3)可以在用于创建线程的attr参数中显式设置栈大小属性以获得不同于默认值的栈大小。如果RLIMIT_STACK资源限制被设置为 “unlimited”则会使用特定于架构的值作为栈大小大多数架构为 2 MBPOWER 和 Sparc-64 架构为 4 MB。缺陷 (BUGS)在已废弃的 LinuxThreads 实现中同一个进程中的每个线程都有不同的进程 ID。这违反了 POSIX 线程规范也是该实现中许多其他不符合标准问题的根源参见pthreads(7)。示例 (EXAMPLES)下面的程序演示了pthread_create()以及 pthreads API 中一些其他函数的使用方法。在以下运行示例中在提供 NPTL 线程实现的系统上栈大小默认为 “stack size” (栈大小) 资源限制所给定的值$ulimit-s8192# 栈大小限制为 8 MB (0x800000 字节)$ ./a.out hola salut servus Thread1:topof stack near 0xb7dd03b8;argv_stringhola Thread2:topof stack near 0xb75cf3b8;argv_stringsalut Thread3:topof stack near 0xb6dce3b8;argv_stringservus Joined with thread1;returned value was HOLA Joined with thread2;returned value was SALUT Joined with thread3;returned value was SERVUS在下一次运行中程序显式地为创建的线程设置了 1 MB 的栈大小使用pthread_attr_setstacksize(3)$ ./a.out-s0x100000 hola salut servus Thread1:topof stack near 0xb7d723b8;argv_stringhola Thread2:topof stack near 0xb7c713b8;argv_stringsalut Thread3:topof stack near 0xb7b703b8;argv_stringservus Joined with thread1;returned value was HOLA Joined with thread2;returned value was SALUT Joined with thread3;returned value was SERVUS程序源码#includectype.h#includeerr.h#includepthread.h#includestdio.h#includestdlib.h#includestring.h#includesys/types.h#includeunistd.hstructthread_info{/* 用于作为 thread_start() 的参数 */pthread_tthread_id;/* 由 pthread_create() 返回的 ID */intthread_num;/* 应用程序定义的线程编号 */char*argv_string;/* 来源于命令行参数 */};/* 线程启动函数显示栈顶附近的地址 并返回 argv_string 的大写副本。 */staticvoid*thread_start(void*arg){structthread_info*tinfoarg;char*uargv;printf(Thread %d: top of stack near %p; argv_string%s\n,tinfo-thread_num,(void*)tinfo,tinfo-argv_string);uargvstrdup(tinfo-argv_string);if(uargvNULL)err(EXIT_FAILURE,strdup);for(char*puargv;*p!\0;p)*ptoupper(*p);returnuargv;}intmain(intargc,char*argv[]){ints,opt;void*res;size_tnum_threads;ssize_tstack_size;pthread_attr_tattr;structthread_info*tinfo;/* -s 选项为我们的线程指定栈大小。 */stack_size-1;while((optgetopt(argc,argv,s:))!-1){switch(opt){cases:stack_sizestrtoul(optarg,NULL,0);break;default:fprintf(stderr,Usage: %s [-s stack-size] arg...\n,argv[0]);exit(EXIT_FAILURE);}}num_threadsargc-optind;/* 初始化线程创建属性。 */spthread_attr_init(attr);if(s!0)errc(EXIT_FAILURE,s,pthread_attr_init);if(stack_size0){spthread_attr_setstacksize(attr,stack_size);if(s!0)errc(EXIT_FAILURE,s,pthread_attr_setstacksize);}/* 为 pthread_create() 的参数分配内存。 */tinfocalloc(num_threads,sizeof(*tinfo));if(tinfoNULL)err(EXIT_FAILURE,calloc);/* 为每个命令行参数创建一个线程。 */for(size_ttnum0;tnumnum_threads;tnum){tinfo[tnum].thread_numtnum1;tinfo[tnum].argv_stringargv[optindtnum];/* pthread_create() 调用将线程 ID 存储到对应的 tinfo[] 元素中。 */spthread_create(tinfo[tnum].thread_id,attr,thread_start,tinfo[tnum]);if(s!0)errc(EXIT_FAILURE,s,pthread_create);}/* 销毁线程属性对象因为它不再需要了。 */spthread_attr_destroy(attr);if(s!0)errc(EXIT_FAILURE,s,pthread_attr_destroy);/* 现在连接 (join) 每个线程并显示其返回值。 */for(size_ttnum0;tnumnum_threads;tnum){spthread_join(tinfo[tnum].thread_id,res);if(s!0)errc(EXIT_FAILURE,s,pthread_join);printf(Joined with thread %d; returned value was %s\n,tinfo[tnum].thread_num,(char*)res);free(res);/* 释放由线程分配的内存 */}free(tinfo);exit(EXIT_SUCCESS);}参见 (SEE ALSO)getrlimit(2),pthread_attr_init(3),pthread_cancel(3),pthread_detach(3),pthread_equal(3),pthread_exit(3),pthread_getattr_np(3),pthread_join(3),pthread_self(3),pthread_setattr_default_np(3),pthreads(7)跋 (COLOPHON)本页面是 man-pagesLinux 内核与 C 库用户空间接口文档项目的一部分。关于该项目的信息可以前往 ⟨https://www.kernel.org/doc/man-pages/⟩ 了解。如果您发现本手册页有漏洞或错误请参阅 ⟨https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING⟩。此页面是从 man-pages-6.18.tar.gz 压缩包中获取的该包于 2026-05-24 获取自 ⟨https://mirrors.edge.kernel.org/pub/linux/docs/man-pages/⟩。如果您在此 HTML 版本的页面中发现任何渲染问题或者您认为有更好、更新的页面源码又或者您想要对本跋此部分不属于原始手册页中的信息提供更正或改进请发送邮件至 man-pagesman7.org。Linux man-pages 6.18 | 2026-02-10 | pthread_create(3)引用了此页面的其他页面mmap(2),spu_run(2),wait(2),pthread_attr_init(3),pthread_attr_setdetachstate(3),pthread_attr_setguardsize(3),pthread_attr_setinheritsched(3),pthread_attr_setschedparam(3),pthread_attr_setschedpolicy(3),pthread_attr_setscope(3),pthread_attr_setstack(3),pthread_attr_setstackaddr(3),pthread_attr_setstacksize(3),pthread_cancel(3),pthread_detach(3),pthread_equal(3),pthread_exit(3),pthread_getattr_default_np(3),pthread_getattr_np(3),pthread_join(3),pthread_key_create(3),pthread_self(3),pthread_setaffinity_np(3),pthread_setname_np(3),pthread_setschedparam(3),pthread_setschedprio(3),pthread_sigmask(3),pthreads(7)HTML 渲染由 Michael Kerrisk 于 2026-05-30 创建《The Linux Programming Interface》的作者。二、上面示例程序的介绍解释这段 C 多线程程序。这段程序是一个POSIX 多线程pthread示例展示了如何创建多个线程、传递参数并收集返回值。我先给你一张整体流程图再逐部分解释。 点击图中任意节点可深入追问某一步。整体目标这段程序的功能很简单为每个命令行参数启动一个线程每个线程把对应参数转成大写并返回主线程最后打印所有结果。各部分详解1. 数据结构thread_infostructthread_info{pthread_tthread_id;// pthread_create() 返回的线程句柄intthread_num;// 可读的线程编号1, 2, 3...char*argv_string;// 指向命令行参数的指针};这是「线程参数包」用于向pthread_create()传递多个数据。pthread_create只接受一个void *参数所以把所有信息打包成结构体是标准做法。2. 线程函数thread_startstaticvoid*thread_start(void*arg)每个线程运行的函数做三件事打印栈地址tinfo是局部变量的地址近似反映这个线程的栈顶位置可用来观察不同线程栈分布在内存哪里。复制字符串用strdup()分配新内存堆上避免修改原始参数。转大写遍历字符并调用toupper()最后return uargv——这个指针就是线程的返回值由pthread_join取回。3.main中的参数解析while((optgetopt(argc,argv,s:))!-1){...}getopt处理-s 数字选项允许用户自定义线程栈大小。optind之后的参数都是要处理的字符串。4. 线程属性设置pthread_attr_init(attr);pthread_attr_setstacksize(attr,stack_size);// 可选pthread_attr_t是一个配置对象可设置栈大小、调度策略等。创建完所有线程后就pthread_attr_destroy了——因为属性只在创建时用到之后不再需要。5. 创建线程pthread_create(tinfo[tnum].thread_id,attr,thread_start,tinfo[tnum]);四个参数依次是参数含义tinfo[tnum].thread_id输出存储新线程的 IDattr线程属性栈大小等thread_start线程入口函数tinfo[tnum]传给入口函数的参数6. 等待线程并收集结果pthread_join(tinfo[tnum].thread_id,res);printf(... %s\n,(char*)res);free(res);pthread_join有两个作用阻塞等待主线程暂停直到指定线程结束。取回返回值res接收线程return的指针——即线程里strdup分配的大写字符串。注意这块内存是线程分配的在堆上主线程取回后必须free否则内存泄漏。核心要点总结概念说明pthread_create创建线程传参通过void *thread_start返回void *线程可以返回值本质是个指针pthread_join同步线程 获取返回值strdup/free线程在堆上分配内存主线程负责释放pthread_attr_t可选配置如自定义栈大小三、getopt函数先看代码本身再配两张图帮你理解。代码回顾stack_size-1;while((optgetopt(argc,argv,s:))!-1){switch(opt){cases:stack_sizestrtoul(optarg,NULL,0);break;default:fprintf(stderr,Usage: %s [-s stack-size] arg...\n,argv[0]);exit(EXIT_FAILURE);}}num_threadsargc-optind;getopt是什么getopt是 POSIX 标准库函数专门用来解析 Unix 风格的命令行选项-x、-s 4096这类。它每次调用只处理一个选项所以要放在while循环里反复调用直到返回-1表示没有更多选项了。函数签名intgetopt(intargc,char*constargv[],constchar*optstring);第三个参数s:是选项字符串含义是s— 程序接受-s这个选项:— 紧跟在s后面的冒号表示-s需要一个参数如果不带冒号则不需要参数每次getopt返回时返回识别到的选项字母如s把该选项的参数存入全局变量optarg遇到未知选项时返回?所有选项处理完毕时返回-1下图展示getopt每轮循环在argv数组里的扫描过程接下来看while循环每一轮的决策逻辑—各行代码的具体含义stack_size -1初始化为-1作为未设置的哨兵值后面用if (stack_size 0)来判断用户是否提供了-s。while ((opt getopt(argc, argv, s:)) ! -1)这是一个赋值 比较合并写法。getopt的返回值先赋给opt同一表达式里再与-1比较。等价于optgetopt(argc,argv,s:);while(opt!-1){...optgetopt(argc,argv,s:);}case s: stack_size strtoul(optarg, NULL, 0)optarg是getopt.h提供的全局指针自动指向当前选项的参数字符串这里是4096这样的字符串。strtoul把它转换成无符号长整型第三个参数0表示自动推断进制0x开头十六进制0开头八进制其他十进制。default: fprintf(stderr, ...); exit(EXIT_FAILURE)getopt遇到选项字符串里没有的选项字母时返回?由default捕获向标准错误输出使用说明然后退出。num_threads argc - optindoptind是另一个全局变量getopt处理完所有选项后它指向argv中第一个非选项参数的下标。argc - optind就是剩余参数的个数也就是要创建的线程数。一句话总结getopt这段代码做的事就是把-s 4096 hello world这样的命令行拆成两部分——配置项-s 4096和数据hello world配置项交给stack_size保存数据个数交给num_threads保存之后用argv[optind tnum]逐个访问每个数据参数。