news 2026/10/29 1:48:45

自学嵌入式第N+1天(Linux篇——线程)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自学嵌入式第N+1天(Linux篇——线程)

上一篇讲了进程,我们知道了进程的创建过程就是复制他的父进程,以此来提高他的并发。那么进程和线程有啥区别呢?

进程是重量级线程,资源分配的基本(最小)单位,更侧重成为一个资源分配的单位
线程轻量级进程,线程是CPU的基本调度单位,线程侧重于成为一个执行体 ,执行单位

进程组成 = pcb + [text|data|bss|堆|栈]
线程组成 = 线程ID + 程序计数器 + 一组相关寄存器 + 栈空间 (由于其他几个所占内存都非常小,栈空间比他们大很多,所以线程就是就约等于栈空间大小)

线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它与属于同一进程的其他线程共享代码段、数据段和其他操作系统资源,如打开文件和信号。一个传统重量级的进程只有单个控制线程如果进程有多个控制线程,那么它能同时做多个任务。

代码实现

1.线程创建

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

功能:创建一个线程

参数: thread为线程ID;attr为线程属性,NULL为默认属性也就是可结合属性还有可分离属性后面再讲;start_routine 线程可执行函数 ;arg 给线程执行函数的参数 。
void * start_thread (void *arg)
{
//完成线程执行功能的
}

参数:arg是由pthread_create最后一个参数传递过来的
返回值:void * 返回一个指针

返回值:成功 0,失败 返回error number不是之前的errno,这里需要将自己手动赋值即errno=返回值

注意:线程共用的是进程空间,资源共享,如果操作同一个资源,相互之间会影响
线程之间关系是对等,不存在进程间父子关系

查看线程的命令:ps -eLf

2.执行函数:就是创建线程的第三个参数

3.退出

(1)void pthread_exit(void *retval);
功能:结束调用线程
参数:retval表示线程要带出的值也就是状态值
注意:如果想要获得 该线程的 退出状态值,则需要另外的线程 通过pthread_join来获取
pthread_exit 传递的是退出状态值 对应的一块空间的地址
pthread_join 接受的是这个地址
注意:如果是在main线程中调用 pthread_exit(),此时,只是将主线程的执行结束了,但不是整个进程结束了。要等所有子线程都执行结束,进程的空间才会销毁。
(2)int pthread_join(pthread_t thread, void **retval);
功能:等待线程结束,并回收线程资源
参数:thread是要等待的线程的tid,retval是获取退出状态值的参数
返回值:成功 返回0,失败返回错误码。

(3)int pthread_cancel(pthread_t thread);

功能:向同一进程内的目标线程发送取消请求。只是发请求,不会立刻杀死线程;目标线程什么时候退出,由线程自身的取消属性决定(默认等到取消点(sleep/read/pthread_mutex_lock/printf 等阻塞函数)才响应)

参数:thread目标线程的pid号

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

下面用一个例子来演示上面几个函数,创建两个线程,一个线程复制文件前一半,一个线程复制后一半。

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <fcntl.h> #include <sys/stat.h> typedef struct { int fd_s; int fd_d; int size; }msg_t;//方便给执行函数传参 void * do_cp1(void *arg) { msg_t *p = arg; lseek(p->fd_s,0,SEEK_SET); lseek(p->fd_d,0,SEEK_SET); char buf[p->size/2]; int ret = read(p->fd_s,buf,sizeof(buf)); write(p->fd_d,buf,ret); return NULL; } //这里需要注意读写指针的位置,在文件编程的时候说过 void * do_cp2(void *arg) { msg_t *p = arg; lseek(p->fd_s,p->size/2,SEEK_SET); lseek(p->fd_d,p->size/2,SEEK_SET); char buf[p->size -p->size/2]; int ret = read(p->fd_s,buf,sizeof(buf)); write(p->fd_d,buf,ret); return NULL; } void cleanup(void *arg) { msg_t *p = arg; close(p->fd_s); close(p->fd_d); printf("---close---file\n"); } int main(int argc, const char *argv[]) { int fd_s = open(argv[1],O_RDONLY); int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666); if (fd_s < 0 || fd_d < 0) { perror("open fail"); return -1; } struct stat st; if (stat(argv[1],&st) < 0) { perror("stat fail"); return -1; } msg_t m; m.size = st.st_size; m.fd_s = fd_s; m.fd_d = fd_d; pthread_t tid[2]; int ret = pthread_create(&tid[0],NULL,do_cp1,&m); if (ret != 0) { perror("pthread_create fail"); return -1; } ret = pthread_create(&tid[1],NULL,do_cp2,&m); if (ret != 0) { perror("pthread_create fail"); return -1; } pthread_cleanup_push(cleanup,&m); pthread_join(tid[0],NULL); pthread_join(tid[1],NULL); pthread_exit(NULL); pthread_cleanup_pop(0); return 0; }

(4)退出清理函数

a.void pthread_cleanup_push(void (*routine)(void *), void *arg);

功能:注册一个线程清理函数
参数:routine是线程清理函数的入口,arg是清理函数的参数。
b.void pthread_cleanup_pop(int execute);
功能:调用清理函数
参数:execute为非0 执行清理函数,为0不执行清理,只有在push 之后、pop 之前,线程执行pthread_exit() ;被pthread_cancel()取消且走到取消点,才会执行cleanup函数

(5)设置线程属性

设置分离属性,目的是让线程消亡,自动回收空间。

a.int pthread_attr_init(pthread_attr_t *attr);
功能:初始化一个attr的变量
参数:attr,需要变量来接受初始值
返回:成功返回0,错误返回非0 ;

b.int pthread_attr_destroy(pthread_attr_t *attr);
功能:销毁attr变量。
参数:attr,属性变量
返回:成功返回,错误返回非0 ;

c.int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:把一个线程设置成相应的属性
参数:attr,属性变量,有init函数初始化他;
detachstate:有2个可选值,

PTHREAD_CREATE_DETACHED 使用该属性创建的线程,为分离态。

PTHREAD_CREATE_JOINABLE 使用该属性创建的线程,为可汇合态。

返回值:成功返回0,失败返回非0;

d.int pthread_detach(pthread_t thread);
功能,设置分离属性
参数,线程id号,填自己的id

返回值:成功返回0,失败返回非0;

这里是两个设置分离属性的方法,具体区别如下

对比项

pthread_attr_setdetachstate

pthread_detach

调用时机

pthread_create 之前设置 attr 属性

pthread_create 之后调用

作用对象

还未创建的线程(模板属性)

已经存在、正在运行的线程

使用流程

①attr_init → ②setdetachstate → ③pthread_create(attr) → ④attr_destroy

①pthread_create → ②传入 tid 调用 pthread_detach (tid)

能不能设置 JOINABLE

两种都能设置(分离 / 可汇合)

只能设置成分离态,不能改回可汇合

典型场景

批量创建一批线程,统一全部设为分离态

线程运行中途动态设置分离;线程内部pthread_detach(pthread_self())

限制

只对本次用这个 attr 创建的线程生效

一旦 detach 之后,不能再 pthread_join 这个线程

(6)子线程的回收

注意:如果预估子线程可以有限范围内结束则正常用pthread_join等待回收。如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收。如果子线程已知必须长时间运行则,不再回收其资源。就设置为分离属性。

a.pid_t wait (int *wstatus)

头文件:#include<sys/types.h> #include<sys/wait.h>

功能:等待子进程状态发生改变(子进程结束,子进程被暂停,子进程被恢复),可以阻塞等待任意子进程退出,并回收该进程的资源和获取退出状态值。用于父进程回收子进程。

参数:传一个int变量地址,内核会把子进程退出信息填到这个变量里。

WIFEXITED(status):子进程是否正常退出

WEXITSTATUS(status):拿到子进程 main return /exit () 返回值

WIFSIGNALED(status):判断是否是信号结束

WTERMSTG(status):是哪个信号结束

返回值:成功返回回收子进程的pid号,失败返回-1;

当不关心子进程状态是可以用wait(NULL)

b. pid_t waitpid(pid_t pid, int *wstatus, int options);

功能:和wait一样,但是可以选择是否阻塞等待。

参数:wstatus和wait一样

pid 值含义
pid > 0只等待PID 等于该值的那一个子进程
pid = 0等待同进程组的任意子进程
pid = -1等待任意一个子进程,等价于wait()
pid < -1等待指定进程组内任意子进程(取绝对值为进程组 ID)

options为0时是阻塞等待,为WNOHANG是非阻塞等待

返回值:成功时返回回收子进程的pid号,失败时返回-1;非阻塞时返回0表示没有任何子进程状态改变。

waitpid(-1,status,0)等价于wait(status)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 1:48:38

F3D:把「查看 3D 文件」压到一条命令的轻量 3D 查看器

F3D&#xff1a;把「查看 3D 文件」压到一条命令的轻量 3D 查看器 【免费下载链接】f3d Fast and minimalist 3D viewer. 项目地址: https://gitcode.com/GitHub_Trending/f3/f3d F3D 是一个在终端里用一条命令打开 3D 文件的命令行 3D 查看器&#xff0c;面向需要快速检…

作者头像 李华
网站建设 2026/10/29 1:46:18

Neo4j导入DBpedia全指南:RDF转CSV的映射逻辑与批量导入实战

简介&#xff1a;面向需要将 DBpedia.org 知识图谱数据导入 Neo4j 的开发者与数据工程师&#xff0c;该项目使用 Scala 编写 Spark 应用&#xff0c;可处理 DBpedia 的 RDF 平面文件&#xff08;如 Wikipedia 链接映射&#xff09;&#xff0c;转换为 CSV 后生成 Neo4j 数据存储…

作者头像 李华
网站建设 2026/10/29 1:45:17

ECDICT:150万词汇量开源词典数据库的实战指南

ECDICT&#xff1a;150万词汇量开源词典数据库的实战指南 在当今数字化学习时代&#xff0c;如何快速、准确地获取高质量的词典数据是每个开发者和语言学习者面临的共同挑战。ECDICT作为一款拥有超过150万词汇量的开源中英文词典数据库&#xff0c;以其毫秒级查询响应、完整的词…

作者头像 李华
网站建设 2026/10/29 1:42:33

ponytail插件怎么用?多源数据聚合与批量操作实战指南

1. 从“ponytail”这个热词说起&#xff1a;它到底指什么第一次看到“ponytail”被当成技术关键词搜索&#xff0c;我其实愣了一下。这个词本意是马尾辫&#xff0c;但在最近的技术社区和工具圈里&#xff0c;它已经变成了一个特定功能的代称——把零散、分散、多来源的信息或任…

作者头像 李华
网站建设 2026/10/29 1:42:26

Kled V3:面向中小团队的AI训练数据众包采集操作系统

1. 项目概述&#xff1a;这不是一个工具&#xff0c;而是一套可复用的数据采集操作系统“Kled V3&#xff1a;72小时众包采集AI训练数据”——光看标题&#xff0c;很多人第一反应是“又一个开源爬虫项目&#xff1f;”或者“是不是某个新出的标注平台&#xff1f;”但实话讲&a…

作者头像 李华