成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

圖解:進程怎么綁定 CPU

系統 Linux
昨天在群里有朋友問:把進程綁定到某個 CPU 上運行是怎么實現的。首先,我們先來了解下將進程與 CPU 進行綁定的好處。

 

昨天在群里有朋友問:把進程綁定到某個 CPU 上運行是怎么實現的。

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

進程綁定 CPU 的好處:在多核 CPU 結構中,每個核心有各自的L1、L2緩存,而L3緩存是共用的。如果一個進程在核心間來回切換,各個核心的緩存命中率就會受到影響。相反如果進程不管如何調度,都始終可以在一個核心上執行,那么其數據的L1、L2 緩存的命中率可以顯著提高。

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

設置進程的 CPU 親和性

前面介紹了進程與 CPU 綁定的好處后,現在來介紹一下在 Linux 系統下怎么將進程與 CPU 進行綁定的(也就是設置進程的 CPU 親和性)。

Linux 系統提供了一個名為 sched_setaffinity 的系統調用,此系統調用可以設置進程的 CPU 親和性。我們來看看 sched_setaffinity 系統調用的原型: 

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

下面介紹一下 sched_setaffinity 系統調用各個參數的作用:

  •  pid:進程ID,也就是要進行綁定 CPU 的進程ID。
  •  cpusetsize:mask 參數所指向的 CPU 集合的大小。
  •  mask:與進程進行綁定的 CPU 集合(由于一個進程可以綁定到多個 CPU 上運行)。

參數 mask 的類型為 cpu_set_t,而 cpu_set_t 是一個位圖,位圖的每個位表示一個 CPU,如下圖所示:

例如,將 cpu_set_t 的第0位設置為1,表示將進程綁定到 CPU0 上運行,當然我們可以將進程綁定到多個 CPU 上運行。

我們通過一個例子來介紹怎么通過 sched_setaffinity 系統調用來設置進程的 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);  // 將本進程綁定到 CPU2 上  
  13.     // 設置進程的 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 親和性實現

知道怎么設置進程的 CPU 親和性后,現在我們來分析一下 Linux 內核是怎樣實現 CPU 親和性功能的。

本文使用的 Linux 內核版本為 2.6.23

Linux 內核為每個 CPU 定義了一個類型為 struct rq 的 可運行的進程隊列,也就是說,每個 CPU 都擁有一個獨立的可運行進程隊列。

一般來說,CPU 只會從屬于自己的可運行進程隊列中選擇一個進程來運行。也就是說,CPU0 只會從屬于 CPU0 的可運行隊列中選擇一個進程來運行,而絕不會從 CPU1 的可運行隊列中獲取。

所以,從上面的信息中可以分析出,要將進程綁定到某個 CPU 上運行,只需要將進程放置到其所屬的 可運行進程隊列 中即可。

下面我們來分析一下 sched_setaffinity 系統調用的實現,sched_setaffinity 系統調用的調用鏈如下: 

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

從上面的調用鏈可以看出,sched_setaffinity 系統調用最終會調用 migrate_task 函數來完成進程與 CPU 進行綁定的工作,我們來分析一下 migrate_task 函數的實現: 

  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.     // 如果進程還沒有在任何運行隊列中  
  7.     // 那么只需要將進程的 cpu 字段設置為 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.     // 如果進程已經在某一個 CPU 的可運行隊列中 
  14.     // 那么需要將進程從之前的 CPU 可運行隊列中遷移到新的 CPU 可運行隊列中  
  15.     // 這個遷移過程由 migration_thread 內核線程完成  
  16.     // 構建進程遷移請求 
  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 函數各個參數的意義:

  •  p:要設置 CPU 親和性的進程描述符。
  •  dest_cpu:綁定的 CPU 編號。
  •  req:進程遷移請求對象(下面會介紹)。

所以,migrate_task 函數的作用就是將進程描述符為 p 的進程綁定到編號為 dest_cpu 的目標 CPU 上。

migrate_task 函數主要分兩種情況來將進程綁定到某個 CPU 上:

  •  情況1:如果進程還沒有在任何 CPU 的可運行隊列中(不可運行狀態),那么只需要將進程描述符的 cpu 字段設置為 dest_cpu 即可。當進程變為可運行時,會根據進程描述符的 cpu 字段來自動放置到對應的 CPU 可運行隊列中。
  •  情況2:如果進程已經在某個 CPU 的可運行隊列中,那么需要將進程從之前的 CPU 可運行隊列中遷移到新的 CPU 可運行隊列中。遷移過程由 migration_thread 內核線程完成,migrate_task 函數只是構建一個進程遷移請求,并通知 migration_thread 內核線程有新的遷移請求需要處理。

而進程遷移過程由 __migrate_task 函數完成,我們來看看 __migrate_task 函數的實現: 

  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);    // 進程所在的原可運行隊列  
  8.     rq_dest = cpu_rq(dest_cpu);  // 進程希望放置的目標可運行隊列  
  9.     ...  
  10.     on_rq = p->se.on_rq;  // 進程是否在可運行隊列中(可運行狀態)  
  11.     if (on_rq)  
  12.         deactivate_task(rq_src, p, 0);  // 把進程從原來的可運行隊列中刪除  
  13.     set_task_cpu(p, dest_cpu);  
  14.     if (on_rq) {  
  15.         activate_task(rq_dest, p, 0);   // 把進程放置到目標可運行隊列中  
  16.         ...  
  17.     }  
  18.     ...  
  19.     return ret; 
  20.  

__migrate_task 函數主要完成以下兩個工作:

  •  把進程從原來的可運行隊列中刪除。
  •  把進程放置到目標可運行隊列中。

其工作過程如下圖所示(將進程從 CPU0 的可運行隊列遷移到 CPU3 的可運行隊列中):

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

遷移過程首先將進程從 CPU0 的可運行隊列中刪除,然后再將進程插入到 CPU3 的可運行隊列中。

當 CPU 要運行進程時,首先從它所屬的可運行隊列中挑選一個進程,并將此進程調度到 CPU 中運行。

總結

從上面的分析可知,其實將進程綁定到某個 CPU 只是將進程放置到 CPU 的可運行隊列中。

由于每個 CPU 都有一個可運行隊列,所以就有可能會出現 CPU 間可運行隊列負載不均衡問題。如 CPU0 可運行隊列中的進程比 CPU1 可運行隊列多非常多,從而導致 CPU0 的負載非常高,而 CPU1 負載非常低的情況。

當出現上述情況時,就需要對 CPU 間的可運行隊列進行重平衡操作,有興趣的可以自行閱讀源碼或參考相關資料。 

 

責任編輯:龐桂玉 來源: 良許Linux
相關推薦

2010-07-21 09:32:03

Linux多核

2021-10-17 19:49:52

CPURedis緩存

2021-02-22 07:58:45

算法進程調度

2023-04-12 15:37:31

Linux系統CPU

2021-06-15 08:02:55

Linux 進程管理

2017-02-16 19:39:29

Windows 10System進程CPU

2021-09-30 10:45:33

Linux進程通信

2022-01-10 17:41:31

內存結構PostgreSQL

2021-07-15 08:00:47

系統性能調優cpunuma架構

2010-11-26 09:25:28

2021-06-30 21:13:49

CPUCache數據

2021-04-20 13:40:56

Epoll IO

2009-06-17 09:06:59

Unix系統資源進程

2020-10-12 14:18:15

CPU技巧代碼

2020-08-03 14:17:34

CPU內存系統運維

2013-12-17 10:26:14

Windows XPSVCHOST

2019-09-10 08:13:33

LinuxCPU內存

2021-09-14 10:21:13

CPU高性能服務器

2019-12-16 09:10:38

Linux中央處理器進程

2019-12-16 11:00:04

LinuxCPU進程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天天操欧美| 欧美一区二区三区日韩 | 综合国产 | 国产精品成人一区二区三区夜夜夜 | 久久精品一区二区 | 久草资源在线 | 国产精品1区 | 国产精品小视频在线观看 | 自拍偷拍在线视频 | 中文字幕人成乱码在线观看 | 日韩三级电影一区二区 | 亚洲欧洲日韩 | 国内自拍第一页 | 成人福利片 | 日韩电影中文字幕 | a黄毛片 | 亚洲欧美精品一区 | 欧美一级高清片 | 国产在线区| 亚洲免费观看 | 午夜丁香视频在线观看 | 日本午夜在线视频 | 日韩一区二区三区在线观看 | 亚洲精品久久久久久久久久久久久 | 久久久成人免费视频 | 青青草在线播放 | 五月综合激情婷婷 | av性色全交蜜桃成熟时 | 欧美日韩成人 | 欧美日韩高清在线一区 | 在线观看av不卡 | 操人视频在线观看 | 欧美日韩亚洲国产 | 国产精品久久片 | 亚洲一区二区三区在线播放 | 色婷婷av一区二区三区软件 | 欧美三级在线 | 久久精品小短片 | 成人一区av偷拍 | 亚洲午夜精品视频 | 久久精品国产精品青草 |