Linux系统应用编程---线程原语

简介: Linux系统应用编程---线程原语

线程概念

什么是线程

LWP: light wight process,轻量级进程,本质还是进程(在Linux环境下)

进程:独立地址空间,拥有PCB

线程:也有PCB,但没有独立的地址空间(共享)

区别:在于是否共享地址空间

Linux下:     进程:最小的执行单位

                    线程:最小分配资源单元,可看成是只有一个进程的线程

Linux线程实现原理

1、轻量级线程也有PCB,创建线程使用的底层函数和进程一样,都是clone

2、从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表示相同的,

3、进程可以蜕变成线程

4、线程可看做寄存器和栈的结合

5、在Linux下,线程是最小的执行单位,进程是最小的资源分配单位。

查看LWP号

ps -lf pid              查看指定进程的lwp号

ps –aux          看进程

ps –eLf          看线程

ps –Lw pid     查看某一个进程的线程有那些

线程共享资源

1、文件描述符表

2、每种信号的处理方式

3、当前工作目录

4、用户ID和组ID

5、内存地址空间(Text/data/bss/堆/共享库)

 

线程非共享资源

1.线程id

2.处理器现场和栈指针(内核栈)

3.独立的栈空间(用户空间栈)

4.errno变量

5.信号屏蔽字

6.调度优先级

 

线程优缺点

优点:

提高程序的并发性

开销小,不用重新分配内存

通信和共享数据方便

缺点:

线程不稳定(库函数实现)

线程调试比较困难(gdb支持不好)

线程无法使用unix经典事件,例如信号

 

线程原语

1、pthread_create

1. #include <pthread.h>
2. 
3. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
4. 
5. 
6. pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)
7. 
8. const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL
9. 
10. void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块
11. 
12. void *arg:指定线程将要加载调用的那个函数的参数

返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。

 

2、pthread_self

1. #include <pthread.h>
2. 
3. pthread_t pthread_self(void);

获取调用线程tid,其作用对应进程中getpid()函数。

线程ID:pthread_t类型,其本质是在Linux下午无符号整数,其他系统中可能是结构体实现

线程ID是进程内部识别标志。(两个进程间,线程ID允许相同)

注意:不应该使用全局变量pthread_t tid,在子进程中通过pthread_create传出参数来获取线程ID,而应该使用pthread_self()

 

线程ID与线程号的区别:

Ps –eLf命令看到的LWP那一列是线程号,线程号是CPU分配时间轮片的依据

Pthread_self得到的是线程ID,线程ID用来在进程内部区分线程

 

程序1:循坏创建多个子线程

1. #include <stdio.h>
2. #include <string.h>
3. #include <unistd.h>
4. #include <stdlib.h>
5. #include <pthread.h>
6. 
7. void *thread_func(void *arg)
8. {
9.  int i = (int )arg;
10.   sleep(i);
11.   printf("%dth thread: thread id = %lu, pid = %u\n", i+1, pthread_self(), getpid());
12. 
13.   return NULL;
14. }
15. 
16. int  main(void)
17. {
18.   pthread_t tid;
19.   int ret, i;
20. 
21.   for(i=0; i<5; i++){
22.     ret = pthread_create(&tid, NULL, thread_func, (void *)i);
23.     if(ret != 0){
24.       fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
25.       exit(1);
26.     }
27.   }
28. 
29.   //sleep(i);
30. 
31.   //return 0;     //将当前进程退出
32.   pthread_exit(NULL);
33. 
34. }

执行结果如下:

线程默认共享数据段、代码段等地址空间,常用的是全局变量,而进程不共享全局变量,只能借助mmap.

 

程序2:验证线程之间共享全局数据

1. #include <stdio.h>
2. #include <string.h>
3. #include <unistd.h>
4. #include <stdlib.h>
5. #include <pthread.h>
6. 
7. int var = 100;
8. 
9. void *tfn(void *arg)
10. {
11.   var = 200;
12.   printf("thread.\n");
13. 
14.   return NULL;
15. }
16. 
17. int  main(void)
18. {
19.   printf("At first var = %d.\n", var);
20. 
21.   pthread_t tid;
22.   pthread_create(&tid, NULL, tfn, NULL);
23.   sleep(1);
24. 
25.   printf("after pthread_create, var = %d.\n", var);
26. 
27.   return 0;     //将当前进程退出
28. 
29. }

执行结果如下:

3、pthread_exit

1. #include <pthread.h>
2. 
3. void pthread_exit(void *retval);

void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。一般传NULL。

pthread_exit与exit的区别:

调用线程退出函数pthread_exit,只是推出当前线程

任何线程里使用exit都会导致进程退出,主控线程退出时不能return或exit,因为会影响其他线程未工作。

 

exit与_exit的区别:

exit关闭C标准文件流,并刷新文件缓冲区

_exit(Linux底层函数),导致进程退出,关闭未关闭的文件描述符

 

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 

4、pthread_join

阻塞等待线程退出,获取线程退出状态,其作用对应进程中的waitpid()函数

1. #include <pthread.h>
2. 
3. int pthread_join(pthread_t thread, void **retval);

pthread_t thread:回收线程的tid

void **retval:接收退出线程传递出的返回值

返回值:成功返回0,失败返回错误号

5、pthread_cancel

在进程内的某个线程可以取消另一个线程

1. #include <pthread.h>
2. 
3. int pthread_cancel(pthread_t thread);

6、pthread_detach

1. #include <pthread.h>
2. 
3. int pthread_detach(pthread_t tid);

pthread_t tid:分离线程tid

返回值:成功返回0,失败返回错误号。

 

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

7、pthread_equal

比较两个线程是否相等

 

线程终止方式

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3.线程可以调用pthread_exit终止自己

目录
打赏
0
0
0
0
20
分享
相关文章
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
67 27
|
8天前
|
Linux系统ext4磁盘扩容实践指南
这个过程就像是给你的房子建一个新的储物间。你需要先找到空地(创建新的分区),然后建造储物间(格式化为ext4文件系统),最后将储物间添加到你的房子中(将新的分区添加到文件系统中)。完成这些步骤后,你就有了一个更大的储物空间。
57 10
|
10天前
|
微服务2——MongoDB单机部署4——Linux系统中的安装启动和连接
本节主要介绍了在Linux系统中安装、启动和连接MongoDB的详细步骤。首先从官网下载MongoDB压缩包并解压至指定目录,接着创建数据和日志存储目录,并配置`mongod.conf`文件以设定日志路径、数据存储路径及绑定IP等参数。之后通过配置文件启动MongoDB服务,并使用`mongo`命令或Compass工具进行连接测试。此外,还提供了防火墙配置建议以及服务停止的两种方法:快速关闭(直接杀死进程)和标准关闭(通过客户端命令安全关闭)。最后补充了数据损坏时的修复操作,确保数据库的稳定运行。
41 0
|
2天前
|
linux命令详细说明以及案例
本文介绍了常用的 Linux 命令及其详细说明和示例,包括:`ls`(列出目录内容)、`cd`(更改目录)、`rm` 和 `mv`(删除与移动文件)、`grep`(搜索文本)、`cat`(显示文件内容)以及 `chmod`(更改文件权限)。每个命令均配有功能描述、选项说明及实际案例,帮助用户更好地掌握 Linux 命令的使用方法。
82 56
|
4天前
|
Linux基础:文件和目录类命令分析。
总的来说,这些基础命令,像是Linux中藏匿的小矮人,每一次我们使用他们,他们就把我们的指令准确的传递给Linux,让我们的指令变为现实。所以,现在就开始你的Linux之旅,挥动你的命令之剑,探索这个充满神秘而又奇妙的世界吧!
49 19
|
19天前
|
Linux 常用文件查看命令
`cat` 命令用于连接文件并打印到标准输出,适用于快速查看和合并文本文件内容。常用示例包括:`cat file1.txt` 查看单个文件,`cat file1.txt file2.txt` 合并多个文件,`cat &gt; filename` 创建新文件,`cat &gt;&gt; filename` 追加内容。`more` 和 `less` 命令用于分页查看文件,`tail` 命令则用于查看文件末尾内容,支持实时追踪日志更新,如 `tail -f file.log`。
45 5
Linux 常用文件查看命令
|
2天前
|
linux常用命令详细说明以及案例
本文介绍了Linux中几个常用的命令及其用法,包括:`ls`(列出目录内容)、`cd`(切换目录)、`mkdir`(创建目录)、`rm -p`(删除目录及内容)和`mv`(移动或重命名文件/目录)。每个命令都配有详细说明、语法格式、常见选项及实用案例,帮助用户更好地理解和使用这些基础命令。内容源自[linux常用命令详细说明以及案例](https://linux.ciilii.com/show/news-285.html)。
|
1月前
|
Linux系统之whereis命令的基本使用
Linux系统之whereis命令的基本使用
103 24
Linux系统之whereis命令的基本使用
|
25天前
|
Linux od命令
本文详细介绍了Linux中的 `od`命令,包括其基本语法、常用选项和示例。通过这些内容,你可以灵活地使用 `od`命令查看文件内容,提高分析和调试效率。确保理解每一个选项和示例的实现细节,应用到实际工作中时能有效地处理各种文件查看需求。
55 19
Linux中yum、rpm、apt-get、wget的区别,yum、rpm、apt-get常用命令,CentOS、Ubuntu中安装wget
通过本文,我们详细了解了 `yum`、`rpm`、`apt-get`和 `wget`的区别、常用命令以及在CentOS和Ubuntu中安装 `wget`的方法。`yum`和 `apt-get`是高层次的包管理器,分别用于RPM系和Debian系发行版,能够自动解决依赖问题;而 `rpm`是低层次的包管理工具,适合处理单个包;`wget`则是一个功能强大的下载工具,适用于各种下载任务。在实际使用中,根据系统类型和任务需求选择合适的工具,可以大大提高工作效率和系统管理的便利性。
164 25

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等