一、线程分离属性
- 线程的分离属性有两种:分离态(detached)和非分离态(joinable)。
- 非分离态的线程只有在调用pthread_join之后才会完全释放自己所占用的资源,可以用pthread_detach函数将线程修改为分离态,分离态的线程在线程结束后由系统自动释放其所占用的所有资源;
- 线程创建后默认属性是非分离态,需要调用pthread_join释放线程所占用的资源;
二、线程的栈大小
- 线程的栈空间可以使用pthread_attr_setstacksize函数设置
三、线程的栈地址
- 通过pthread_attr_setstack和
pthread_attr_getstack两个函数分别设置和获取线程的栈地址。 - 当进程空间不够用时,可以使用这个函数指定线程的栈地址;
- 设置的栈地址指向的空间大小不能小于PTHREAD_STACK_MIN;
四、栈尾警戒区
线程属性guardsize控制着线程栈末尾以后用以避免栈溢出的扩展内存的大小,这个属性默认是PAGESIZE个字节。
五、线程的调度策略和优先级
- 线程的调度策略决定了cpu接下来要执行哪一个线程,调度器根据系统上所有线程的调度策略和静态优先级来决定如何进行调度。
- linux调度器会为每一个优先级维持一张就绪态线程列表,调度器会优先调度具有最高优先级的非空列表中位于最前面的就绪态线程,比如一个进程中有1、3、5三个优先级的线程列表,那么调度器会优先查找优先级为5的线程列表并运行位于最前面的就绪态线程;
- 对于使用普通调度策略(SCHED_OTHER, SCHED_IDLE, SCHED_BATCH)的线程来说,sched_priority并不会影响调度结果,且必须设置为0。
5.1、实时调度策略和非实时调度策略
实时调度策略
- SCHED_FIFO 先进先出( first-in, first-out policy)
- SCHED_RR 轮询(Round-Robin Scheduling)
- 对于使用实时策略(SCHED_FIFO,SCHED_RR)的进程,其优先级的取值为1到99(1为最低值)。实时线程的调度优先级总是高于普通线程。POSIX.1的系统在实现中,会要求实时调度策略有32个优先级设置,因此,为了可移植性,可以使用sched_get_priority_min和sched_get_priority_max来查找调度策略所支持的优先级范围。
- SCHED_FIFO的线程会一直运行,直到它被IO阻塞(如调用了休眠函数)、被其它高优先级的线程抢占或者主动调用了pthread_yield;
- SCHED_FIFO调度策略的优先级必须大于0,是一种不依赖于时间片的调度算法,这种调度算法适用于以下规则:
- [1] 具有SCHED_FIFO属性的线程如果被高优先级的线程抢占,调度器会将其放在其优先级列表的最前面,当所有更高优先级的线程运行结束后该线程会立即运行。
- [2] 一个具有SCHED_FIFO属性的线程如果由其它状态编程就绪态(runnable),它会被放倒优先级列表的末尾,即在同等优先级下它会被最后调度;
- [3]调用sched_yield函数会将该线程放在优先级列表的末尾;
- [4]如果调用sched_setscheduler()或者 sched_setparam改变线程的调度方式,调度器会将其放在优先级列表的最前面,并且该线程有可能会抢占其它正在运行的同等优先级的线程;
- 上述规则同样适用于SCHED_RR,不同的是SCHED_RR可以指定一个最大运行时间,当线程运行超过指定的时间,调度器会将其置于优先级列表的末尾。如果具有SHSED_RR属性的线程被其它高优先级的线程抢占,那么该线程会保存其运行时间,在更高优先级的线程执行完毕之后它会继续执行剩余的时间。
非实时调度策略
- SCHED_OTHER 轮询调度算法(Round-Robin Scheduling)
- SCHED_BATCH
- SCHED_IDLE
1.SCHED_OTHER是linux默认的线程调度策略,它的静态优先级必须为0,这种调度方式基于CFS调度(完全公平调度算法)。关于CSF调度方式请参考(https://www.cnblogs.com/tianguiyu/articles/6091378.html)
- linux操作系统中使用nice值表示SCHED_OTHER类线程在执行列表中的权重,取值范围从-20~+19,值越大表示优先级越低。
- SCHED_BATCH可以用于静态优先级为0的线程。该策略类似SCHED_OTHER,并根据动态优先级(nice值)进行调度。区别是使用该策略时,调度器会假设线程是CPU密集型的,因此,该调度器会根据线程的唤醒行为施加调度惩罚,因此这种调度策略比较不受欢迎。
- SCHED_IDLE的静态优先级必须为0,并且nice值对这样的线程没有作用。这种调度方式的优先级极低,甚至低于nice值为19的线程;
六、线程属性相关函数:
1.pthread_attr_setdetachstate设置分离属性
函数原型:int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
函数功能:设置线程的分离属性
函数参数:attr 需要设置的线程参数
detachstate 线程的分离属性, PTHREAD_CREATE_DETACHED设置线程为分离态,PTHREAD_CREATE_JOINABLE设置线程的属性为非分离态。
返回值:成功返回0,失败返回错误码
2.pthread_attr_getdetachstate获取线程的分离属性
函数原型:int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
函数功能:获取线程的分离属性
3.pthread_attr_setstacksize设置线程栈空间
函数原型:int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
函数功能:设置线程的栈大小
函数参数:attr 需要设置的线程参数
stacksize 线程栈空间大小
返回值:成功返回0,失败返回错误码
4.pthread_attr_getstacksize获取线程栈空间
函数原型:int pthread_attr_getstacksize(pthread_attr_t *attr, size_t stacksize);
5.pthread_attr_setstackaddr设置栈空间起始地址
函数原型:int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);
函数说明:设置和获取线程栈空间的起始地址,设置栈空间时stackaddr指向的地址空间不能小于PTHREAD_STACK_MIN,并且具有可读可写的权限;
6.pthread_attr_setstack和pthread_attr_getstack
函数原型:int pthread_attr_setstack(pthread_attr_t *attr,void *stackaddr, size_t stacksize);
int pthread_attr_getstack(pthread_attr_t *attr,void **stackaddr, size_t *stacksize);
函数功能:设置/获取线程的栈起始地址和栈大小
7.pthread_setschedparam和pthread_getschedparam
函数原型:int pthread_setschedparam(pthread_t thread, int policy,const struct sched_param *param);
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);
函数功能:设置/获取线程的调度策略相关参数
函数参数:thread 线程id
param 调度参数,原型为:
struct sched_param {
int sched_priority; /* Scheduling priority */
};
示例:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
pthread_attr_t g_attr;
pthread_t tid1,tid2;
void print_shed_info(char *ptr)
{
struct sched_param pri;
int policy;
sleep(1);
pthread_getschedparam(pthread_self(),&policy,&pri);
if(policy == SCHED_OTHER)
{
printf("%s shed policy:SCHED_OTHER\r\n",ptr);
}
else if(policy == SCHED_FIFO )
{
printf("%s shed policy:SCHED_FIFO\r\n",ptr);
}
else if(policy == SCHED_RR )
{
printf("%s shed policy:SCHED_RR\r\n",ptr);
}
else
{
printf("%s shed unknow\r\n",ptr);
}
printf("%s prority:%d\r\n",ptr,pri.sched_priority);
}
void *pthread1(void *param)
{
int value;
print_shed_info("thread1");
while(1)
{
break;
}
}
void *pthread2(void *param)
{
int ret = -1;
print_shed_info("thread2");
while(1)
{
break;
}
}
int main()
{
int ret = -1;
unsigned int stacksize = 0;
struct sched_param pri;
int detachstate;
void* stackaddr;
pri.sched_priority = 10;
pthread_attr_init(&g_attr);
pthread_attr_getstackaddr(&g_attr,&stackaddr);
pthread_attr_getstacksize(&g_attr,&stacksize);
pthread_attr_getdetachstate(&g_attr, &detachstate);
printf("thread stackaddr:%u\r\n",stackaddr);
printf("thread stacksize:%u bytes\r\n",stacksize);
if (detachstate == PTHREAD_CREATE_DETACHED)
printf("thread detached\n");
else if (detachstate == PTHREAD_CREATE_JOINABLE)
printf("thread join\n");
else
printf("thread unknown\n");
//设置进程调度方式和优先级,所有线程的默认调度方式和优先级同进程
if(0 != sched_setscheduler(getpid(),SCHED_FIFO,&pri))
{
printf("process shed policy set fail\r\n");
}
pthread_create(&tid1,NULL,pthread1,NULL);//线程attr为NULL表示使用系统默认属性
pthread_create(&tid2,NULL,pthread2,NULL);
//设置线程调度策略
pri.sched_priority = 5;
if(0 != pthread_setschedparam(tid1,SCHED_RR ,&pri))
printf("thread shed policy set fail\r\n");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
运行结果:
注意:
- 修改线程和进程的调度策略必须以管理员权限运行,即sudo ./a.out
- 如果进程调度方式为实时调度策略,则线程的调度策略也必须是实时调度方式,反之也成立。