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  阅读(86)  评论(0编辑  收藏  举报

导航