요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
===================
Block io priorities
===================
Intro
-----
The io priority feature enables users to io nice processes or process groups,
similar to what has been possible with cpu scheduling for ages. Support for io
priorities is io scheduler dependent and currently supported by bfq and
mq-deadline.
Scheduling classes
------------------
Three generic scheduling classes are implemented for io priorities that
determine how io is served for a process.
IOPRIO_CLASS_RT: This is the realtime io class. This scheduling class is given
higher priority than any other in the system, processes from this class are
given first access to the disk every time. Thus it needs to be used with some
care, one io RT process can starve the entire system. Within the RT class,
there are 8 levels of class data that determine exactly how much time this
process needs the disk for on each service. In the future this might change
to be more directly mappable to performance, by passing in a wanted data
rate instead.
IOPRIO_CLASS_BE: This is the best-effort scheduling class, which is the default
for any process that hasn't set a specific io priority. The class data
determines how much io bandwidth the process will get, it's directly mappable
to the cpu nice levels just more coarsely implemented. 0 is the highest
BE prio level, 7 is the lowest. The mapping between cpu nice level and io
nice level is determined as: io_nice = (cpu_nice + 20) / 5.
IOPRIO_CLASS_IDLE: This is the idle scheduling class, processes running at this
level only get io time when no one else needs the disk. The idle class has no
class data, since it doesn't really apply here.
Tools
-----
See below for a sample ionice tool. Usage::
# ionice -c<class> -n<level> -p<pid>
If pid isn't given, the current process is assumed. IO priority settings
are inherited on fork, so you can use ionice to start the process at a given
level::
# ionice -c2 -n0 /bin/ls
will run ls at the best-effort scheduling class at the highest priority.
For a running process, you can give the pid instead::
# ionice -c1 -n2 -p100
will change pid 100 to run at the realtime scheduling class, at priority 2.
ionice.c tool::
#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]>
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-39I/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-59sample `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]>`.
요약과 해설
ioprio.rst:1-178block 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을 직접 사용해 이 동작을 구현합니다.