Linux:线程概念 | 线程控制

【Linux】线程 Linux线程的基础知识 阅读详情

一、线程概念

 一个进程需要访的大部分资源,诸如自身的代码、数据、new\malloc的空间数据、命令行参数和环境变量、动态库、甚至是系统调用访问内核代码…都是通过虚拟地址空间来访问的。换而言之,进程地址空间是进程的资源窗口!!
 进程创建费时费力。在创建时,我们需要为进程创建PCB、地址空间、页表、将进程自身的代码和数据换入内存并建立映射、将进程PCB状态改为R状态、添加带运行队列中…  但如果现在已经存在一个线程了,我仅仅将进程PCB复制多份,然后让所有“进程”PCB全部指向同一个虚拟地址空间。然后通过技术手段,将虚拟地址空间合理分配给每一个“进程”。当CPU调度执行该“”进程“时,只会执行原本进程中的一部分代码和数据,执行我们要执行任务的一部分任务,我们将这种比传统“进程”更加轻量化的进程就称为线程!!

 线程是在进程内部执行,比进程更加轻量化的一种执行流!!
在这里插入图片描述

二、pthread原生线程库

 在Linux中,如果操作系统真的支持线程,此时在计算机中必然存在大量的线程,所以OS需要对线程进行管理。先描述,在组织!!操作系统需要创建struct TCB结构体来描述线程,然后通过链表将所有线程进行链接,组织起来。  并且线程是进程内部的一种执行流,还要将进程和线程进行接藕。更重要的是,线程也需要被调度,也就意味着我们需要为线程调度重新设计一套调度算法。

 但其实我们发现线程TCB和进程PCB所需要的属性字段是差不多的,都需要pid、状态、调度优先级…并且两者都是需要被调度被运行的!如果Linux既要支持线程,也需要满足对线程使用的需求。其实是可以用PCB充当TCB,这样我们就可以把系统中所有线程调度和切换的代码在线程层面上全部复用起来。并且对线程的管理工作,也可以直接复用进程管理代码!我们将这种直接创建PCB,然后让所有PCB指向同一个地址空间,在对资源进行分配的线程实现方式称为Linux中线程的实现方案!!我们也将这种线程称为轻量级进程!!

 在Linux中没有真正意义上的线程,而是用轻量级进程来充当线程。但这是Linux OS自身的独特设计,对于用户来说只认识进程和线程。所以我们在Linux操作系统和用户之间封装了一层软件层。该软件层向下调用轻量级进程相关接口,向上给用户提供线程的控制接口,而该软件层被称为pthread原生线程库!这也是为啥我们编写线程代码时,在编译阶段需要主动链接pthread库原因!
 可以通过ps -aL查看系统线程!

在这里插入图片描述

三、线程 VS 进程

  1. 线程进程内部中一种执行流,线程比进程更加轻量化。(创建、调度切换、删除更加轻量化)
  2. 线程时CPU调度的基本单位;而进程是资源分配的基本单位。
  3. 进程拥有独立的地址空间和页表;而线程是共享进程的地址空间和其他资源!
  4. 进程的栈是由进程地址空间维护;而线程的栈空间是由pthread维护,映射到共享区中的!

3.1 线程切换轻量化原理

 CPU在调度执行某行代码时,由于局部性原理,计算机会将该行周围的代码全部加载到CPU cache缓存中。而cache缓存是基于进程,进程切换,chche数据立即失效,需要重新对数据进行热加载,将数据加载到cache缓存中;但对于线程切换来说,下一个线程极有可能还是访问这些代码和数据,大概率是不需要进程切换掉cache缓存中的数据!!(局部性原理也给磁盘加载到内存的预加载提供了理论基础)

 除此之外,在CPU中还存在很多寄存器来存储进程/线程的上下文,存在一些寄存器保存的内容执行地址空间和页表。对于线程来说,地址空间和页表是相同的,这也意味着线程切换时我们不需要将所有线程的寄存器全部切换走;只需将少部分保存临时数据的寄存器切换即可!

  1. 需要切换的寄存器少!
  2. 不需要重新跟新cache!
    在这里插入图片描述

3.2 线程私有数据

 线程和进程共享大部分数据,诸如代码段、数据段、全局数据等都是共享的。除此外,还包含如下进程资源:

  1. 共享当前进程文件描述符表
  2. 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  3. 当前工作目录
  4. 用户id和组id

 除此在外,线程中还存在一些数据是线程私有的,具体如下:

  1. 线程拥有独立的寄存器硬件上下文!线程是独立被调度的,线程必然拥有独立的上下文,比如运行过程中产生的临时数据。
  2. 线程都有独立栈结构!‘一般地址空间中的栈属于主线程,然后通过在堆上申请空间,从当其他新线程的栈空间!
  3. 线程ID、信号屏蔽字、调度优先级、errno等

3.3 线程优缺点

1)、线程优点

  1. 线程的创建、删除、切换比进程更加简单轻量;线程占用的系统资源比进程更少!
  2. 对于计算密集型应用,线程可以利用多处理器并行特点,将计算分解到多个线程并行处理,提高效率!
  3. 对于I/O密集型应用,可以将I/O操作进行重叠,以线程同时等待不同的I/O操作,提高性能!

2)、线程缺点

  1. 如果线程的数量比处理器多,增加了额外的同步和调度开销,而可用的资源不变,导致性能损失!
  2. 线程会导致整体程序的健壮性降低。如果存在一些错误导致线程异常,会导致进程整体异常退出!
  3. 线程缺乏访问控制!进程是访问控制的基本粒度。对于线程,OS中并没有提供相关的方法进行线程访问控制!
  4. 编写难度高!线程出现问题,通常是很难进行排查的。

四、线程相关函数及功能

4.2 线程创建

 【函数接口】:

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

 Compile and link with -pthread//编译时,需要链接pthread原生线程库
 返回值:成功返回0;失败返回错误码
  1. thread:输出型参数,返回线程id。
  2. attr:设置线程属性,为NULL表示使用默认属性。
  3. start_routine:新线程执行方法的函数地址。
  4. arg:传给start_routine函数的参数。
  5. 错误检查:传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回。pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小

【实例】:

void* ThreadRoution(void* arg)
{
    std::cout << "I am new thread" << std::endl;
    const char* threadname = (const char*)arg;
    std::cout << threadname << std::endl;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoution, (void*)"thread-1");
    std::cout << "I am main thread" << std::endl;
    sleep(1);
    return 0;
}

在这里插入图片描述

4.3 获取自身线程id

 pthread_t pthread_self(void);

4.4 线程取消

  int pthread_cancel(pthread_t thread);
  
  返回值:成功返回0;失败返回错误码

4.5 线程终止

 线程退出有3种方式:return返回、pthread_cancel取消线程、pthread_exit()线程终止。(不能调用exit()函数,该函数用于进程退出,一般调用,所以相关线程将全部退出!)

【进程终止函数原型】:

 void pthread_exit(void *retval);
  1. retval:retval不要指向一个局部变量。
  2. 返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)。
  3. pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

4.6 线程等待

 一般情况下,主线程需要对新线程进行等待,否则会导致类似于进程的“僵尸”问题,导致内存泄漏!

 int pthread_join(pthread_t thread, void **retval);
 
 thread:需要等待的线程id
 retval:指向待等待线程的返回值

 调用该函数的线程会被阻塞,知道id为thread的线程终止。依据线程终止时的不同方式,retval中会返回不同的结果!

  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  2. . 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED(值为-1,原型为#define PTHREAD_CANCELED ((void *) -1))。
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

4.7 线程分离

 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。如果不关心线程的返回值,join是一种负担,这个时候,我们可以将线程设置为分离状态,告诉系统,当线程退出时,自动释放线程资源!
 线程组内其他线程对目标线程进行分离,也可以是线程自己分离!但joinable和分离是冲突的,一个线程不能既是joinable又是分离的!

 int pthread_detach(pthread_t thread);
  1. 线程分离后是可以cancel的,但不能join!!

五、线程id含义

4.1 线程id含义

 下面我们来看看一段代码,看看线程id是啥?

void* ThreadRoution(void* arg)
{
    std::cout << "I am new thread" << std::endl;
    const char* threadname = (const char*)arg;
    std::cout << threadname << std::endl;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoution, (void*)"thread-1");
    std::cout << tid << std::endl;
    std::cout << "I am main thread" << std::endl;
    return 0;
}

【运行结果】:
在这里插入图片描述

 我们发现线程id非常大,很奇怪。那这究竟是啥?

 在Linux中,系统提供了相关创建LWP的相关接口:clone。此时pthread库通过调用该函数来创建线程给上层用户使用!

 #include <sched.h>

 int clone(int (*fn)(void *), void *child_stack,
           int flags, void *arg, ...
           /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
//stack: 允许用户传入空间从当新轻量级进程的栈空间
// flag: 该函数也是fork()函数的底层原理,该标记位指明到创建的LWP是一个新的进程还是线程

 既然已经可以创建出线程了,我们需要对线程进程管理。但一般资源都是由OS来管理,但操作系统中只存在轻量级进程,线程概念是由原生线程库pthread提供的。而该库会被加载到共享去中,该库可以看见所有用户创建的线程,所以需要pthread原生线程库自身对线程进程管理。先描述,在组织!

在这里插入图片描述

&emsp但;pthread原生线程库本质是第三方库,被加载映射到共享区中。具体如下:
在这里插入图片描述
 所以在Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

  1. 线程局部存储:我们在程序中定义一个全局变量,而该全局变量是所有线程共享的。此时我们可以加上__thread编译选项。如__thread int gal_val = 100;。此时所有线程gal_val各自私有一份。

六、线程互斥

6.1 线程互斥相关概念

  1. 临界资源:在多线程中,多执行流访问的公共资源被称为临界资源。
  2. 临界区:在线程内部,访问临界资源的代码称为临界区。
  3. 互斥:在任何时候,只允许一个执行流进入临界区访问临界资源,对临界资源进行保护!
  4. 原子性:不会被任何调度机制打断的操作,原子性操作只有两态,要么完成,要么不完成!

6.2 多执行流访问数据不一致问题(抢票)

 下面我们对线程进行简单疯转,然后在主逻辑代码中直接创建5个线程,并用vector管理起来。然后启动所有线程,每个线程执行抢票函数,最后在对所有线程进行等待回收!

 抢票逻辑:首先定义一个全局变量tickets表示票的总数。抢票函数是一个死循环,只要还存在票(tickets >0),我们直接将票数减减,表示抢到票了。具体如下:

【线程封装】:

#include <iostream>
#include <pthread.h>
#include <functional>
#include <string>

template <class T>
using func_t = std::function<void(T)>;

template <class T>
class Thread
{
public:
    Thread(const std::string &name, func_t<T> func, T data)
        : _tid(0), _threadname(name), _isrunning(false), _func(func), _data(data)
    {
    }

    static void *Routine(void *args)
    {
        Thread *ts = static_cast<Thread *>(args);
        ts->_func(ts->_data);
        return nullptr;
    }

    bool Start()
    {
        int n = pthread_create(&_tid, nullptr, Routine, this);
        if (n != 0)
        {
            return false;
        }
        _isrunning = true;
        return true;
    }

    bool Join()
    {
        if (!_isrunning)
            return true;

        int n = pthread_join(_tid, nullptr);
        if (n == 0)
        {
            _isrunning = false;
            return true;
        }

        return false;
    }

    std::string GetThreadName()
    {
        return _threadname;
    }

    bool IsRunning()
    {
        return _isrunning;
    }

private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

【抢票主逻辑】:

#include "Thread.hpp"
#include <unistd.h>
#include <vector>

int ticket = 10000; // 全局变量从当票数

void GetTicket(const std::string &threadname)
{
    while (true)
    {
        if (ticket > 0)
        {
            usleep(1000);                                              // 充当抢票逻辑
            printf("%s get ticket: %d\n", threadname.c_str(), ticket); // 不要用cout,复现不了
            ticket--;
        }
        else
        {
            break;
        }
        // 还存在后续其他动作,购买者信息填充
    }
}

int main()
{
    int num = 5;
    std::vector<Thread<std::string>> threads;
    for (int i = 1; i <= num; i++)
    {
        char name[128];
        snprintf(name, sizeof(name), "%s-%d", "Thread", i);
        Thread<std::string> td(name, GetTicket, name);
        threads.push_back(td);
    }
    for (int i = 0; i < threads.size(); i++)
    {
        threads[i].Start();
    }

    for (int i = 0; i < threads.size(); i++)
    {
        threads[i].Join();
    }
    return 0;
}

【结果展示】:
请添加图片描述
 我们惊奇发现最后票数竟然时负数,显然不合理。而这种现象就是我们没有对全局资源进行保护,导致的数据不一致问题!!

6.3 抢票数据不一致原因

 首先票数ticket > 0和ticket--不是原子性的,由3条汇编组成!下面时ticket--汇编代码,ticket > 0类似!
在这里插入图片描述
 在我们抢票之前需要先进行判断,票数是否大于0。但该逻辑分3步:先将ticket的值读到CPU中、CPU进行逻辑计算、将结果写回内存中!
 但在该过程中,如果此时ticket的值为1。计算机刚将ticket的值读到CPU中,此时线程的事件片到来,需要切换到下一个线程。但不巧的是,之后的几个线程的发送了类似的情况!!此时所有线程的硬件上下文中ticket的值都为。
 在后面调度这些进程时,首先回恢复线程上下文,即ticket为1。所有线程全部进入临界区访问临界资源。导致ticket一直被减减,减到负数!!

在这里插入图片描述

在这里插入图片描述

6.4 互斥量

 要解决上述问题,需要进行加锁。在Linux中,我们将这把锁称为互斥量!!

1)、锁的创建和销毁

 锁的创建有两种方式:

  1. 使用pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;定义一把全局的锁就可!使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁!!
  2. 创建局部锁,并且需要对锁进行初始化!在使用完后需要对互斥量进行销毁!!
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    const pthread_mutexattr_t *restrict attr); // 初始化互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex); // 销毁互斥量

2)、加锁、解锁

 引入互斥量之后,在当问临界资源时需要先进行加锁操作。将可以的并行访问操作转化为串行访问!!

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号
  1. 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功。
  2. 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_ lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。

3)、互斥量实现原理

 单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题。为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。
在这里插入图片描述

下面我们来解释上面这段程序:(mutex本质就是一个变量,这里我们直接将其简化为一个整型,值为1)。
在这里插入图片描述
 此时公共资源锁变量的值已经为0,表示锁被其他线程所持有。一旦其他线程重复上述的指令时,%al和mutex变量的值进行交换后,%al寄存器中的值为0.表示当前线程没有得到锁资源,被挂起等待。

 同理unlock解锁时,直接将1填充到mutex变量内容,此时就表示解锁。然后唤醒等待线程即可!!

七、线程同步

 前面抢票逻辑看似没问题,但在某些系统(比如Centos)下,其中一些线程抢占锁的能力是非常强的,导致其他线程长时间无法获得锁资源,导致线程饥饿问题。为了解决线程饥饿问题,我们引入同步机制,让所有线程按照一定的顺序进行调度!!

八、可重入 VS 线程安全

8.1 可重入 VS 线程安全

  1. 线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。
  2. 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

8.2 常见的线程安全/不安全的情况

不安全:

  1. 不保护共享变量的函数。
  2. 函数状态随着被调用,状态发生变化的函数。比如在函数内部定义一个静态的计数器,用来表示函数进入多少次。而这种数据虽然已经类似于全局的,但作用域在函数内部。每次调用,函数内部的状态数据都会发生改变,可能不安全。
  3. 返回指向静态变量指针的函数。
  4. 调用线程不安全函数的函数

安全:

  1. 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的类或者接口对于线程来说都是原子操作
  2. 多个线程之间的切换不会导致该接口的执行结果存在二义性

8.3 常见可重入/不可重入的情况

不可重入:

  1. 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
  2. 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
  3. 可重入函数体内使用了静态的数据结构

可重入:

  1. 不使用全局变量或静态变量
  2. 不使用用malloc或者new开辟出的空间
  3. 不调用不可重入函数
  4. 不返回静态或全局数据,所有数据都由函数的调用者提供
  5. 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

8.4 可重入与线程安全区别

  1. 线程安全是指多个线程并发执行同一段代码时,不会出现不同的结果。
  2. 重入指同一函数被不同执行流调用,当前流程还没执行完,其他流程就进入了。
  3. 可重入的函数是线程安全的,线程安全不一定可重入。

9)常见锁

Linux系统编程-线程(Thread)相关内容详解 在Linux系统中,线程(Threads)是指进程内部的轻量级执行单元。轻量级的进程(LWP:light weight process),在Linux环境下线程的本质仍是进程。进程:拥有独立的地址空间,拥有PCB,相当于独居。线程:有PCB,但没有独立的地址空间,多个线程共享进程空间,相当于合租。 阅读详情

相关推荐

Linux——进程与线程

Linux线程,也称为轻量级进程(LWP),是操作系统进行调度的基本单位,用于在进程内部执行不同的代码路径,实现并发执行。与进程相比,线程具有更小的执行粒度,更低的调度成本,并且可以共享进程的资源,如内存空间、文件描述符等。LWP是轻量级进程,在Linux下进程是资源分配的基本单位,线程是cpu调度的基本单位,而线程使用进程pcb描述实现,并且同一个进程中的所有pcb共用同一个虚拟地址空间,因此相较于传统进程更加的轻量化。

mdjsmg的博客 1850

【Linux】线程详解

在一个程序里的一个执行路线就叫做线程。更准确的定义是:线程是“一个进程内部的控制序列”一切进程都至少有一个执行线程线程在进程内部运行,本质是在进程地址空间内运行在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更轻量化透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流进程的创建伴随着进程控制块,进程地址空间以及页表的创建,虚拟地址和物理地址就是通过页表建立映射的。

m0_72940975的博客 3685

【Docker】如何让docker容器正常使用nvidia显卡

描述了如何在docker容器中使用英伟达显卡的过程和问题解决方法。

NameLoadingFailed的博客 8372

从虚拟内存到 pthread:Linux 线程概念与控制全解析

从虚拟内存到 pthread:Linux 线程概念与控制全解析

2502_90266547的博客 600

AI图像识别在临电配电箱安全检测中的应用与实践

计算机视觉与AI图像识别技术正逐步改变传统工业检测方式。通过深度学习模型如YOLOv5和ResNet,系统能自动识别设备元件并分析其状态,大幅提升检测效率和准确性。这种技术特别适用于需要严格遵循行业标准的场景,如临电配电箱的安全检测。将《施工现场临时用电安全技术规范》等条文转化为可量化的算法,AI系统能自动检查PE线连接、漏保功能等关键项目。实际应用表明,此类解决方案可使电气事故率下降90%以上,同时检测耗时缩短80%,为工地安全提供了智能化保障。

weixin_29042035的博客 326

Flutter版本升级踩坑实录:从3.27到3.35,我们是如何修复Impeller引擎导致的花屏问题的?

本文详细记录了团队在Flutter版本升级过程中遇到的Impeller引擎导致的花屏问题及解决方案。从问题诊断到最终升级至稳定版3.35,文章分享了排查步骤、升级策略和性能对比数据,为开发者提供了实用的参考经验。

weixin_26854555的博客 502

Linux系统:线程概念 & 线程控制

讲解Linux中线程的基本概念和操作,并深入讲解了线程库维护线程的策略等

博客网站 https://async-area.com 1332

K型热电偶分度表读取方法

K型热电偶分度表读取方法

guaizaiguaizai的博客 5888

Linux:线程概念及线程控制

1、线程相关概念 2、线程控制的接口 3、线程id及进程地址空间

碧瑶的博客呀 225

真题库系列之 CCF GESP 2025年12月认证 Python 1级真题含正确答案与解析

学生通过计算机基础知识的学习,了解计算机的构成与操作,以及计算机的发 展历程。通过编程基础知识以及语句的掌握,可以独立完成简单功能的顺序结构、 分支结构、循环结构的程序。

iCloudEnd的博客 314

[网络安全自学篇] 二.Chrome浏览器保留密码功能渗透解析及登录加密入门笔记

最近开始学习网络安全相关知识,接触了好多新术语,感觉自己要学习的东西太多,真是学无止境,也发现了好几个默默无闻写着博客、做着开源的大神。接下来系统分享一些网络安全的自学笔记,希望读者们喜欢。 上一篇文章分享了看雪Web安全总结知识和一个异或解密的示例,本篇文章着重讲解Chrome浏览器保留密码功能渗透解析及登录加密入门笔记,结合实际例子一步步实现浏览器漏洞的挖掘。非常基础的文章,希望对入门的博友们有帮助,大神请飘过,谢谢各位看官!

杨秀璋的专栏 2万+

Linux:线程概念及控制

Linux线程是进程内部的执行流,共享进程资源但拥有独立的栈和上下文。线程比进程更轻量级,创建和切换开销更小。Linux通过轻量级进程(LWP)实现线程,内核使用struct page结构体管理物理内存页。线程共享进程地址空间但拥有私有数据,如线程ID、寄存器、栈等。线程终止可通过return、pthread_exit或pthread_cancel实现,需用pthread_join回收资源以避免泄漏。线程可分离开启自动回收,此时不可再join。pthread库维护的线程ID实质是进程地址空间中的内存地址。

atuddjdec_的博客 610

Dify智能体开发:Dify文件上传

相较于聊天文本,文档文件能够承载大量的信息,例如学术报告、法律合同。受限于 LLM 自身仅能够支持文件或图片,难以获取文件内更加丰富的上下文信息,应用的使用者不得不手动复制粘贴大量信息与 LLM 对话,增加了许多不必要的使用成本。

王国平的博客 2590

Linux:线程概念与控制

线程的概念,页表,多级页表,进程vs线程,线程控制,创建、终止、等待、分离,线程的空间布局,封装线程

QinMou 923

基于 STM32 的手持式安检金属探测器设计与实现

【代码】基于 STM32 的手持式安检金属探测器设计与实现。

weixin_42300449的博客 899

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Linux系统:线程概念和线程控制

而且从上方的struct page源码中可以发现,它是不存储页框的起始地址和终止地址的,因为可以通过下标计算出起始地址,起始地址 + 4kb就可以求出终止地址。可以看到,确实是有两个叫做test.exe的线程的,它们的PID都是141776,但是它们的LWP不同。另外的,你会发现第一个线程的PID = LWP = 141776,说明这个线程是主线程,其余的所有线程都是这个主线程创造的。在CPU调度进程的时候,本质是在调度进程的PCB,把进程的PCB放到运行队列中,然后CPU依次执行队列中的进程。

m0_61088872的博客 1045

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Linux:线程概念、理解、控制

本文介绍了线程的理解和线程控制基本概念

主要为C++知识点、Linux知识点,计算机组成结构笔记。 810

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Linux:线程的概念与控制

• 线程(thread)是程序中的一条独立执行路径。更准确地说,线程是"进程内部的控制序列"• 每个进程至少包含一个执行线程• 线程运行在进程内部,确切地说是在进程的地址空间内运行• 在Linux系统中,CPU视角下的PCB(进程控制块)比传统进程更加轻量级• 通过进程虚拟地址空间可以查看进程的大部分资源,将这些资源合理分配给各个执行流,就形成了线程执行流。

2302_79860961的博客 1312

【Linux探索学习】第二十九弹——线程概念:Linux线程的基本概念与线程控制详解

【Linux探索学习】第二十九弹:线程的基本概念和线程控制的相关知识点,理清线程与进程的关系与区别!!

2301_80220607的博客 5571

Linux系统编程——多线程[上]:线程概念和线程控制

目录1.再谈页表2.Linux线程概念2.1pthread原生库的基本使用2.2PID和LWP2.3Linux线程的资源以及优缺点2.4Linux线程健壮性问题2.5可重入函数和线程独立栈3.Linux线程控制3.1Linux线程终止3.2Linux线程等待3.3线程取消3.4线程分离使用线程库的注意事项4.pthread原生线程库的理解4.1从语言的角度理解原生线程库4.2从底层的角度理解原生线程库5.对pthread原生线程库进行封装在通用操作系统和进程的部分当中,介绍了一个容易理解但不正确的有关页表的

weixin_59913110的博客 2077

linux:线程概念与控制

CR3中存储了当前进程页表目录的物理地址,我们根据虚拟地址高10位查找该虚拟地址所属页表(物理地址 = 页表目录物理地址 + 高10位值*4),页表目录存储的是地址元素,4字节为单位,所以偏移项*4。

2301_79954395的博客 626

Linux系统编程:线程概念与控制

本文从Linux内核角度详细阐述了线程的概念及其与进程的区别。线程作为进程内部的执行流,与父进程共享地址空间和页表,相比进程具有更细粒度和更低调度成本。文章分析了线程的优缺点:优点包括创建代价小、切换效率高、资源占用少;缺点涉及性能损失、健壮性降低等问题。同时介绍了Linux线程控制相关函数库(pthread)的使用方法,包括线程创建、等待、退出等操作。此外,还深入解析了页表的作用机制,以及线程独立栈的实现原理。最后讨论了线程分离的概念及注意事项,指出分离线程后主线程不应过早退出以避免资源回收问题。全文系统

chenyuhao2024的博客 794

【Linux系统:多线程】线程概念与控制

(1) 在 Linux 内核中,线程被称为轻量级进程(LWP, Light Weight Process)。(2) 传统的进程模型中,一个进程对应一个地址空间,对应一个 task_struct;但在 Linux 线程模型中,多个 task_struct 指向同一个 mm_struct(进程地址空间)。(3) 线程是“一个进程内部的控制序列”,它在进程的虚拟地址空间内运行。(4) CPU 在进行调度时,并不区分进程和线程,它只认 task_struct。

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 697

Linux系统:线程的概念和控制

本文主要介绍了线程的概念、优缺点及其控制方法。线程作为进程内部的执行单元,允许多个任务并发执行,共享同一进程的内存和资源,具有轻量级、高效的优势。相比进程,线程更节省系统资源,能更好利用多核CPU,但同时也存在稳定性、访问控制和编程复杂度等挑战。文章详细讲解了POSIX线程库的使用,包括线程创建、线程ID类型及线程终止方法,并通过C语言代码示例演示了线程的基本操作。掌握线程技术有助于提升程序性能和响应速度,为并发编程和服务器开发奠定基础。

Visual_progress的博客 905
上一篇: Linux: Mysql环境安装
下一篇: Linux OS:基于阻塞队列的生产者消费者模型
小白debug~
小白debug~ 新星创作者: C/C++技术领域 新星创作者: C/C++技术领域
博客等级 码龄4年 3510粉丝 128原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白debug~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值