0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

把進(jìn)程綁定到某個(gè) CPU 上運(yùn)行是怎么實(shí)現(xiàn)?

Linux愛(ài)好者 ? 來(lái)源:Linux內(nèi)核那些事 ? 作者:songsong001 ? 2021-07-02 09:55 ? 次閱讀

昨天在群里有朋友問(wèn):把進(jìn)程綁定到某個(gè) CPU 上運(yùn)行是怎么實(shí)現(xiàn)的。

首先,我們先來(lái)了解下將進(jìn)程與 CPU 進(jìn)行綁定的好處。

進(jìn)程綁定 CPU 的好處:在多核 CPU 結(jié)構(gòu)中,每個(gè)核心有各自的L1、L2緩存,而L3緩存是共用的。如果一個(gè)進(jìn)程在核心間來(lái)回切換,各個(gè)核心的緩存命中率就會(huì)受到影響。相反如果進(jìn)程不管如何調(diào)度,都始終可以在一個(gè)核心上執(zhí)行,那么其數(shù)據(jù)的L1、L2 緩存的命中率可以顯著提高。

所以,將進(jìn)程與 CPU 進(jìn)行綁定可以提高 CPU 緩存的命中率,從而提高性能。而進(jìn)程與 CPU 綁定被稱(chēng)為:CPU 親和性。

設(shè)置進(jìn)程的 CPU 親和性前面介紹了進(jìn)程與 CPU 綁定的好處后,現(xiàn)在來(lái)介紹一下在 Linux 系統(tǒng)下怎么將進(jìn)程與 CPU 進(jìn)行綁定的(也就是設(shè)置進(jìn)程的 CPU 親和性)。

Linux 系統(tǒng)提供了一個(gè)名為 sched_setaffinity 的系統(tǒng)調(diào)用,此系統(tǒng)調(diào)用可以設(shè)置進(jìn)程的 CPU 親和性。我們來(lái)看看 sched_setaffinity 系統(tǒng)調(diào)用的原型:

int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);

下面介紹一下 sched_setaffinity 系統(tǒng)調(diào)用各個(gè)參數(shù)的作用:

pid:進(jìn)程ID,也就是要進(jìn)行綁定 CPU 的進(jìn)程ID。

cpusetsize:mask 參數(shù)所指向的 CPU 集合的大小。

mask:與進(jìn)程進(jìn)行綁定的 CPU 集合(由于一個(gè)進(jìn)程可以綁定到多個(gè) CPU 上運(yùn)行)。

參數(shù) mask 的類(lèi)型為 cpu_set_t,而 cpu_set_t 是一個(gè)位圖,位圖的每個(gè)位表示一個(gè) CPU。:

例如,將 cpu_set_t 的第0位設(shè)置為1,表示將進(jìn)程綁定到 CPU0 上運(yùn)行,當(dāng)然我們可以將進(jìn)程綁定到多個(gè) CPU 上運(yùn)行。

我們通過(guò)一個(gè)例子來(lái)介紹怎么通過(guò) sched_setaffinity 系統(tǒng)調(diào)用來(lái)設(shè)置進(jìn)程的 CPU 親和性:

#define _GNU_SOURCE#include 《sched.h》#include 《stdio.h》#include 《string.h》#include 《stdlib.h》#include 《unistd.h》#include 《errno.h》int main(int argc, char **argv)

{

cpu_set_t cpuset;

CPU_ZERO(&cpuset); // 初始化CPU集合,將 cpuset 置為空

CPU_SET(2, &cpuset); // 將本進(jìn)程綁定到 CPU2 上

// 設(shè)置進(jìn)程的 CPU 親和性

if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {

printf(“Set CPU affinity failed, error: %s

”, strerror(errno));

return -1;

}

return 0;

}

CPU 親和性實(shí)現(xiàn)知道怎么設(shè)置進(jìn)程的 CPU 親和性后,現(xiàn)在我們來(lái)分析一下 Linux 內(nèi)核是怎樣實(shí)現(xiàn) CPU 親和性功能的。

本文使用的 Linux 內(nèi)核版本為 2.6.23

Linux 內(nèi)核為每個(gè) CPU 定義了一個(gè)類(lèi)型為 struct rq 的 可運(yùn)行的進(jìn)程隊(duì)列,也就是說(shuō),每個(gè) CPU 都擁有一個(gè)獨(dú)立的可運(yùn)行進(jìn)程隊(duì)列。

一般來(lái)說(shuō),CPU 只會(huì)從屬于自己的可運(yùn)行進(jìn)程隊(duì)列中選擇一個(gè)進(jìn)程來(lái)運(yùn)行。也就是說(shuō),CPU0 只會(huì)從屬于 CPU0 的可運(yùn)行隊(duì)列中選擇一個(gè)進(jìn)程來(lái)運(yùn)行,而絕不會(huì)從 CPU1 的可運(yùn)行隊(duì)列中獲取。

所以,從上面的信息中可以分析出,要將進(jìn)程綁定到某個(gè) CPU 上運(yùn)行,只需要將進(jìn)程放置到其所屬的 可運(yùn)行進(jìn)程隊(duì)列 中即可。

下面我們來(lái)分析一下 sched_setaffinity 系統(tǒng)調(diào)用的實(shí)現(xiàn),sched_setaffinity 系統(tǒng)調(diào)用的調(diào)用鏈如下:

sys_sched_setaffinity()

└→ sched_setaffinity()

└→ set_cpus_allowed()

└→ migrate_task()

從上面的調(diào)用鏈可以看出,sched_setaffinity 系統(tǒng)調(diào)用最終會(huì)調(diào)用 migrate_task 函數(shù)來(lái)完成進(jìn)程與 CPU 進(jìn)行綁定的工作,我們來(lái)分析一下 migrate_task 函數(shù)的實(shí)現(xiàn):

static int

migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)

{

struct rq *rq = task_rq(p);

// 情況1:

// 如果進(jìn)程還沒(méi)有在任何運(yùn)行隊(duì)列中

// 那么只需要將進(jìn)程的 cpu 字段設(shè)置為 dest_cpu 即可

if (!p-》se.on_rq && !task_running(rq, p)) {

set_task_cpu(p, dest_cpu);

return 0;

}

// 情況2:

// 如果進(jìn)程已經(jīng)在某一個(gè) CPU 的可運(yùn)行隊(duì)列中

// 那么需要將進(jìn)程從之前的 CPU 可運(yùn)行隊(duì)列中遷移到新的 CPU 可運(yùn)行隊(duì)列中

// 這個(gè)遷移過(guò)程由 migration_thread 內(nèi)核線(xiàn)程完成

// 構(gòu)建進(jìn)程遷移請(qǐng)求

init_completion(&req-》done);

req-》task = p;

req-》dest_cpu = dest_cpu;

list_add(&req-》list, &rq-》migration_queue);

return 1;

}

我們先來(lái)介紹一下 migrate_task 函數(shù)各個(gè)參數(shù)的意義:

p:要設(shè)置 CPU 親和性的進(jìn)程描述符。

dest_cpu:綁定的 CPU 編號(hào)。

req:進(jìn)程遷移請(qǐng)求對(duì)象(下面會(huì)介紹)。

所以,migrate_task 函數(shù)的作用就是將進(jìn)程描述符為 p 的進(jìn)程綁定到編號(hào)為 dest_cpu 的目標(biāo) CPU 上。

migrate_task 函數(shù)主要分兩種情況來(lái)將進(jìn)程綁定到某個(gè) CPU 上:

情況1:如果進(jìn)程還沒(méi)有在任何 CPU 的可運(yùn)行隊(duì)列中(不可運(yùn)行狀態(tài)),那么只需要將進(jìn)程描述符的 cpu 字段設(shè)置為 dest_cpu 即可。當(dāng)進(jìn)程變?yōu)榭蛇\(yùn)行時(shí),會(huì)根據(jù)進(jìn)程描述符的 cpu 字段來(lái)自動(dòng)放置到對(duì)應(yīng)的 CPU 可運(yùn)行隊(duì)列中。

情況2:如果進(jìn)程已經(jīng)在某個(gè) CPU 的可運(yùn)行隊(duì)列中,那么需要將進(jìn)程從之前的 CPU 可運(yùn)行隊(duì)列中遷移到新的 CPU 可運(yùn)行隊(duì)列中。遷移過(guò)程由 migration_thread 內(nèi)核線(xiàn)程完成,migrate_task 函數(shù)只是構(gòu)建一個(gè)進(jìn)程遷移請(qǐng)求,并通知 migration_thread 內(nèi)核線(xiàn)程有新的遷移請(qǐng)求需要處理。

而進(jìn)程遷移過(guò)程由 __migrate_task 函數(shù)完成,我們來(lái)看看 __migrate_task 函數(shù)的實(shí)現(xiàn):

static int

__migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)

{

struct rq *rq_dest, *rq_src;

int ret = 0, on_rq;

。。。

rq_src = cpu_rq(src_cpu); // 進(jìn)程所在的原可運(yùn)行隊(duì)列

rq_dest = cpu_rq(dest_cpu); // 進(jìn)程希望放置的目標(biāo)可運(yùn)行隊(duì)列

。。。

on_rq = p-》se.on_rq; // 進(jìn)程是否在可運(yùn)行隊(duì)列中(可運(yùn)行狀態(tài))

if (on_rq)

deactivate_task(rq_src, p, 0); // 把進(jìn)程從原來(lái)的可運(yùn)行隊(duì)列中刪除

set_task_cpu(p, dest_cpu);

if (on_rq) {

activate_task(rq_dest, p, 0); // 把進(jìn)程放置到目標(biāo)可運(yùn)行隊(duì)列中

。。。

}

。。。

return ret;

}

__migrate_task 函數(shù)主要完成以下兩個(gè)工作:

把進(jìn)程從原來(lái)的可運(yùn)行隊(duì)列中刪除。

把進(jìn)程放置到目標(biāo)可運(yùn)行隊(duì)列中。

其工作過(guò)程如下圖所示(將進(jìn)程從 CPU0 的可運(yùn)行隊(duì)列遷移到 CPU3 的可運(yùn)行隊(duì)列中):

如上圖所示,進(jìn)程原本在 CPU0 的可運(yùn)行隊(duì)列中,但由于重新將進(jìn)程綁定到 CPU3,所以需要將進(jìn)程從 CPU0 的可運(yùn)行隊(duì)列遷移到 CPU3 的可運(yùn)行中。

遷移過(guò)程首先將進(jìn)程從 CPU0 的可運(yùn)行隊(duì)列中刪除,然后再將進(jìn)程插入到 CPU3 的可運(yùn)行隊(duì)列中。

當(dāng) CPU 要運(yùn)行進(jìn)程時(shí),首先從它所屬的可運(yùn)行隊(duì)列中挑選一個(gè)進(jìn)程,并將此進(jìn)程調(diào)度到 CPU 中運(yùn)行。

總結(jié)從上面的分析可知,其實(shí)將進(jìn)程綁定到某個(gè) CPU 只是將進(jìn)程放置到 CPU 的可運(yùn)行隊(duì)列中。

由于每個(gè) CPU 都有一個(gè)可運(yùn)行隊(duì)列,所以就有可能會(huì)出現(xiàn) CPU 間可運(yùn)行隊(duì)列負(fù)載不均衡問(wèn)題。如 CPU0 可運(yùn)行隊(duì)列中的進(jìn)程比 CPU1 可運(yùn)行隊(duì)列多非常多,從而導(dǎo)致 CPU0 的負(fù)載非常高,而 CPU1 負(fù)載非常低的情況。

當(dāng)出現(xiàn)上述情況時(shí),就需要對(duì) CPU 間的可運(yùn)行隊(duì)列進(jìn)行重平衡操作,有興趣的可以自行閱讀源碼或參考相關(guān)資料。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1336

    瀏覽量

    40082
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10698

    瀏覽量

    209324
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207889

原文標(biāo)題:圖解:進(jìn)程怎么綁定 CPU

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    rk3588s 怎么將gpio中斷綁定其它cpu

    目前了解只有GIC的中斷才能用這種:echo 2 > /proc/irq/102/smp_affinity, 方式綁定cpu,而GPIO這種是用不了這種方式綁定的。 想問(wèn)下有其
    發(fā)表于 07-16 15:09

    nginx重啟命令linux步驟是什么?

    worker進(jìn)程,不可能處理其它進(jìn)程的請(qǐng)求。   worker進(jìn)程的個(gè)數(shù)是可以設(shè)置的,一般我們會(huì)設(shè)置與機(jī)器cpu核數(shù)一致。同時(shí),nginx為了更好的利用多核特性,具有
    發(fā)表于 07-11 17:13

    nginx重啟命令linux步驟是什么?

    worker進(jìn)程,不可能處理其它進(jìn)程的請(qǐng)求。   worker進(jìn)程的個(gè)數(shù)是可以設(shè)置的,一般我們會(huì)設(shè)置與機(jī)器cpu核數(shù)一致。同時(shí),nginx為了更好的利用多核特性,具有
    發(fā)表于 07-10 16:40

    ESP32S3的差分OTA升級(jí),有沒(méi)有方法可以用戶(hù)固定某個(gè)位置,或者用戶(hù)程序放到最后?

    我想做一個(gè)ESP32 S3 的差分OTA升級(jí),但發(fā)現(xiàn)ESP-IDF編譯生成的BIN文件用戶(hù)程序放在前面,ESP32的庫(kù)文件放在后面。這樣即使改動(dòng)很小的做出的差分程序也很大。有沒(méi)有方法可以用戶(hù)固定
    發(fā)表于 06-07 07:18

    STM32F4的裸機(jī)源碼可以移植linux ARM運(yùn)行嗎? 具體需要怎么實(shí)現(xiàn)呢?

    STM32F4的裸機(jī)源碼可以移植linux ARM開(kāi)發(fā)板運(yùn)行嗎? 具體需要怎么實(shí)現(xiàn)呢?
    發(fā)表于 03-20 07:00

    鴻蒙OS跨進(jìn)程IPC與RPC通信

    一、IPC與RPC通信概述 基本概念 IPC(Inter-Process Communication)與RPC(Remote Procedure Call)用于實(shí)現(xiàn)進(jìn)程通信,不同的是前者
    發(fā)表于 02-17 14:20

    請(qǐng)問(wèn)tc233 Uart收發(fā)數(shù)據(jù)如何綁定DMA?

    請(qǐng)問(wèn)tc233 Uart收發(fā)數(shù)據(jù)如何綁定DMA?綁定DMA之后中斷觸發(fā)還是原來(lái)的 IfxCpu_Irq_installInterruptHandler()這個(gè)函數(shù)里
    發(fā)表于 01-22 06:27

    線(xiàn)程、進(jìn)程、多線(xiàn)程、多進(jìn)程和多任務(wù)之間有何關(guān)系?

    進(jìn)程是程序執(zhí)行時(shí)的一個(gè)實(shí)例,即它是程序已經(jīng)執(zhí)行課中程度的數(shù)據(jù)結(jié)構(gòu)的匯集。從內(nèi)核的觀點(diǎn)看,進(jìn)程的目的就是擔(dān)當(dāng)分配系統(tǒng)資源(CPU時(shí)間、內(nèi)存等)的基本單位。
    的頭像 發(fā)表于 01-11 13:39 ?288次閱讀
    線(xiàn)程、<b class='flag-5'>進(jìn)程</b>、多線(xiàn)程、多<b class='flag-5'>進(jìn)程</b>和多任務(wù)之間有何關(guān)系?

    linux查看weblogic進(jìn)程

    在Linux操作系統(tǒng)中,WebLogic是一種常用的Java應(yīng)用服務(wù)器,用于部署和管理企業(yè)級(jí)Java應(yīng)用程序。為了確保WebLogic服務(wù)器正常運(yùn)行,有時(shí)我們需要查看WebLogic進(jìn)程以了解其狀態(tài)
    的頭像 發(fā)表于 12-05 16:07 ?1492次閱讀

    Linux內(nèi)核驅(qū)動(dòng)與單個(gè)PCI設(shè)備的綁定和解綁定

    在Linux內(nèi)核2.6.13-rc3以前,驅(qū)動(dòng)和設(shè)備之間的綁定和解綁只能通過(guò)insmod(modprobe)和rmmod來(lái)實(shí)現(xiàn),但是這種實(shí)現(xiàn)方法有一個(gè)弊端,就是一旦綁定或者解
    的頭像 發(fā)表于 11-17 17:11 ?1312次閱讀
    Linux內(nèi)核驅(qū)動(dòng)與單個(gè)PCI設(shè)備的<b class='flag-5'>綁定</b>和解<b class='flag-5'>綁定</b>

    進(jìn)程和線(xiàn)程的區(qū)別

    1.什么是進(jìn)程?為什么要有進(jìn)程? 進(jìn)程有一個(gè)相當(dāng)精簡(jiǎn)的解釋?zhuān)?b class='flag-5'>進(jìn)程是對(duì)操作系統(tǒng)正在運(yùn)行程序的一個(gè)
    的頭像 發(fā)表于 11-11 16:46 ?658次閱讀
    <b class='flag-5'>進(jìn)程</b>和線(xiàn)程的區(qū)別

    Linux系統(tǒng)多線(xiàn)程和多進(jìn)程運(yùn)行效率

    關(guān)于多進(jìn)程和多線(xiàn)程,教科書(shū)上最經(jīng)典的一句話(huà)是“進(jìn)程是資源分配的最小單位,線(xiàn)程是CPU調(diào)度的最小單位”,這句話(huà)應(yīng)付考試基本夠了,但如果在工作中遇到類(lèi)似的選擇問(wèn)題,那就沒(méi)有這么簡(jiǎn)單了,選
    的頭像 發(fā)表于 11-10 10:54 ?1056次閱讀
    Linux系統(tǒng)<b class='flag-5'>上</b>多線(xiàn)程和多<b class='flag-5'>進(jìn)程</b>的<b class='flag-5'>運(yùn)行</b>效率

    什么是進(jìn)程

    組織形式的描述,進(jìn)程是程序的實(shí)體。進(jìn)程是一個(gè)具有獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它可以申請(qǐng)和擁有系統(tǒng)資源,是一個(gè)動(dòng)態(tài)的概念,是一個(gè)活動(dòng)的實(shí)體。它不只是程序的代碼,還包括當(dāng)
    的頭像 發(fā)表于 11-08 15:21 ?3206次閱讀
    什么是<b class='flag-5'>進(jìn)程</b>

    labview 數(shù)據(jù)綁定求助

    求助 LABVIEW 大神 在每個(gè)控件屬性中有一個(gè)數(shù)據(jù)綁定綁定本機(jī)的OPC服務(wù)器 是正??梢杂玫娜绻蚁氚堰@個(gè)綁定路徑做成根我提供的
    發(fā)表于 10-24 09:19

    PCB進(jìn)程的創(chuàng)建和終止過(guò)程

    PCB進(jìn)程控制塊 獨(dú)立運(yùn)行基本單位的標(biāo)志:創(chuàng)建進(jìn)程時(shí)創(chuàng)建PCB,進(jìn)程結(jié)束時(shí)回PCB,進(jìn)程隨之消亡。系統(tǒng)是通過(guò)PCB,感知
    的頭像 發(fā)表于 10-08 15:36 ?970次閱讀