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

目录

回顾与总结

线程封装

线程启动

线程等待

总结

附源码

回顾与总结
进程是承担分配资源的基本实体,本质:

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

如果申请的资源能划分成若干份,每一个执行流拥有一个资源,就叫做线程
站在一款具体OS视角来理解:

进程角度:进程 =内核数据结构(...)+ 代码和数据
线程角度:进程内部运行,创建PCB,用进程控制块来模拟线程,提高可维护性
一个线程?会把资源做划分?

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

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

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

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

typedef struct { unsigned long pte; } pte_t; // 页表项
typedef struct { unsigned long pgd; } pgd_t; // 页全局目录项
AI写代码
bash
那页表是什么呢?

页表项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;
AI写代码
bash
线程启动
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;
}
}
AI写代码
bash

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

放在类外
用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;
}
}
AI写代码
bash

线程等待
void join()
{
if (!_isrunning)
return;
int n = pthread_join(_tid, nullptr);
if (n == 0)
{
std::cout << "pthread_join success" << std::endl;
}
}
AI写代码
bash

如果要像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
AI写代码
bash

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;
}
AI写代码
bash

————————————————
版权声明:本文为CSDN博主「egoist2023」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/egoist2023/article/details/154366015

阅读剩余
THE END