自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

圖解:進(jìn)程怎么綁定 CPU

系統(tǒng) Linux
昨天在群里有朋友問:把進(jìn)程綁定到某個(gè) CPU 上運(yùn)行是怎么實(shí)現(xiàn)的。首先,我們先來了解下將進(jìn)程與 CPU 進(jìn)行綁定的好處。

 

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

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

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

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

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

前面介紹了進(jìn)程與 CPU 綁定的好處后,現(xiàn)在來介紹一下在 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 親和性。我們來看看 sched_setaffinity 系統(tǒng)調(diào)用的原型: 

  1. 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 的類型為 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)行。

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

  1. #define _GNU_SOURCE  
  2. #include <sched.h>  
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. #include <stdlib.h>  
  6. #include <unistd.h>  
  7. #include <errno.h>  
  8. int main(int argc, char **argv)  
  9.  
  10.     cpu_set_t cpuset;  
  11.     CPU_ZERO(&cpuset);    // 初始化CPU集合,將 cpuset 置為空  
  12.     CPU_SET(2, &cpuset);  // 將本進(jìn)程綁定到 CPU2 上  
  13.     // 設(shè)置進(jìn)程的 CPU 親和性  
  14.     if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {  
  15.         printf("Set CPU affinity failed, error: %s\n", strerror(errno)); 
  16.          return -1;   
  17.     }  
  18.     return 0;  

CPU 親和性實(shí)現(xiàn)

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

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

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

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

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

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

  1. sys_sched_setaffinity()  
  2. └→ sched_setaffinity()  
  3.    └→ set_cpus_allowed()  
  4.       └→ migrate_task() 

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

  1. static int  
  2. migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)  
  3.  
  4.     struct rq *rq = task_rq(p);  
  5.     // 情況1:  
  6.     // 如果進(jìn)程還沒有在任何運(yùn)行隊(duì)列中  
  7.     // 那么只需要將進(jìn)程的 cpu 字段設(shè)置為 dest_cpu 即可  
  8.     if (!p->se.on_rq && !task_running(rq, p)) {  
  9.         set_task_cpu(p, dest_cpu);  
  10.         return 0;  
  11.     }  
  12.     // 情況2:  
  13.     // 如果進(jìn)程已經(jīng)在某一個(gè) CPU 的可運(yùn)行隊(duì)列中 
  14.     // 那么需要將進(jìn)程從之前的 CPU 可運(yùn)行隊(duì)列中遷移到新的 CPU 可運(yùn)行隊(duì)列中  
  15.     // 這個(gè)遷移過程由 migration_thread 內(nèi)核線程完成  
  16.     // 構(gòu)建進(jìn)程遷移請(qǐng)求 
  17.     init_completion(&req->done);  
  18.     req->task = p 
  19.     req->dest_cpudest_cpu = dest_cpu;  
  20.     list_add(&req->list, &rq->migration_queue);  
  21.     return 1;  

我們先來介紹一下 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ù)主要分兩種情況來將進(jìn)程綁定到某個(gè) CPU 上:

  •  情況1:如果進(jìn)程還沒有在任何 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 字段來自動(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ì)列中。遷移過程由 migration_thread 內(nèi)核線程完成,migrate_task 函數(shù)只是構(gòu)建一個(gè)進(jìn)程遷移請(qǐng)求,并通知 migration_thread 內(nèi)核線程有新的遷移請(qǐng)求需要處理。

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

  1. static int   
  2. __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu)  
  3.  
  4.     struct rq *rq_dest, *rq_src;  
  5.     int ret = 0, on_rq;  
  6.     ...  
  7.     rq_src = cpu_rq(src_cpu);    // 進(jìn)程所在的原可運(yùn)行隊(duì)列  
  8.     rq_dest = cpu_rq(dest_cpu);  // 進(jìn)程希望放置的目標(biāo)可運(yùn)行隊(duì)列  
  9.     ...  
  10.     on_rq = p->se.on_rq;  // 進(jìn)程是否在可運(yùn)行隊(duì)列中(可運(yùn)行狀態(tài))  
  11.     if (on_rq)  
  12.         deactivate_task(rq_src, p, 0);  // 把進(jìn)程從原來的可運(yùn)行隊(duì)列中刪除  
  13.     set_task_cpu(p, dest_cpu);  
  14.     if (on_rq) {  
  15.         activate_task(rq_dest, p, 0);   // 把進(jìn)程放置到目標(biāo)可運(yùn)行隊(duì)列中  
  16.         ...  
  17.     }  
  18.     ...  
  19.     return ret; 
  20.  

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

  •  把進(jìn)程從原來的可運(yùn)行隊(duì)列中刪除。
  •  把進(jìn)程放置到目標(biāo)可運(yùn)行隊(duì)列中。

其工作過程如下圖所示(將進(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)行中。

遷移過程首先將進(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ù)載不均衡問題。如 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)資料。 

 

責(zé)任編輯:龐桂玉 來源: 良許Linux
相關(guān)推薦

2010-07-21 09:32:03

Linux多核

2021-10-17 19:49:52

CPURedis緩存

2021-02-22 07:58:45

算法進(jìn)程調(diào)度

2023-04-12 15:37:31

Linux系統(tǒng)CPU

2017-02-16 19:39:29

Windows 10System進(jìn)程CPU

2021-06-15 08:02:55

Linux 進(jìn)程管理

2021-09-30 10:45:33

Linux進(jìn)程通信

2022-01-10 17:41:31

內(nèi)存結(jié)構(gòu)PostgreSQL

2021-07-15 08:00:47

系統(tǒng)性能調(diào)優(yōu)cpunuma架構(gòu)

2021-06-30 21:13:49

CPUCache數(shù)據(jù)

2010-11-26 09:25:28

2009-06-17 09:06:59

Unix系統(tǒng)資源進(jìn)程

2021-04-20 13:40:56

Epoll IO

2020-08-03 14:17:34

CPU內(nèi)存系統(tǒng)運(yùn)維

2020-10-12 14:18:15

CPU技巧代碼

2013-12-17 10:26:14

Windows XPSVCHOST

2019-09-10 08:13:33

LinuxCPU內(nèi)存

2021-09-14 10:21:13

CPU高性能服務(wù)器

2020-03-10 19:34:08

CPU虛擬化調(diào)度

2019-12-16 09:10:38

Linux中央處理器進(jìn)程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)