티스토리 뷰

5-5 kernel/pid.c alloc_pidmap

  • pid namespace 에서 pid 를 할당
  • 여기서는 pid 를 보관할 수 있는 페이지가 없다면 페이지만 할당 해줌
// pid 가 존재할수 있는 모든 페이지, offset 이 0이라면 1을 뺌
// 그러나 페이지의 개념은 아니고, 실패시 offset 과 pid 를 새로 변경해서 할당
max_scan = DIV_ROUND_UP(pid_max, BITS_PER_PAGE) - !offset;

5-6 kernel/pid.c alloc_pidmap 2

  • 현재 page 에서 pid 할당이 가능한지 확인
  • 만약 안된다면 다음 페이지에서 할당 시도 or 다음페이지가 없다면 reserved pid 로 시도
// 내껄 마지막에 등록하는걸 실패하더라도, 다른 더 뒤에있던 pid 가 쓰여졌다면 write 하지 않음
static void set_last_pid(struct pid_namespace *pid_ns, int base, int pid)
{
    int prev;
    int last_write = base;
    do {
        prev = last_write;
        last_write = cmpxchg(&pid_ns->last_pid, prev, pid);
    } while ((prev != last_write) && (pid_before(base, last_write, pid)));
}

5.2.3 PID 해제하기

5-7 kernel/pid.c free_pid

    for (i = 0; i <= pid->level; i++) { // 최상위 pid ns 부터 탐색
        struct upid *upid = pid->numbers + i; // upid 는 pid 의 정수 & hash 리스트 & namespace 를 갖음

5.2.4 PID 할당을 위한 초기화 과정

5-9 kernel/pid.c pidhash_init

  • pid 해시 테이블을 초기화. struct upid 의 struct hlist_node pid_chain; 리스트를 위함
  • pid 와 연관된 task_struct 를 찾을 때 사용
  • hlist = hash list
struct hlist_head {
    struct hlist_node *first;
};

struct hlist_node {
    struct hlist_node *next, **pprev;
};

5-10 kernel/pid.c pidmap_init

  • PID_MAX_LIMIT : 최대 PID 개수 제한
  • PIDNS_HASH_ADDING : PID 해시 테이블에 추가할 수 있는 hash 개수
#define PID_MAX_DEFAULT (CONFIG_BASE_SMALL ? 0x1000 : 0x8000)
/*
 * A maximum of 4 million PIDs should be enough for a while.
 * [NOTE: PID/TIDs are limited to 2^29 ~= 500+ million, see futex.h.]
 */
#define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \
    (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT))

// 최초
int pid_max = PID_MAX_DEFAULT;
#define RESERVED_PIDS        300
int pid_max_min = RESERVED_PIDS + 1;
int pid_max_max = PID_MAX_LIMIT;

5.3 실행 상태 관리하기

TASK_INTERRUPTIBLE

  • 시그널로 죽일수도 있는 대기 상태
    TASK_UNINTERRUPTIBLE
  • 원하는 이벤트로만 깨울 수 있는 상태

5.4 우선순위 관리하기

5.4.1 nice 값과 우선순위

nice 가 높다 -> 친절하다 -> 다른 task 에 많이 양보

SCHED_NORMAL

  • The default policy for most tasks, including the shell. It uses a dynamic priority that changes based on the thread's characteristics.
    SCHED_BATCH
  • Used for non-interactive tasks that should run without interruption. These tasks are usually scheduled after all SCHED_NORMAL tasks are complete. SCHED_BATCH tasks don't preempt as often as regular tasks, allowing them to run longer.
    SCHED_IDLE
  • Used for low-priority tasks that only run when nothing else is running. SCHED_IDLE tasks are given some time to run even if other SCHED_NORMAL tasks are running.

5.4.2 static priority, normal priority, dynamic priority

  • static priority : 실시간 task 에서 사용
  • normal priority : 일반 task 에서 동적으로 dynamic priority 와 사용
  • dynamic priority : 실시간 task 에서 동적으로 boosting 하기도 하는데 기준을 normal priority 로 잡음

5.4.3 우선순위 설정하기

5-11 kernel/sched/core.c set_user_nice

// sched.h
struct rq {}

5-12 kernel/sched/core.c set_load_weight

struct task_struct {
  struct sched_entity se;
}

// include/linux/sched.h
struct sched_entity {
    struct load_weight    load;        /* for load-balancing */
}

struct load_weight *load = &p->se.load;

sched_prio_to_weight[40 nice] -> 낮을 수록 nice 하지 않으니 weight 가 큼

load->weight = scale_load(sched_prio_to_weight[prio]);
load->inv_weight = sched_prio_to_wmult[prio];

5.4.4 PI boosting priority leak PI boosting priority leak

boost-up 된 부모의 우선순위 누수

태스크가 생성될때 자식 태스크의 static, normal priority 는 별도로 재성성 되지 않고 부모의 우선순위를 상속 받는다.

dynamic 만 부모의 normal priority 로 재설정 된다

그 이유는 boost-up 된 부모 태스크의 dynamic priority 를 자식이 상속 받는것을 막기 위해. 이 경우를 PI boosting priority leak 이라 부름

부모의 우선순위를 상속받지 않고 초기화 하기

SCHED_FLAG_RESET_ON_FORK 를 사용하면, 부모 태스크의 우선순위를 모두 상속받지 않음

5.5 태스크 생성하기

5-13 kernel/fork.c _do_fork

태스크 생성의 시작점

5.5.2 copy_process

태스크 생성은 기본적으로 부모 태스크를 복사하고, 전달된 clone 플래그에 따라 복사할 부분과 공유할 부분을 결정한다.

5-14 kernel/fork.c copy_process

CLONE_FS : 부모의 현재, 최상위 디렉토리를 자식과 공유.

  • CLONE_NEWNS 는 마운트 포인트 리스트를 새로 생성해서, 같은 곳을 바라보지 못하기 때문에 사용 x
  • CLONE_USER 도 아무런 이득이 없음

CLONE_THREAD 로 쓰레드를 생성할때 쓰레드 그룹은 시그널 정보를 공유해야 하므로 CLONE_SIGHAND 가 필요

CLONE_SIGHAND 플래그를 사용하면 시그널 정보를 공유하기 위해 CLONE_VM 가 필요

current->signal->flags & SIGNAL_UNKILLABLE 일때 current 는 init 태스크이다 (Pid=1) 이때 CLONE_PARENT 를 사용하면 swapper 가 새로운 부모가 되는데(init 태스크보다 높은 0), swapper 는 죽은 자식 task 를 처리할 수 없어 계속 좀비로 머문다. 따라서 플래그를 사용하면 안됨

CLONE_THREAD 때는 NEWUSER 나 NEWPID 를 사용하면 안됨

5-15 kernel/fork.c copy_process 2

Linux Security Module (LSM) 
retval = security_task_create(clone_flags); // 보안 검사

dup_task_struct
// task_struct, kernel stack 할당
// task_struct 를 부모것을 복사 후 task_struct 의 stack 을 할당된 것에 가르키게하고, thread_info 의 task 를 연결

cred: The security context of a task

유저가 보유할수 있는 task 를 넘거나, thread max 를 넘어가는 경우 실패

그 외 task 초기화 등등
댓글