← Documents Documentation/block/ioprio.rst GitHub 원문 ↗

Linux 6.18.37 · Block

Block io priorities

I/O priority scheduling class와 ionice 명령·reference implementation을 설명합니다.

Source pathDocumentation/block/ioprio.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

ioprio.rst:1-178

block I/O priority는 realtime, best-effort, idle class로 process와 process group의 disk service 우선순위를 조절합니다. RT는 starvation 위험이 있고 BE는 CPU nice에서 파생되며 IDLE은 disk가 비었을 때만 동작합니다.

`ionice`는 class·level·pid를 설정하거나 새 command를 해당 priority로 실행합니다. 원문 C sample은 `ioprio_set`·`ioprio_get` syscall을 직접 사용해 이 동작을 구현합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 ===================
2 Block io priorities
3 ===================
4
5
6 Intro
7 -----
8
9 The io priority feature enables users to io nice processes or process groups,
10 similar to what has been possible with cpu scheduling for ages. Support for io
11 priorities is io scheduler dependent and currently supported by bfq and
12 mq-deadline.
13
14 Scheduling classes
15 ------------------
16
17 Three generic scheduling classes are implemented for io priorities that
18 determine how io is served for a process.
19
20 IOPRIO_CLASS_RT: This is the realtime io class. This scheduling class is given
21 higher priority than any other in the system, processes from this class are
22 given first access to the disk every time. Thus it needs to be used with some
23 care, one io RT process can starve the entire system. Within the RT class,
24 there are 8 levels of class data that determine exactly how much time this
25 process needs the disk for on each service. In the future this might change
26 to be more directly mappable to performance, by passing in a wanted data
27 rate instead.
28
29 IOPRIO_CLASS_BE: This is the best-effort scheduling class, which is the default
30 for any process that hasn't set a specific io priority. The class data
31 determines how much io bandwidth the process will get, it's directly mappable
32 to the cpu nice levels just more coarsely implemented. 0 is the highest
33 BE prio level, 7 is the lowest. The mapping between cpu nice level and io
34 nice level is determined as: io_nice = (cpu_nice + 20) / 5.
35
36 IOPRIO_CLASS_IDLE: This is the idle scheduling class, processes running at this
37 level only get io time when no one else needs the disk. The idle class has no
38 class data, since it doesn't really apply here.
39
40 Tools
41 -----
42
43 See below for a sample ionice tool. Usage::
44
45 # ionice -c<class> -n<level> -p<pid>
46
47 If pid isn't given, the current process is assumed. IO priority settings
48 are inherited on fork, so you can use ionice to start the process at a given
49 level::
50
51 # ionice -c2 -n0 /bin/ls
52
53 will run ls at the best-effort scheduling class at the highest priority.
54 For a running process, you can give the pid instead::
55
56 # ionice -c1 -n2 -p100
57
58 will change pid 100 to run at the realtime scheduling class, at priority 2.
59
60 ionice.c tool::
61
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <errno.h>
65 #include <getopt.h>
66 #include <unistd.h>
67 #include <sys/ptrace.h>
68 #include <asm/unistd.h>
69
70 extern int sys_ioprio_set(int, int, int);
71 extern int sys_ioprio_get(int, int);
72
73 #if defined(__i386__)
74 #define __NR_ioprio_set 289
75 #define __NR_ioprio_get 290
76 #elif defined(__ppc__)
77 #define __NR_ioprio_set 273
78 #define __NR_ioprio_get 274
79 #elif defined(__x86_64__)
80 #define __NR_ioprio_set 251
81 #define __NR_ioprio_get 252
82 #else
83 #error "Unsupported arch"
84 #endif
85
86 static inline int ioprio_set(int which, int who, int ioprio)
87 {
88 return syscall(__NR_ioprio_set, which, who, ioprio);
89 }
90
91 static inline int ioprio_get(int which, int who)
92 {
93 return syscall(__NR_ioprio_get, which, who);
94 }
95
96 enum {
97 IOPRIO_CLASS_NONE,
98 IOPRIO_CLASS_RT,
99 IOPRIO_CLASS_BE,
100 IOPRIO_CLASS_IDLE,
101 };
102
103 enum {
104 IOPRIO_WHO_PROCESS = 1,
105 IOPRIO_WHO_PGRP,
106 IOPRIO_WHO_USER,
107 };
108
109 #define IOPRIO_CLASS_SHIFT 13
110
111 const char *to_prio[] = { "none", "realtime", "best-effort", "idle", };
112
113 int main(int argc, char *argv[])
114 {
115 int ioprio = 4, set = 0, ioprio_class = IOPRIO_CLASS_BE;
116 int c, pid = 0;
117
118 while ((c = getopt(argc, argv, "+n:c:p:")) != EOF) {
119 switch (c) {
120 case 'n':
121 ioprio = strtol(optarg, NULL, 10);
122 set = 1;
123 break;
124 case 'c':
125 ioprio_class = strtol(optarg, NULL, 10);
126 set = 1;
127 break;
128 case 'p':
129 pid = strtol(optarg, NULL, 10);
130 break;
131 }
132 }
133
134 switch (ioprio_class) {
135 case IOPRIO_CLASS_NONE:
136 ioprio_class = IOPRIO_CLASS_BE;
137 break;
138 case IOPRIO_CLASS_RT:
139 case IOPRIO_CLASS_BE:
140 break;
141 case IOPRIO_CLASS_IDLE:
142 ioprio = 7;
143 break;
144 default:
145 printf("bad prio class %d\n", ioprio_class);
146 return 1;
147 }
148
149 if (!set) {
150 if (!pid && argv[optind])
151 pid = strtol(argv[optind], NULL, 10);
152
153 ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
154
155 printf("pid=%d, %d\n", pid, ioprio);
156
157 if (ioprio == -1)
158 perror("ioprio_get");
159 else {
160 ioprio_class = ioprio >> IOPRIO_CLASS_SHIFT;
161 ioprio = ioprio & 0xff;
162 printf("%s: prio %d\n", to_prio[ioprio_class], ioprio);
163 }
164 } else {
165 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT) == -1) {
166 perror("ioprio_set");
167 return 1;
168 }
169
170 if (argv[optind])
171 execvp(argv[optind], &argv[optind]);
172 }
173
174 return 0;
175 }
176
177
178 March 11 2005, Jens Axboe <[email protected]>
179

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Block I/O priority 개요

1-13

`Block io priorities` 기능을 사용하면 오랫동안 CPU scheduling에서 가능했던 것처럼 process 또는 process group에 I/O nice 값을 지정할 수 있습니다.

I/O priority 지원 여부는 I/O scheduler에 따라 달라지며 현재 `bfq`와 `mq-deadline`이 지원합니다.

세 scheduling class

14-39

I/O priority에는 process의 I/O service 방식을 결정하는 세 generic scheduling class가 구현되어 있습니다.

`IOPRIO_CLASS_RT`는 realtime I/O class입니다. system의 다른 모든 class보다 priority가 높고 이 class process가 매번 disk에 가장 먼저 접근합니다. 하나의 I/O RT process가 system 전체를 starve할 수 있으므로 주의해서 사용해야 합니다.

RT class 안에는 service할 때마다 process가 disk를 얼마나 오래 필요로 하는지 정하는 class data level 8개가 있습니다. 향후에는 원하는 data rate를 전달해 performance에 더 직접 mapping하는 방식으로 바뀔 수 있습니다.

`IOPRIO_CLASS_BE`는 specific I/O priority를 설정하지 않은 모든 process의 default인 best-effort scheduling class입니다. class data는 process가 받을 I/O bandwidth를 결정하며 CPU nice level과 직접 mapping되지만 더 거친 단계로 구현됩니다.

BE priority level은 `0`이 가장 높고 `7`이 가장 낮습니다. CPU nice level과 I/O nice level 사이 mapping은 `io_nice = (cpu_nice + 20) / 5`로 계산합니다.

`IOPRIO_CLASS_IDLE`은 idle scheduling class입니다. 이 level의 process는 다른 어느 process도 disk를 필요로 하지 않을 때만 I/O time을 받습니다. 여기에는 class data가 적용되지 않으므로 idle class에는 class data가 없습니다.

ionice 사용법

40-59

sample `ionice` tool의 기본 사용법은 다음과 같습니다.

# ionice -c<class> -n<level> -p<pid>

`pid`를 주지 않으면 current process를 대상으로 합니다. I/O priority setting은 fork 때 inherit되므로 다음처럼 process를 특정 level에서 시작할 수 있습니다.

# ionice -c2 -n0 /bin/ls

이 명령은 `ls`를 best-effort scheduling class의 가장 높은 priority로 실행합니다. 이미 실행 중인 process에는 다음처럼 pid를 지정합니다.

# ionice -c1 -n2 -p100

이 명령은 pid `100`을 realtime scheduling class의 priority `2`로 변경합니다.

ionice.c reference implementation

60-178

원문이 제공하는 `ionice.c` sample implementation은 다음과 같습니다. architecture별 syscall number, priority class·target enum, class bit shift, get/set mode와 command execution을 포함합니다.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <asm/unistd.h>

extern int sys_ioprio_set(int, int, int);
extern int sys_ioprio_get(int, int);

#if defined(__i386__)
#define __NR_ioprio_set                289
#define __NR_ioprio_get                290
#elif defined(__ppc__)
#define __NR_ioprio_set                273
#define __NR_ioprio_get                274
#elif defined(__x86_64__)
#define __NR_ioprio_set                251
#define __NR_ioprio_get                252
#else
#error "Unsupported arch"
#endif

static inline int ioprio_set(int which, int who, int ioprio)
{
      return syscall(__NR_ioprio_set, which, who, ioprio);
}

static inline int ioprio_get(int which, int who)
{
      return syscall(__NR_ioprio_get, which, who);
}

enum {
      IOPRIO_CLASS_NONE,
      IOPRIO_CLASS_RT,
      IOPRIO_CLASS_BE,
      IOPRIO_CLASS_IDLE,
};

enum {
      IOPRIO_WHO_PROCESS = 1,
      IOPRIO_WHO_PGRP,
      IOPRIO_WHO_USER,
};

#define IOPRIO_CLASS_SHIFT        13

const char *to_prio[] = { "none", "realtime", "best-effort", "idle", };

int main(int argc, char *argv[])
{
      int ioprio = 4, set = 0, ioprio_class = IOPRIO_CLASS_BE;
      int c, pid = 0;

      while ((c = getopt(argc, argv, "+n:c:p:")) != EOF) {
              switch (c) {
              case 'n':
                      ioprio = strtol(optarg, NULL, 10);
                      set = 1;
                      break;
              case 'c':
                      ioprio_class = strtol(optarg, NULL, 10);
                      set = 1;
                      break;
              case 'p':
                      pid = strtol(optarg, NULL, 10);
                      break;
              }
      }

      switch (ioprio_class) {
              case IOPRIO_CLASS_NONE:
                      ioprio_class = IOPRIO_CLASS_BE;
                      break;
              case IOPRIO_CLASS_RT:
              case IOPRIO_CLASS_BE:
                      break;
              case IOPRIO_CLASS_IDLE:
                      ioprio = 7;
                      break;
              default:
                      printf("bad prio class %d\n", ioprio_class);
                      return 1;
      }

      if (!set) {
              if (!pid && argv[optind])
                      pid = strtol(argv[optind], NULL, 10);

              ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);

              printf("pid=%d, %d\n", pid, ioprio);

              if (ioprio == -1)
                      perror("ioprio_get");
              else {
                      ioprio_class = ioprio >> IOPRIO_CLASS_SHIFT;
                      ioprio = ioprio & 0xff;
                      printf("%s: prio %d\n", to_prio[ioprio_class], ioprio);
              }
      } else {
              if (ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT) == -1) {
                      perror("ioprio_set");
                      return 1;
              }

              if (argv[optind])
                      execvp(argv[optind], &argv[optind]);
      }

      return 0;
}

원문 기록: `March 11 2005, Jens Axboe <[email protected]>`.