当前位置: 首页 > news >正文

[linux仓库]线程库封装[线程·肆]

🌟 各位看官好,我是egoist2023!

🌍 Linux == Linux is not Unix !

🚀 学习了线程的概念及操作之后,模仿C++对线程操作的封装自己造一个线程封装的轮子。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦!

目录

回顾与总结

线程封装

线程启动

线程等待

总结

附源码


回顾与总结

进程是承担分配资源的基本实体,本质:

  • 进程=内核数据结构 +代码和数据
  • 内核数据结构:不管是PCB,文件地址空间,文件描述符表,信号相关的话题,通信模块 ->都是数据结构对象 ->要占内存
  • 代码和数据:本质是ELF加载到内存,本身也是要占据内存.
  • 无论是进程是新建时还是运行后,无时无刻在占据资源 ->不管是写实拷贝还是缺页中断,都是要占内存的.
  • 一个进程在运行期间,也是要占据CPU调度资源,切换成本.在I0领域要占用带宽

线程是调度的基本单位:

  • 如果申请的资源能划分成若干份,每一个执行流拥有一个资源,就叫做线程

站在一款具体OS视角来理解:

  • 进程角度:进程 =内核数据结构(...)+ 代码和数据
  • 线程角度:进程内部运行,创建PCB,用进程控制块来模拟线程,提高可维护性

一个线程?会把资源做划分?

  • 一个进程看待资源,是通过地址空间+页表方式,即拥有虚拟地址的个数,占据的资源会更多.
  • 编译器是进行资源划分的兑现者,0S是进行申请资源的执行者
  • 线程角度:每一个线程,函数本身是代码块的集合,都要有地址,是代码块的入口地址,线程以某一个函数为入口,ELF被编址后,该线程就拥有整个虚拟地址的一部分。
  • 编译器角度:把虚拟地址空间给线程划分好

本质:划分虚拟地址资源!

一个线程出现异常,整个进程就会崩溃?

  1. 线程是代表一个进程;
  2. 线程出现异常,0S就会给进程发送信号,就会被杀掉,而进程的资源就会被释放,而线程用的资源是进程提供的,没有生存空间

页表和页表项:

逻辑上是个表结构,实际上 无符号长整数

typedef struct { unsigned long pte; } pte_t; // 页表项
typedef struct { unsigned long pgd; } pgd_t; // 页全局目录项

那页表是什么呢?

页表项1024所对应的数组都是1024项,都用的是2^10次方

把页表的起始地址拷贝到pgd里面,没有任何权限:

  • pgd_t[1024]就是页目录表,就是个整形数组
  • pte_t[1024]也是页表啊,也是个整形数组

但是,我们页表不是有用户级页表和内核级页表吗?不是有对应的读写权限吗?那么哪有位置给它们写入呢?只有32bit位
物理内存的每一个页框的低12位都是全0,那么就只需要用pte整数前20位就可以表示页框地址,低12位就可以充当标志位了,表示页框的状态.(我们的page也是有这几个状态的啊,直接查page不就可以了?低12位是为了增加效率)

1024个整形数组,不就是4kb吗?每一个表都占据一个页框

线程封装

我们将仿照C++封装Linux中的线程的做法,做一个独属于自己的线程库,并且这个线程库之后也需要被我们自己扩展使用.

封装的线程库的成员变量如下:

pthread_t _tid;
pid_t _lwpid;
std::string _name;
func_t _fun;
bool _isrunning;

线程启动

    void *start_route(void *args){std::string name = static_cast<char*>(args);}int start(){int n = pthread_create(&_tid, nullptr, start_route, (void*)_name.c_str());if (n == 0){std::cout << "run thread success" << std::endl;}}

pthread_create创建线程时,需要交代一个新线程要执行的函数,函数内部会进行回调func方法.而上述代码为什么会报红呢?本质上是成员函数含有隐藏的this指针,只允许一个参数.那么该如何解决这个问题呢?

  1. 放在类外
  2. 用static进行修饰

如果使用static修饰,那么就只能访问静态成员变量了,无法直接访问实例的成员,此时就不能访问func包装器了.那么又该怎么做呢?

为了解决静态函数无法访问实例成员的问题,可以将当前对象的 this 指针作为参数传递给回调函数。通过将 this 指针传递给静态回调函数,start_route 可以通过 this 指针访问实例的成员变量和方法。

    using func_t = std::function<void()>;static void *start_route(void *args){Thread *self = static_cast<Thread *>(args);self->_isrunning = true;self->_lwpid = get_lwp_id();self->_fun();pthread_exit((void *)0);}int start(){int n = pthread_create(&_tid, nullptr, start_route, this);if (n == 0){std::cout << "run thread success" << std::endl;}}

线程等待

    void join(){if (!_isrunning)return;int n = pthread_join(_tid, nullptr);if (n == 0){std::cout << "pthread_join success" << std::endl;}}

如果要像C++11那样进行可变参数的传递,是可以这样设计的,但是太⿇烦了,真到了哪⼀步,就直接⽤c++11吧,我们的⽬标主要是理解系统概念对象化,此处不做复杂设计,⽽且后续可以使⽤std::bind来进行对象间调用

总结

本文探讨了Linux系统中进程与线程的核心概念及其实现原理。首先分析了进程本质是"内核数据结构+代码和数据"的组合体,详细解释了进程资源分配机制。然后从操作系统角度比较了进程和线程的区别,指出线程作为调度基本单位通过划分虚拟地址资源来优化性能。文章还深入解析了页表结构及权限管理机制,特别是12位标志位的设计原理。最后通过C++代码示例展示了线程封装技术,包括回调函数设计、静态成员访问解决方案,并提供了完整的线程库实现代码。全文涵盖了从理论到实践的线程管理知识,为深入理解Linux系统编程提供了实用指导。

附源码

Thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <vector>
#include <pthread.h>
#include <string>
#include <functional>
#include <iostream>
#include <unistd.h>#define get_lwp_id() syscall(SYS_gettid)using func_t = std::function<void()>;
const std::string threadnamedefault = "None-Name";class Thread
{
public:Thread(func_t func, const std::string &name = threadnamedefault): _fun(func), _name(name){std::cout << "创建了一个线程" << std::endl;}static void *start_route(void *args){Thread *self = static_cast<Thread *>(args);self->_isrunning = true;self->_lwpid = get_lwp_id();self->_fun();pthread_exit((void *)0);}int start(){int n = pthread_create(&_tid, nullptr, start_route, this);if (n == 0){std::cout << "run thread success" << std::endl;}}void join(){if (!_isrunning)return;int n = pthread_join(_tid, nullptr);if (n == 0){std::cout << "pthread_join success" << std::endl;}}~Thread(){}private:pthread_t _tid;pid_t _lwpid;std::string _name;func_t _fun;bool _isrunning;
};#endif

testThread.cc

#include "Thread.hpp"void test()
{int cnt = 5;while (cnt--){std::cout << "新线程运行了" << std::endl;sleep(1);}
}int main()
{std::vector<Thread> tids;for (int i = 0; i < 5; i++){std::string name = "thread-";name += std::to_string(i + 1);Thread t(test, name);tids.push_back(t);}for (auto &tid : tids){tid.start();}// 回收线程for (auto &tid : tids){tid.join();}return 0;
}

http://www.dtcms.com/a/565049.html

相关文章:

  • 看天线的E面和H面方向图,就相当于看天线的 “身份证” 和 “体检报告”
  • ps怎么网站首页wordpress插件补丁
  • EchoTraffic: Enhancing Traffic Anomaly Understanding with Audio-Visual Insights
  • 如何基于llm+mysql构建轻量级全文搜索
  • 做uml图网站视频制作软件推荐
  • 深度掌握LCA方法论、OpenLCA建模、GREET能源评估及生物质碳核算R语言分析!
  • 判断题:可再生能源发电利用率指水电、风电、太阳能、生物质能等非化石能源占一次能源消费总量的比重。 这句话为什么错误
  • sward零基础学习,如何管理文档
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P06-29 属性信息委托
  • 3D光学弯管测量系统:空调管路高效质量管控利器
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P06-31 映射标签到属性
  • 微信小程序原生车牌输入器
  • 百度的网址是什么呢优化学校网站建设方案
  • Node.Js Express Sqlite3 接口开发
  • docker上部署 PolarDB-X v2.4.2数据库
  • 待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
  • 【Solidity 从入门到精通】前言
  • 天硕工业级SSD深度解析:NVMe性能分层的根源与高可靠选型指南
  • 人证查验一体机:公安安全检查的智能新助手
  • Python 查找并高亮显示指定 Excel 数据
  • 如何把xmind里的一整段文字变成独立主题的方法
  • 免费CDN
  • 【Java】流程控制
  • 公司网站开源源码现在pc端网站开发用的什么技术
  • 【weblogic】JND注入漏洞
  • 简单建设一个网站的过程安徽建设工程协会网站
  • Spring Security权限认证机制详解 实战
  • java每日精进 11.03【基于Spring AOP和事件驱动的资源操作消息处理流程(类似于若依框架的@Log注解)】
  • Spring 从 0 → 1 保姆级笔记:IOC、DI、多配置、Bean 生命周期一次讲透
  • SpringBoot 项目基于责任链模式实现复杂接口的解耦和动态编排