按需求设计特殊线程
线程是如何运行的
线程是内核调度的最小单元,每一个线程创建都会在内核实例出一个thread_info结构体,结构体中有一个task_struct结构体。这两个结构体描述着线程的信息。当中断触发调度时,schedule函数开始在几种不同类型的线程之间进行调度选择,在相同类型的线程之间进行调度算法选择。task_struct中描述着线程的类型,和调度方法,等各种信息。
详细可参考:
https://blog.csdn.net/weixin_42427338/article/details/89323122
https://blog.csdn.net/yangming2466/article/details/83280643
什么决定线程的运行的顺序
线程的优先级决定着线程运行的顺序,不同的线程优先级与调度算法不同。
实时进程
实时调度算法SCHED_FIFO和SCHED_RR。使用top命令,如果PR列的值为RT,则说明该进程采用的是实时策略,即调度策略是SCHED_FIFO或者为SCHED_RR,而对于非实时调度策略(比如SCHED_OTHER)的进程,该列的值是NI+20,以供Linux内核使用。我们可以通过命令:
ps -eo state,uid,pid,ppid,rtprio,time,comm
来查看进程对应的实时优先级(位于RTPRIO列下),如果有进程对应的列显示“-”,则说明它不是实时进程。注意任何实时策略进程的优先级都高于普通的进程,也就说实时优先级和nice优先级处于互不相交的两个范畴。
SCHED_FIFO:
先入先出调度策略(First in-first out scheduling)。该策略简单的说就是一旦线程占用cpu则一直运行,一直运行直到有更高优先级任务到达或自己放弃。
SCHED_RR:
时间片轮转调度(Round-robin scheduling)。该策略是SCHED_FIFO基础上改进来的,他给每个线程增加了一个时间片限制,当时间片用完后,系统将把该线程置于队列末尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。此调度会比普通进程的时间片要长。
普通进程
大多数的普通进程,用的就是CFS算法
CFS:
自行百度
小结
实时线程的优先级肯定高于普通线程。调度算法会在运行队列中先调取实时进程来执行,而后才轮到普通进程。我们可根据任务的类型选择索要开创的线程是SCHED_FIFO还是SCHED_RR或是一个普通线程。
代码展示线程
在线程中更改调度算法
C语言代码。c++的话可以自己去封装一个线程类
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
void * threadFun(void *)
{
printf("the thread init scheduler = %d \n",sched_getscheduler(0));
// 尽可能高的实时优先级
struct sched_param my_params;
my_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sched_setscheduler(0,SCHED_FIFO,&my_params) < 0) {
printf("threadFun sched_setscheduler fail\n");
}
while (1) {
printf("the thread scheduler = %d \n",sched_getscheduler(0));
sleep(1);
}
return nullptr;
}
int main()
{
// 获取当前线程的调度算法
printf("the main init scheduler = %d \n",sched_getscheduler(0));
pthread_t id;
pthread_attr_t attr; // 定义属性变量
pthread_attr_init(&attr); // 初始化
// 设置线程为分离属性,不用join回收资源
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
// 设置线程继承属性为手动设置调度策略,否则调度策略继承父亲的
pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
/* *** 设置调度策略,必须得先设置继承属性 *** */
struct sched_param my_params;
my_params.sched_priority = sched_get_priority_max(SCHED_RR);
// 设置调度策略
pthread_attr_setschedpolicy(&attr, SCHED_RR);
// 设置策略优先级参数
pthread_attr_setschedparam(&attr, &my_params);
printf("get sched_priority is %d\n", my_params.sched_priority);
if (0 != pthread_create(&id, &attr, threadFun, nullptr)) {
printf("set policy fail\n");
}
while (1) {
printf("the main scheduler = %d \n",sched_getscheduler(0));
sleep(1);
}
}
程序输出(程序执行时要加 sudo)
the main init scheduler = 0 // 主线程初始为一个普通线程
get sched_priority is 99
the main scheduler = 0
the thread init scheduler = 2 // 线程被我们以SCHED_RR策略创建
the thread scheduler = 1 // 线程自己动态修改调度策略SCHED_FIFO
the thread scheduler = 1
the main scheduler = 0
the thread scheduler = 1
the main scheduler = 0
the thread scheduler = 1
普通进程通过nice值来设置优先级
ps 命令简介
输入ps -l 如下
ps命令各个参数的含有详情可自己搜索这里只简单介绍。此实验使用
ps -l -p (查看指定线程的信息)
UID:启动这些进程的用户。
PID:进程的进程ID。
PPID:父进程的进程号(图中的终端就是bash,在此终端运行的程序的ppid都是他的pid:5644)。
C:进程生命周期中的CPU利用率。
STIME:进程启动时的系统时间。
TTY:进程启动时的终端设备。
TIME:运行进程需要的累计CPU时间。
CMD:启动的程序名称。
F:内核分配给进程的系统标记。
S:进程的状态(O代表正在运行;S代表在休眠;R代表可运行,正等待运行;Z代表僵化,进程已结束但父进程已不存在;T代表停止)。
PRI:进程的优先级(越大的数字代表越低的优先级)。
NI:谦让度值用来参与决定优先级。
ADDR:进程的内存地址。
SZ:假如进程被换出,所需交换空间的大致大小。
WCHAN:进程休眠的内核函数的地址。
实时进程和普通进程设置nice值后得变化
在LINUX系统中,Nice值的范围从-20到+19(不同系统的值范围是不一样的),正值表示低优先级,负值表示高优先级,值为零则表示不会调整该进程的优先级。具有最高优先级的程序,其nice值最低,所以在LINUX系统中,值-20使得一项任务变得非常重要;与之相反,如果任务的nice为+19,则表示它是一个高尚的、无私的任务,允许所有其他任务比自己享有宝贵的CPU时间的更大使用份额,这也就是nice的名称的来意。
int main()
{
int a = getpid();
printf("pid is %d \n", getpid());
std::string comd1 = "renice -n 2 -p " + std::to_string(a);
std::string comd2 = "renice -n -6 -p " + std::to_string(a);
// 暂停住程序
printf("初始默认进程\n");
getchar();
// 修改 nice 值 2
system(comd1.c_str());
// 暂停住程序
printf("修改nice值为2\n");
getchar();
// 设置线程为实时线程后修改nice值
struct sched_param my_params;
my_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (sched_setscheduler(0,SCHED_FIFO,&my_params) < 0) {
printf("sched_setscheduler fail\n");
}
// 暂停住程序
printf("修改线程为实时线程\n");
getchar();
// 修改 nice 值 -6
system(comd2.c_str());
printf("修改实时线程nice值为-6\n");
while (true);
}
程序运行(必须有系统权限):
程序输出:
小结
由此可看出,普通进程的nice值,过代码中系统调用修改后可改变优先级PRI,但是实时进程修改nice值不改变。原因是因为进程不同优先级算法不同