QNX-19—QNX绑核优先级-1-理论
一、Thread affinity翻译
翻译:
QNX Software Development Platform --> Programming --> Programmer's Guide --> Multicore Processing --> The impact of multicore
https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.prog/topic/multicore_thread_affinity.html
多核环境中经常出现的一个问题可以这样表述:“我可以让一个处理器处理 GUI,另一个处理器处理数据库,另外两个处理器处理实时功能吗?”
答案是:“当然可以。”
这是通过线程亲和性实现的,线程亲和性是指将某些程序(甚至是程序中的线程)与特定处理器或处理器相关联的能力。
线程亲和性的工作原理如下。当线程启动时,其亲和性掩码(或运行掩码)被设置为允许它在所有处理器上运行。这意味着线程亲和性掩码没有继承,因此由线程使用带有 _NTO_TCTL_RUNMASK 控制标志的 ThreadCtl() 来设置其运行掩码:
if (ThreadCtl( _NTO_TCTL_RUNMASK, (void *)my_runmask) == -1) { /* An error occurred. */ }
运行掩码只是一个位图;每个位位置表示一个特定的处理器。例如,运行掩码 0x05(二进制 00000101)允许线程在处理器 0(0x01 位)和 2(0x04 位)上运行。
如果您使用 _NTO_TCTL_RUNMASK,则运行掩码的大小限制为 int(当前为 32 位)。默认被创建的线程不会继承运行掩码。#######
如果您想要支持比 int 中容纳的更多的处理器,或者想要设置继承掩码,则需要使用下面描述的 _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT 命令。
<sys/neutrino.h> 文件定义了一些可用于处理运行掩码的宏:
RMSK_SET(cpu, p) //在 p 指向的掩码中设置 cpu 的位。 RMSK_CLR(cpu, p) //清除 p 指向的掩码中 cpu 的位。 RMSK_ISSET(cpu, p) //确定 p 指向的掩码中 cpu 的位是否已设置。
CPU 从 0 开始编号。这些宏适用于任意长度的运行掩码。
绑定多处理 (BMP) 是 SMP 的一种变体,可让您指定进程或线程及其子进程可以在哪些处理器上运行。要指定这一点,请使用继承掩码。
要设置线程的继承掩码,请使用带有 _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT 控制标志的 ThreadCtl()。从概念上讲,使用此命令传递的结构如下:
struct _thread_runmask { int size; unsigned runmask[size]; unsigned inherit_mask[size]; };
如果将 runmask 成员设置为非零值,ThreadCtl() 会将调用线程的运行掩码设置为指定值。如果将 runmask 成员设置为零,则调用线程的运行掩码不会改变。#######
如果将 inherit_mask 成员设置为非零值,ThreadCtl() 会将调用线程的继承掩码设置为指定值;如果调用线程通过调用 pthread_create()、fork()、posix_spawn()、spawn() 或 exec() 创建任何子线程,则子线程会继承此掩码。####### 如果将 inherit_mask 成员设置为零,则调用线程的继承掩码不会改变。//TODO: 子线程创建的子线程会不会继承此掩码?
如果查看 <sys/neutrino.h> 中 _thread_runmask 的定义,您会发现它实际上是这样声明的:
struct _thread_runmask { int size; /* unsigned runmask[size]; */ /* unsigned inherit_mask[size]; */ };
这是因为 runmask 和 inherit_mask 数组中的元素数量取决于多核系统中处理器的数量。您可以使用 RMSK_SIZE() 宏来确定掩码需要多少个无符号整数;将 CPU 数量(可在系统页面中找到)传递给此宏。
以下是一段代码片段,展示了如何设置 runmask 和 inherit mask:
unsigned num_elements = 0; int *rsizep, masksize_bytes, size; unsigned *rmaskp, *imaskp; void *my_data; /* Determine the number of array elements required to hold the runmasks, based on the number of CPUs in the system. */ num_elements = RMSK_SIZE(_syspage_ptr->num_cpu); //TODO: 看是CPU个数,还是CPU个数对应的bit位对应的字节数? ######### /* Determine the size of the runmask, in bytes. */ masksize_bytes = num_elements * sizeof(unsigned); /* Allocate memory for the data structure that we'll pass * to ThreadCtl(). We need space for an integer (the number * of elements in each mask array) and the two masks * (runmask and inherit mask). */ size = sizeof(int) + 2 * masksize_bytes; if ((my_data = malloc(size)) == NULL) { /* Not enough memory. */ … } else { memset(my_data, 0x00, size); /* Set up pointers to the "members" of the structure. */ rsizep = (int *)my_data; rmaskp = rsizep + 1; imaskp = rmaskp + num_elements; /* Set the size. */ *rsizep = num_elements; /* Set the runmask. Call this macro once for each processor the thread can run on. */ RMSK_SET(cpu1, rmaskp); /* Set the inherit mask. Call this macro once for each processor the thread's children can run on. */ RMSK_SET(cpu1, imaskp); if ( ThreadCtl( _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT, my_data) == -1) { /* Something went wrong. */ … } }
如果您要启动一个新进程,您可以通过以下方式指定其运行掩码:
(1) 调用 posix_spawnattr_setrunmask(),使用 posix_spawnattr_setxflags() 设置 POSIX_SPAWN_EXPLICIT_CPU 扩展标志,然后调用 posix_spawn()
或者:
(2) 设置 inheritance 结构的运行掩码成员并在调用 spawn() 时指定 SPAWN_EXPLICIT_CPU 标志。
您还可以使用 on 命令的 -C 和 -R 选项启动带有运行掩码的进程(假设它们没有以编程方式设置运行掩码);例如,使用 on -C 1 io-pkt-v4-hc 启动 io-pkt-v4-hc 并将所有线程锁定到 CPU 1。此命令同时设置运行掩码和继承掩码。
您还可以使用 slay 命令的相同选项来修改正在运行的进程或线程的运行掩码。例如,slay -C 0 io-pkt-v4-hc 将 io-pkt-v4-hc 的所有线程移动到 CPU 0 上运行。如果您使用 -C 和 -R 选项,slay 会设置运行掩码;如果您还使用 -i 选项,slay 还会将进程或线程的继承掩码设置为与运行掩码相同。
二、设置优先级
A few examples 翻译:
https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.getting_started/topic/s1_procs_Examples.html
QNX Software Development Platform --> Programming --> Getting Started with QNX Neutrino --> Processes and ThreadsThreads and processes --> Starting a thread
让我们看一些例子。我们假设已经包含了正确的包含文件(<pthread.h> 和 <sched.h>),并且要创建的线程名为 new_thread(),并且已正确原型化和定义。
创建线程的最常见方法是简单地让其保持默认值:
pthread_create (NULL, NULL, new_thread, NULL);
在上面的例子中,我们使用默认值创建了新线程,并向其传递了一个 NULL 作为其唯一参数(这是上面 pthread_create() 调用中的第三个 NULL)。
通常,您可以将任何您想要的内容(通过 arg 字段)传递给新线程。这里我们传递的是数字 123:
pthread_create (NULL, NULL, new_thread, (void *) 123);
一个更复杂的例子是创建一个优先级为 15 的循环调度的不可连接线程:
pthread_attr_t attr; // initialize the attribute structure pthread_attr_init (&attr); // set the detach state to "detached" pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); // override the default of INHERIT_SCHED pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy (&attr, SCHED_RR); attr.param.sched_priority = 15; //实测这个是不行的 // finally, create the thread pthread_create (NULL, &attr, new_thread, NULL);
这里创建线程并指定其优先级。
要查看多线程程序“是什么样子”,可以从 shell 运行 pidin 命令。假设我们的程序名为 spud。如果我们在 spud 创建线程之前运行一次 pidin,在 spud 创建另外两个线程(总共三个)之后运行一次 pidin,则输出将如下所示(我已缩短 pidin 输出以仅显示 spud):
# pidin pid tid name prio STATE Blocked 12301 1 spud 10r READY # pidin pid tid name prio STATE Blocked 12301 1 spud 10r READY 12301 2 spud 10r READY 12301 3 spud 10r READY
如您所见,进程 spud(进程 ID 12301)有三个线程(在“tid”列下)。这三个线程以优先级 10 运行,调度算法为循环(由 10 后面的“r”表示)。所有三个线程都处于 READY 状态,这意味着它们可以使用 CPU,但当前未在 CPU 上运行(另一个优先级更高的线程当前正在运行)。
现在我们已经了解了有关创建线程的所有信息,让我们看看如何以及在何处使用它们。
posted on 2024-06-04 15:17 Hello-World3 阅读(512) 评论(0) 编辑 收藏 举报