线程是操作系统进程调度器可调度的最小粒度的执行单元

执行ps -eLF查看线程

UID          PID    PPID     LWP  C NLWP    SZ   RSS PSR STIME TTY          TIME CMD
root 103724 103680 103724 0 14 23667 40048 1 Jan24 ? 00:00:13 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103725 0 14 23667 40048 1 Jan24 ? 00:00:01 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103726 0 14 23667 40048 0 Jan24 ? 00:00:28 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103727 0 14 23667 40048 0 Jan24 ? 00:00:30 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103728 0 14 23667 40048 3 Jan24 ? 00:00:29 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103729 0 14 23667 40048 3 Jan24 ? 00:00:27 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103730 0 14 23667 40048 3 Jan24 ? 00:00:14 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103731 0 14 23667 40048 3 Jan24 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103732 0 14 23667 40048 3 Jan24 ? 00:00:14 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103814 0 14 23667 40048 2 Jan24 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103815 0 14 23667 40048 0 Jan24 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103816 0 14 23667 40048 2 Jan24 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 103817 0 14 23667 40048 2 Jan24 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools
root 103724 103680 403265 0 14 23667 40048 1 10:14 ? 00:00:00 /root/.vscode-server/extensions/ms-vscode.cpptools-1.18.5-linux-arm64/bin/cpptools

通过输出看到是cpptools的进程 103724,它拥有多个线程,LWP就是线程ID,第一行的LWP 103724 == PID,表明是该线程组的主线程。NLWP表示该线程组有多少个线程(14个)。

  • LWP,Light Weighted Process的缩写,Linux的线程实现是NPTL(Native Posix Thread Library),该模型下的线程被称为LWP,每一个用户态线程对应内核中的一个调度实体,拥有自己的task_struct。

空间布局

线程之间共享一份全局内存区域,包括初始化数据段、未初始化数据段(bss)、堆内存段。

Linux中通过pthread_create创建线程,glibc要为每个线程独立分配线程栈,线程栈位于mmap区(位于栈和堆的中间,从高地址向低地址延伸)。

API

  • 使用pthread_*API时,编译和链接时加-pthread选项

线程创建 pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
// thread:指向ptrhead_t结构体地址的指针,后续的API调用都通过该地址来操作 // void *arg:是start_routine函数的参数指针,如果需要传入多个参数,将多个参数放入结构体,将
// 结构体地址传入 // attr: 设置进程属性,调度策略等。man pthread_attr_init查看更多 // 返回值:成功返回0,否则返回错误状态码(不是<=-1的返回值)
// EAGAIN:超过进程能创建线程的限制
// EINVAL:attr值非法
// EPERM:没有权限设置attr的调度策略或参数

线程标识 pthread_self、pthread_equal

int pthread_equal(pthread_t t1, pthread_t t2);
// 相等返回非0,不相等返回0 pthread_t pthread_self(void);
// 返回调用线程的线程ID,上面说了NTPL实现中,这个ID是一个指向pthread_t结构体的指针

可以组合使用于特定的场景,比如:

  1. 主线程创建一个工作队列,再分配给线程池中的线程去处理工作,但是线程不可以自行从队列中争抢任务,由主线程将工作分配给特定的任务,主线程可以在每个任务中存储对应的线程ID
  2. 工作线程就可以通过这两个函数来确定是不是自己该处理的任务

线程退出

  1. 线程start_routine函数执行return 返回值;,例:return ((void *)1);,如果使用return,那么清理函数pthread_cleanup_push()和pthread_cleanup_pop()无效
  2. 线程start_routine函数执行pthread_exit()
  3. 其他线程通过pthread_cancel(pthread_t thread)取消线程,是一个请求,立即返回并不会等待指定线程退出,如果指定的thread可取消,那么其行为类似于thread调用了pthread_exit(),不建议使用
void pthread_exit(void *retval);

retval记录线程的退出信息,记录方式:

  1. 不可以使用线程局部变量,因为线程退出会释放栈,该变量会消失,等到其他线程用pthread_join()接收时已经不存在了。
  2. pthread_exit(NULL),仅退出,不返回信息
  3. 使用全局变量
  4. 将返回的信息定义在堆空间,malloc分配,堆空间不会随着线程退出被释放,使用完信息后切记释放该空间,否则memory leak
  5. 字符串常量

退出的清理工作

// 执行清理函数,清理函数保存在线程栈中,所以先注册的后执行
void pthread_cleanup_push(void (*routine)(void *), void *arg); // 移除清理函数
void pthread_cleanup_pop(int execute); // 他们总是成对出现

以下情况会触发pthread_cleanup_push调用routine:

  • 调用pthread_exit()
  • 响应其他线程的pthread_cancel()
  • 用pthread_cleanup_pop(int execute)主动执行,需要指定execute为非0

连接(释放)线程

join其他线程的时候会调用__nptl_free_tcb (pd);释放退出线程的资源。但是NPTL模型会缓存该线程的内存地址,并不会立即munmap,后创建的线程会复用这块内存地址,避免了频繁的mmap和munmap,所以:

  1. 使用pthread_join连接退出线程,后面启动的线程会复用前面joined栈内存空间。
  2. 如果不使用pthread_join连接线程,那么新的线程会分配新的栈空间,从而导致内存泄漏。
// 等待指定的线程的退出并接收它的返回值,如果等待的线程没有退出则阻塞。
int pthread_join(pthread_t thread, void **retval); // retval 存放线程返回值的地址 // 返回值:成功返回0
/* 失败返回错误码:
ESRCH:传入线程不存在
EINVAL:不是一个可join的线程,或者已经有其他线程在等待
EDEADLK:死锁,自己join自己或存在join环
*/

demo

创建2个线程,一个使用pthread_exit()退出,另一个使用return方式退出,使用pthread_join()接收他们的退出码,并使用pthread_cleanup_push()和pthread_cleanup_pop()做线程结束的清理工作

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFF_SIZE 1024
void clean_up(void *arg) { printf("cleanup: %s\n", (char *)arg); } void *test_func1(void *arg) {
pthread_t thread = pthread_self();
printf("thread1: %lu start\n", thread);
// 构造push handler函数信息
char s1[BUFF_SIZE];
char s2[BUFF_SIZE];
sprintf(s1, "thread1: %lu first handler", thread);
sprintf(s2, "thread1: %lu second handler", thread); pthread_cleanup_push(clean_up, s1);
pthread_cleanup_push(clean_up, s2);
// return方式退出
return ((void *)10);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
} void *test_func2(void *arg) {
pthread_t thread = pthread_self();
printf("thread2: %lu start\n", thread);
// 构造push handler函数信息
char s1[BUFF_SIZE];
char s2[BUFF_SIZE];
sprintf(s1, "thread2: %lu first handler", thread);
sprintf(s2, "thread2: %lu second handler", thread); pthread_cleanup_push(clean_up, s1);
pthread_cleanup_push(clean_up, s2);
// pthread_exit()退出
pthread_exit((void *)15);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
} int main() {
int err;
pthread_t tid1, tid2;
void *ret_info; // 创建线程1
err = pthread_create(&tid1, NULL, test_func1, (void *)NULL);
if (err != 0) {
printf("create thread failure\n");
}
// 后面省略错误判断。。。
pthread_create(&tid2, NULL, test_func2, (void *)NULL);
// 接收进程1的退出信息
pthread_join(tid1, &ret_info);
printf("thread %lu exit code %d\n", tid1, (int)ret_info);
// 接收进程2的退出信息
pthread_join(tid2, &ret_info);
printf("thread %lu exit code %d\n", tid1, (int)ret_info); return 0;
}

执行结果:

root@yielde:~/workspace/code-container/cpp/blog_demo# ./test
thread1: 281473444868384 start
thread2: 281473436414240 start
thread 281473444868384 exit code 10
cleanup: thread2: 281473436414240 second handler
cleanup: thread2: 281473436414240 first handler
thread 281473444868384 exit code 15

使用return退出的线程不会做清理工作,使用pthread_exit退出的线程会

线程分离

不可以同时detach又joinable哦~

通过pthread_join可以释放指定线程的资源,同时也可以获取退出线程的返回值。如果不关心其返回值,只是想让线程退出后由系统回收资源,有两种方法:

  1. 通过pthread_detach释放,可以线程自己调用,也可以通过其他线程调用
int pthread_detach(pthread_t thread);
// 返回值:成功返回0,失败返回错误编号,ESRCH表示无此线程、EINVAL表示线程不是joinable的
  1. 在创建线程时设置线程的属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
/*
int stat;
pthread_attr_getdetachstate(&attr, &stat);
if (stat == PTHREAD_CREATE_DETACHED) {
printf("pthread detached\n");
} else if (stat == PTHREAD_CREATE_JOINABLE) {
printf("pthread joinable\n");
}
*/

demo1 join退出线程

设置线程为joinable,通过pmap观察进程为线程分配的空间,验证我们上面连接(释放)线程的结论。

使用pthread_join回收线程后再启动新的线程,栈空间被复用

#include <pthread.h>
#include <stdio.h>
#include <unistd.h> void *start_thread(void *arg) {
printf("thread %d start\n", (int *)arg);
fflush(stdout);
sleep(10);
return NULL;
} int main() {
pthread_t thread;
int ret;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); //设置joinable
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
sleep(15);
ret = pthread_create(&thread, &attr, start_thread, (void *)1); // 创建线程1
pthread_join(thread, NULL); // 连接线程
sleep(10);
pthread_create(&thread, &attr, start_thread, (void *)2); // 线程1退出后,创建线程2
sleep(120);
return 0;
}

输出结果:

// 线程启动前
root@yielde:~/workspace/othergit# pmap 441486
441486: ./test
0000aaaacce40000 4K r-x-- test
0000aaaacce50000 4K r---- test
0000aaaacce51000 4K rw--- test
0000ffffb2830000 1568K r-x-- libc.so.6
0000ffffb29b8000 60K ----- libc.so.6
0000ffffb29c7000 16K r---- libc.so.6
0000ffffb29cb000 8K rw--- libc.so.6
0000ffffb29cd000 48K rw--- [ anon ]
0000ffffb29de000 172K r-x-- ld-linux-aarch64.so.1
0000ffffb2a13000 8K rw--- [ anon ]
0000ffffb2a15000 8K r---- [ anon ]
0000ffffb2a17000 4K r-x-- [ anon ]
0000ffffb2a18000 8K r---- ld-linux-aarch64.so.1
0000ffffb2a1a000 8K rw--- ld-linux-aarch64.so.1
0000ffffe582e000 132K rw--- [ stack ]
total 2052K // 线程1启动后
root@yielde:~/workspace/othergit# pmap 441486
441486: ./test
0000aaaacce40000 4K r-x-- test
0000aaaacce50000 4K r---- test
0000aaaacce51000 4K rw--- test
0000aaaad3dea000 132K rw--- [ anon ]
0000ffffac000000 132K rw--- [ anon ]
0000ffffac021000 65404K ----- [ anon ]
0000ffffb2020000 64K ----- [ anon ] // 线程1
0000ffffb2030000 8192K rw--- [ anon ] // 线程2
0000ffffb2830000 1568K r-x-- libc.so.6
0000ffffb29b8000 60K ----- libc.so.6
0000ffffb29c7000 16K r---- libc.so.6
0000ffffb29cb000 8K rw--- libc.so.6
0000ffffb29cd000 48K rw--- [ anon ]
0000ffffb29de000 172K r-x-- ld-linux-aarch64.so.1
0000ffffb2a13000 8K rw--- [ anon ]
0000ffffb2a15000 8K r---- [ anon ]
0000ffffb2a17000 4K r-x-- [ anon ]
0000ffffb2a18000 8K r---- ld-linux-aarch64.so.1
0000ffffb2a1a000 8K rw--- ld-linux-aarch64.so.1
0000ffffe582e000 132K rw--- [ stack ]
total 75976K // 线程1被join之后,启动线程2
root@yielde:~/workspace/othergit# pmap 441486
441486: ./test
0000aaaacce40000 4K r-x-- test
0000aaaacce50000 4K r---- test
0000aaaacce51000 4K rw--- test
0000aaaad3dea000 132K rw--- [ anon ]
0000ffffac000000 132K rw--- [ anon ]
0000ffffac021000 65404K ----- [ anon ]
0000ffffb2020000 64K ----- [ anon ] // 线程2,复用线程1的内存
0000ffffb2030000 8192K rw--- [ anon ] // 线程2,复用线程1的内存
0000ffffb2830000 1568K r-x-- libc.so.6
0000ffffb29b8000 60K ----- libc.so.6
0000ffffb29c7000 16K r---- libc.so.6
0000ffffb29cb000 8K rw--- libc.so.6
0000ffffb29cd000 48K rw--- [ anon ]
0000ffffb29de000 172K r-x-- ld-linux-aarch64.so.1
0000ffffb2a13000 8K rw--- [ anon ]
0000ffffb2a15000 8K r---- [ anon ]
0000ffffb2a17000 4K r-x-- [ anon ]
0000ffffb2a18000 8K r---- ld-linux-aarch64.so.1
0000ffffb2a1a000 8K rw--- ld-linux-aarch64.so.1
0000ffffe582e000 132K rw--- [ stack ]
total 75976K

demo2 不join退出线程

设置线程为joinable,通过pmap观察进程为线程分配的空间,验证我们上面连接(释放)线程的结论。

不使用pthread_join回收线程后再启动新的线程,栈空间不能被复用,内存泄漏!!!

#include <pthread.h>
#include <stdio.h>
#include <unistd.h> void *start_thread(void *arg) {
printf("thread %d start\n", (int *)arg);
fflush(stdout);
sleep(10);
return NULL;
} int main() {
pthread_t thread;
int ret;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
sleep(15);
ret = pthread_create(&thread, &attr, start_thread, (void *)1);
// pthread_join(thread, NULL);
sleep(10);
pthread_create(&thread, &attr, start_thread, (void *)2);
sleep(120);
return 0;
}

输出结果:

// 线程启动前
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441707
441707: ./test
0000aaaae39f0000 4K r-x-- test
0000aaaae3a00000 4K r---- test
0000aaaae3a01000 4K rw--- test
0000ffff9a0b0000 1568K r-x-- libc.so.6
0000ffff9a238000 60K ----- libc.so.6
0000ffff9a247000 16K r---- libc.so.6
0000ffff9a24b000 8K rw--- libc.so.6
0000ffff9a24d000 48K rw--- [ anon ]
0000ffff9a25f000 172K r-x-- ld-linux-aarch64.so.1
0000ffff9a294000 8K rw--- [ anon ]
0000ffff9a296000 8K r---- [ anon ]
0000ffff9a298000 4K r-x-- [ anon ]
0000ffff9a299000 8K r---- ld-linux-aarch64.so.1
0000ffff9a29b000 8K rw--- ld-linux-aarch64.so.1
0000ffffea091000 132K rw--- [ stack ]
total 2052K
// 启动线程1
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441707
441707: ./test
0000aaaae39f0000 4K r-x-- test
0000aaaae3a00000 4K r---- test
0000aaaae3a01000 4K rw--- test
0000aaaaefa43000 132K rw--- [ anon ]
0000ffff94000000 132K rw--- [ anon ]
0000ffff94021000 65404K ----- [ anon ]
0000ffff998a0000 64K ----- [ anon ] // 线程1
0000ffff998b0000 8192K rw--- [ anon ] // 线程1
0000ffff9a0b0000 1568K r-x-- libc.so.6
0000ffff9a238000 60K ----- libc.so.6
0000ffff9a247000 16K r---- libc.so.6
0000ffff9a24b000 8K rw--- libc.so.6
0000ffff9a24d000 48K rw--- [ anon ]
0000ffff9a25f000 172K r-x-- ld-linux-aarch64.so.1
0000ffff9a294000 8K rw--- [ anon ]
0000ffff9a296000 8K r---- [ anon ]
0000ffff9a298000 4K r-x-- [ anon ]
0000ffff9a299000 8K r---- ld-linux-aarch64.so.1
0000ffff9a29b000 8K rw--- ld-linux-aarch64.so.1
0000ffffea091000 132K rw--- [ stack ]
total 75976K
// 启动线程2
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441707
441707: ./test
0000aaaae39f0000 4K r-x-- test
0000aaaae3a00000 4K r---- test
0000aaaae3a01000 4K rw--- test
0000aaaaefa43000 132K rw--- [ anon ]
0000ffff94000000 132K rw--- [ anon ]
0000ffff94021000 65404K ----- [ anon ]
0000ffff99090000 64K ----- [ anon ] // 线程1退出,未被join,线程2启动分配新的内存
0000ffff990a0000 8192K rw--- [ anon ] // 线程1退出,未被join,线程2启动分配新的内存
0000ffff998a0000 64K ----- [ anon ] // 线程1
0000ffff998b0000 8192K rw--- [ anon ] // 线程1
0000ffff9a0b0000 1568K r-x-- libc.so.6
0000ffff9a238000 60K ----- libc.so.6
0000ffff9a247000 16K r---- libc.so.6
0000ffff9a24b000 8K rw--- libc.so.6
0000ffff9a24d000 48K rw--- [ anon ]
0000ffff9a25f000 172K r-x-- ld-linux-aarch64.so.1
0000ffff9a294000 8K rw--- [ anon ]
0000ffff9a296000 8K r---- [ anon ]
0000ffff9a298000 4K r-x-- [ anon ]
0000ffff9a299000 8K r---- ld-linux-aarch64.so.1
0000ffff9a29b000 8K rw--- ld-linux-aarch64.so.1
0000ffffea091000 132K rw--- [ stack ]
total 84232K

demo3 分离线程且不join

分离线程,并且不join,应当是内核释放线程资源,新的线程复用旧的线程内存

#include <pthread.h>
#include <stdio.h>
#include <unistd.h> void *start_thread(void *arg) {
printf("thread %d start\n", (int *)arg);
fflush(stdout);
sleep(10);
return NULL;
} int main() {
pthread_t thread;
int ret;
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 分离线程
sleep(15);
ret = pthread_create(&thread, &attr, start_thread, (void *)1);
// pthread_join(thread, NULL);
sleep(10);
pthread_create(&thread, &attr, start_thread, (void *)2);
sleep(120);
return 0;
}

运行结果:

// 启动线程前
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441847
441847: ./test
0000aaaac3640000 4K r-x-- test
0000aaaac3650000 4K r---- test
0000aaaac3651000 4K rw--- test
0000ffffbc680000 1568K r-x-- libc.so.6
0000ffffbc808000 60K ----- libc.so.6
0000ffffbc817000 16K r---- libc.so.6
0000ffffbc81b000 8K rw--- libc.so.6
0000ffffbc81d000 48K rw--- [ anon ]
0000ffffbc834000 172K r-x-- ld-linux-aarch64.so.1
0000ffffbc869000 8K rw--- [ anon ]
0000ffffbc86b000 8K r---- [ anon ]
0000ffffbc86d000 4K r-x-- [ anon ]
0000ffffbc86e000 8K r---- ld-linux-aarch64.so.1
0000ffffbc870000 8K rw--- ld-linux-aarch64.so.1
0000fffff3717000 132K rw--- [ stack ]
total 2052K
// 启动线程1
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441847
441847: ./test
0000aaaac3640000 4K r-x-- test
0000aaaac3650000 4K r---- test
0000aaaac3651000 4K rw--- test
0000aaaaedee5000 132K rw--- [ anon ]
0000ffffb4000000 132K rw--- [ anon ]
0000ffffb4021000 65404K ----- [ anon ]
0000ffffbbe70000 64K ----- [ anon ] // 线程1
0000ffffbbe80000 8192K rw--- [ anon ] // 线程1
0000ffffbc680000 1568K r-x-- libc.so.6
0000ffffbc808000 60K ----- libc.so.6
0000ffffbc817000 16K r---- libc.so.6
0000ffffbc81b000 8K rw--- libc.so.6
0000ffffbc81d000 48K rw--- [ anon ]
0000ffffbc834000 172K r-x-- ld-linux-aarch64.so.1
0000ffffbc869000 8K rw--- [ anon ]
0000ffffbc86b000 8K r---- [ anon ]
0000ffffbc86d000 4K r-x-- [ anon ]
0000ffffbc86e000 8K r---- ld-linux-aarch64.so.1
0000ffffbc870000 8K rw--- ld-linux-aarch64.so.1
0000fffff3717000 132K rw--- [ stack ]
total 75976K
// 启动线程2
root@yielde:~/workspace/code-container/cpp/blog_demo# pmap 441847
441847: ./test
0000aaaac3640000 4K r-x-- test
0000aaaac3650000 4K r---- test
0000aaaac3651000 4K rw--- test
0000aaaaedee5000 132K rw--- [ anon ]
0000ffffb4000000 132K rw--- [ anon ]
0000ffffb4021000 65404K ----- [ anon ]
0000ffffbbe70000 64K ----- [ anon ] // 线程1退出,启动线程2,内存复用
0000ffffbbe80000 8192K rw--- [ anon ] // 线程1退出,启动线程2,内存复用
0000ffffbc680000 1568K r-x-- libc.so.6
0000ffffbc808000 60K ----- libc.so.6
0000ffffbc817000 16K r---- libc.so.6
0000ffffbc81b000 8K rw--- libc.so.6
0000ffffbc81d000 48K rw--- [ anon ]
0000ffffbc834000 172K r-x-- ld-linux-aarch64.so.1
0000ffffbc869000 8K rw--- [ anon ]
0000ffffbc86b000 8K r---- [ anon ]
0000ffffbc86d000 4K r-x-- [ anon ]
0000ffffbc86e000 8K r---- ld-linux-aarch64.so.1
0000ffffbc870000 8K rw--- ld-linux-aarch64.so.1
0000fffff3717000 132K rw--- [ stack ]
total 75976K

总结

线程的优点

  1. 创建、终止线程比进程快
  2. 线程之间的上下文切换开销比进程小
  3. 线程间数据共享比进程间小

线程的缺点

  1. linux系统中,如果一个线程触发segment fault,那么内核会认为该进程有问题,为了防止进一步破坏内存空间,内核会将整个进程杀掉。
  2. 多线程的设计通常比较复杂,一方面线程的负载在很多场景下很难平衡,另一方面如果出现顺序依赖问题,设计不当会出现数据破坏,性能下降的问题。

进程与线程API联想

进程API 线程API 备注
fork pthread_create 创建新的
exit pthread_exit 退出
waitpid pthread_join 获取退出状态,释放指定ID资源
atexit pthread_cleanup_push 退出前做的清理操作
getpid pthread_self/syscall(SYS_gettid) 上面说过,pthread_self获取的是指向存放pthread_t内存的指针。如果要获得shell看到的线程ID号,使用syscall(SYS_gettid)
abort pthread_cancel 被其他人终止

学习自:

《UNIX环境高级编程》

《Linux环境编程从应用到内核》高峰 李彬 著

Linux线程API使用与分析的更多相关文章

  1. 面试问题之操作系统:linux线程API

    https://blog.csdn.net/youwotianya/article/details/80933449

  2. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  3. Linux 线程(进程)数限制分析

    1.问题来源公司线上环境出现MQ不能接受消息的异常,运维和开发人员临时切换另一台服务器的MQ后恢复.同时运维人员反馈在出现问题的服务器上很多基本的命令都不能运行,出现如下错误:2.   初步原因分析和 ...

  4. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  5. Linux 线程实现机制分析--转

    http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 一.基础知识:线程和进程 按照教科书上的定义,进程是资源管理的最小单位,线程是程 ...

  6. Linux 线程实现机制分析(转载)

    自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads ...

  7. Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  8. [转]Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    转载地址:https://www.cnblogs.com/MYSQLZOUQI/p/4233630.html 自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱 ...

  9. Linux线程互斥学习笔记--详细分析

    一.互斥锁 为啥要有互斥? 多个进程/线程执行的先后顺序不确定,何时切出CPU也不确定. 多个进程/线程访问变量的动作往往不是原子的. 1. 操作步骤 (1)创建锁 // 创建互斥锁mutex pth ...

  10. Linux线程的实现 & LinuxThread vs. NPTL & 用户级内核级线程 & 线程与信号处理

    另,线程的资源占用可见:http://www.cnblogs.com/charlesblc/p/6242111.html 进程 & 线程的很多知识可以看这里:http://www.cnblog ...

随机推荐

  1. 火山引擎DataLeap数据调度实例的 DAG 优化方案 (一):问题与需求分析

    DAG:全称为 Directed Acyclic Graph,指有向无环图,具备严密的拓扑性质,有很强的流程表达能力. DataLeap 是火山引擎自研的一站式大数据中台解决方案,集数据集成.开发.运 ...

  2. web应用模式 api接口 接口测试工具postman restful规范

    目录 web应用模式 前后端混合开发 流程说明(重要) 前后端分离开发 流程说明(重要) api接口 接口测试工具postman 基本介绍 编码格式 restful规范(重要) 简介 主要内容 练习 ...

  3. 用户 IP,里面藏了多少秘密?

    大家都知道,要邮寄一封信给正确的收件人,需要提供准确而精细的地址,这个地址需要从国家和城市精确到邮政编码,街道和门牌号码.只有这样,邮局的工作人员才能知道将信送到那里. Internet 上也是如此, ...

  4. leaflet 绘制 带箭头的线

    箭头不是画的线段,是贴的图标,再按方向旋转一下. 代码: //添加箭头线 function addLineDirection(polylinePointArr, source, target) { v ...

  5. SpringBoot 项目实战 | 瑞吉外卖 优化篇 Day01

    该系列将记录一份完整的实战项目的完成过程,该篇属于优化篇第一天,主要负责完成缓存优化问题 案例来自B站黑马程序员Java项目实战<瑞吉外卖>,请结合课程资料阅读以下内容 该篇我们将完成以下 ...

  6. 从运维域看 Serverless 真的就是万能银弹吗?

    作者说 ​ 在开始本篇内容前我想与各位开发者达成几个共识. ​ 第一个共识,软件工程没有银弹, Serverless 也不是银弹,它并不是解决所有问题的万能公式. 第二个共识,Serverless 能 ...

  7. Vue中如何使用sass实现换肤(更换主题)功能

    Vue中如何使用sass实现换肤(更换主题)功能 https://blog.csdn.net/m0_37792354/article/details/82012278

  8. vue中我改变了data中的一个值,但现在视图上没有实时更新,请问我怎么拿到更新后的值?

    Vue在数据初始化的时候会对data,computed,watcher中的属性进行依赖收集,如果支持proxy,则直接使用proxy进行拦截,好处是可以深层次的进行拦截,如果不支持proxy,则使用o ...

  9. 接口自动化复习第四天利用正则和faker提取替换变量值

    在做接口自动化测试的时候,我们经常会遇到,有些字段利用随机生成数据就行了,不需要自己去构造测试数据.今天我就是要python中的第三方库faker来构造随机数,其次使用正则表达式来提取变量. 首先在接 ...

  10. 使用 FormatMessage 格式化 Windows 错误码.md

    https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage #include <str ...