【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!

相关推荐
霍格沃兹测试开发学社测试人社区17 分钟前
软件测试学习笔记丨Flask操作数据库-数据库和表的管理
软件测试·笔记·测试开发·学习·flask
今天我又学废了34 分钟前
Scala学习记录,List
学习
颇有几分姿色40 分钟前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
AndyFrank1 小时前
mac crontab 不能使用问题简记
linux·运维·macos
筱源源1 小时前
Kafka-linux环境部署
linux·kafka
Mephisto.java2 小时前
【大数据学习 | kafka高级部分】kafka中的选举机制
大数据·学习·kafka
算法与编程之美2 小时前
文件的写入与读取
linux·运维·服务器
南宫生2 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
xianwu5432 小时前
反向代理模块
linux·开发语言·网络·git