[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;
}

评论 71
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值