【Linux】线程属性的定义&如何修改线程属性(附图解与代码实现)

我们知道,在创建线程时,会用到pthread_create()函数 ,我们来简单介绍一下该函数:

pthread_create(线程的tid , 线程属性 , 工作函数名 , 函数需要的参数);

这篇博客要讲的线程属性,便是用于进行线程的初始化的,我们可以通过对线程属性的修改来自定义线程,接下来我们来了解一下什么线程属性

线程属性中的成员

线程属性是一个结构体,用法为 pthread_attr_t 变量名(本篇博客里默认变量名为attr)

线程属性结构体中的成员分别有:线程的警戒缓冲区、线程的优先级指针、线程的退出状态、线程栈地址、线程栈大小

接下来,我们来介绍一下该结构体中的这几个成员

线程的警戒缓冲区

首先我们要知道,每当一个线程被创建出来的时候,都会有一个相应的线程栈出现,而栈存在溢出问题(栈的溢出都是上溢),线程栈是申请在堆空间的

一旦栈发生溢出,数据就会向上覆盖,影响甚至破坏到库、栈区等空间中的数据。但更可怕的是,由于用户对用户空间中的内容具有读写权限,线程栈上溢所导致的对这些内容的修改,系统是不会报错的,只有当数据溢出到内核层时,我们才能够发现问题,但此时已经晚了,数据已经全被破坏了,所以我们需要来给每个线程栈"加个盖子",也就是所谓的"警戒缓冲区"。

警戒缓冲区的大小一般为4K,这块内存是不可读写的,所以当线程栈发生上溢,想要修改这块内存中的内容时,系统就会发现有线程非法操作内存,并杀死该线程,这样就可以保护其他内存中的数据

线程的优先级指针

表示线程的优先级,一般情况下不建议修改,因为会影响系统的稳定性,一般只有杀毒软件或系统的防御软件才会修改其优先级

线程退出状态

线程的退出状态有两种,分别是回收态(PTHREAD_JOINABLE)和分离态(PTHREAD_DETACH)

线程栈地址

由于当修改线程属性时,线程还没有被创建,自然也就没有地址可存,所以默认情况下都是nil,表示空

线程栈大小

线程栈大小一般情况下都是8M,但是我们知道,8M如果用二进制表示时非常大的数,将这么大的数放进去仅仅表示线程栈的大小其实没什么意义,所以默认情况下,这里存放的数据就是0,表示8M,申请空间时也是申请8M大小的空间

修改线程属性的相关函数

在了解了线程属性的组成之后,我们就要来了解一下修改线程属性的相关函数了

先介绍下一会会用到的几个变量:

  • pthread_attr_t attr ; //定义一个线程属性结构体
  • int exit_state ; //线程属性中的退出状态
  • void* thread_stack_addr ; //线程属性中的线程栈地址
  • size_t thread_stack_size ; //线程属性中的线程栈大小

|----------------------------------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------|
| 函数 | 功能 | 返回值 |
| pthread_attr_getdetachstate(&attr , &exit_state); | exit_state作为传出参数,可以获取线程属性中的退出状态 | 回收态返回 PTHREAD_CREATE_JOINABLE 分离态返回 PTHREAD_CREATE_DETACHED |
| pthread_attr_setdetachstate(&attr , exit_state); | 通过传入参数exit_state,设置线程属性中的退出状态 | 成功返回0,失败返回非0错误码 |
| pthread_attr_getstack(&attr , &thread_stack_addr , &thread_stack_size); | thread_stack_addr、thread_stack_size作为传出参数,可以获取线程属性中的栈地址与栈大小 | 返回两个参数------线程栈地址与线程栈大小 |
| pthread_attr_setstack(&attr , thread_stack_addr , thread_stack_size); | 通过传入参数thread_stack_addr、thread_stack_size,可以设置线程属性中的栈地址与栈大小 | 成功返回0,失败返回非0错误码 |
| pthread_attr_init(&attr); | 初始化线程属性结构体 | 成功返回0,失败返回-1 |
| pthread_attr_destroy(&attr); | 释放线程属性结构体内存 | 成功返回0,失败返回-1 |

修改线程属性的具体实现

接下来,我们可以根据这些函数来实际操作一下,完成以下两个小任务

  1. 获取线程属性中默认的退出状态(难度:⭐)
  2. 获取默认状态下线程属性中的线程栈地址与大小。将线程属性中的退出状态设置为分离态,修改线程栈地址,修改线程栈大小为1M,并测试使用这种线程属性的线程,系统一共能够创建多少个?(难度:⭐⭐⭐⭐)

PS:64位机下,即使你修改了线程栈大小,创建的数目和原来还是一样的,因为你的修改是无效的,系统创建的线程栈大小还是8M,修改线程栈大小只有32位机有效

一、获取线程属性中默认的退出状态,以下是代码实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <fcntl.h>

int main()
{
    pthread_attr_t attr;
    //1.初始化线程属性
    pthread_attr_init(&attr);
    //2.检测线程属性中的线程退出状态
    int detach_status;
    pthread_attr_getdetachstate(&attr , &detach_status);
    //3.判断是回收态还是分离态
    if(detach_status == PTHREAD_CREATE_JOINABLE)
    {
        printf("线程属性默认为回收态\n");
    }
    else
    {
        printf("线程属性默认为分离态\n");
    }
    //4.释放线程属性结构体内存
    pthread_attr_destroy(&attr);
    printf("进程退出!\n");
}

怎么样,是不是很简单呢? 接下来,我们来完成第二个小任务

二、获取默认状态下线程属性中的线程栈地址与大小。将线程属性中的退出状态设置为分离态,修改线程栈地址,修改线程栈大小为1M,并测试使用这种线程属性的线程,系统一共能够创建多少个,以下是代码实现

cpp 复制代码
//pthread_addr_change.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <pthread.h>
#include <fcntl.h>

void* thread_jobs(void* arg)
{
    while(1)
    {
        sleep(1);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_attr_t attr;
    //1.初始化线程属性
    pthread_attr_init(&attr);
    //2.检测线程属性中的线程退出状态
    int detach_status;
    pthread_attr_getdetachstate(&attr , &detach_status);
    //3.判断是回收态还是分离态并打印
    if(detach_status == PTHREAD_CREATE_JOINABLE)
    {
        printf("线程属性默认为回收态\n");
    }
    else
    {
        printf("线程属性默认为分离态\n");
    }
    //4.将线程属性中的退出态修改为分离态
    pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
    //5.获取线程属性中线程栈的初始地址与大小并打印
    void* thread_stack_addr;
    size_t thread_stack_size;
    pthread_attr_getstack(&attr , &thread_stack_addr , &thread_stack_size);
    printf("线程栈地址为 %p , 线程栈大小为 %d \n" , thread_stack_addr , (int)thread_stack_size);
    //6.通过malloc函数修改线程栈的初始地址,并将线程栈大小改为1M
    pthread_t tid;
    thread_stack_size = 0x100000;//0x100000代表1M
    int flag = 0;
    int errno;
    while(1)
    {
        //如果malloc函数的返回值为NULL,就说明分配失败,内存已经用完
        if((thread_stack_addr = (void*)malloc(thread_stack_size)) == NULL)
        {
            perror("thread_addr malloc failed!\n");
            exit(0);//进程退出
        }
        //修改栈初始地址和大小
        pthread_attr_setstack(&attr , thread_stack_addr , thread_stack_size);
        //创建线程,并判断是否创建失败
        if((errno = pthread_create(&tid , &attr , thread_jobs , NULL)) > 0)
        {
            perror("thread create failed!\n");
            exit(0);//进程直接退出
        }
        else
        {
            flag++;
            printf("flag = %d\n",flag);
        }
    }
    pthread_attr_destroy(&attr);
    return 0;  
}

结果如下图所示:

以上就是本篇博客的全部内容了,大家有什么地方没有看懂的话,可以在评论区留言给我,咱要力所能及的话就帮大家解答解答

今天的学习记录到此结束啦,咱们下篇文章见,ByeBye!

相关推荐
早睡冠军候选人9 小时前
Ansible学习----管理复杂的 Play 和 Playbook 内容
运维·学习·云原生·ansible
LBuffer10 小时前
破解入门学习笔记题四十六
数据库·笔记·学习
Yurko1312 小时前
【计网】基于三层交换机的多 VLAN 局域网组建
网络·学习·计算机网络·智能路由器
月下倩影时12 小时前
视觉进阶篇——机器学习训练过程(手写数字识别,量大管饱需要耐心)
人工智能·学习·机器学习
福旺旺13 小时前
Linux——解压缩各类文件
linux
MasterLi802315 小时前
我的读书清单
android·linux·学习
hssfscv15 小时前
JAVA学习笔记——集合的概念和习题
笔记·学习
ha204289419415 小时前
Linux操作系统学习之---初识网络
linux·网络·学习
飞凌嵌入式15 小时前
【玩转多核异构】T153核心板RISC-V核的实时性应用解析
linux·嵌入式硬件·嵌入式·risc-v
陌路2015 小时前
Linux 34TCP服务器多进程并发
linux·服务器·网络