커널분석_노서영

Download Report

Transcript 커널분석_노서영

cgroup_init() 함수의 주요 Operation
init/main.c
1.
2.
3.
4.
5.
cgroup_init
cgroup_backing_dev_info의 멤버를 초기화
cgroup의 subsystem 전부 initialize
cgroup subsystem 의 해시테이블에 init_css_set을 추가
cgroup 파일시스템을 등록한다
proc파일시스템에 cgroups 엔트리 생성
bid_init(&cgroup_backing_dev_info)
1. cgroup_backing_dev_info의 멤버를 초기화
2. percpu_counter 구조체인 bdi_stat[], completions의
counter를 0으로 세팅, counters의 메모리 할당, lock
class등록을 한다
cgroup subsystem init
CGROUP_SUBSYS_COUNT 만큼 for loop
cgroup의 subsystem (cpuset) (debug) (ns) (cpu_cgroup)
(cpuacct) (mem_cgroup) (devices) (freezer) (net_cls)
모두 initialize
register_filesystem(&cgroup_fs_type)
Proc_create(“cgroups”, 0, NULL,
&proc_cgroupstats_operations)
cgroup 파일시스템을 등록한다.
proc 파일시스템에 cgroups 엔트리 생성
cpuset_init() 함수의 주요 Operation
init/main.c
1. 전역변수 top_cpuset의 cpus_allowed, mems_allowed를
각각 cpu개수만큼, MAX_NUMNODES만큼 set
2. top_cpuset구조체의 fmeter 구조체를 초기화
3. cpuset fs를 파일시스템으로 등록한다
4. cpumask인 cpus_attach에 메모리를 할당
5. 현재 시스템에 있는 cpuset개수를 세팅
cpuset_init
cpumask_setall(top_cupset.cpus_allowed)
nodes_setall(top_cpuset.mems_allowed)
1. top_cpuset의 cpus_allowed, mems_allowed를
2. 각각 cpu개수만큼, MAX_NUMNODES만큼 set
fmeter_init(&top_cpuset.fmeter)
top_cpuset.mems_generation =
cpuset_mems_generation++
set_bit(CS_SCHED_LOAD_BALANCE,
&top_cpuset.flags)
register_filesystem(&cpuset_fs_type)
top_cpuset구조체의 fmeter 구조체를 초기화 fmeter는
frequency meter인데 어떤 이벤트가 얼마나 빠르게
발생하는지에 대한 정보를 가지고 있음
어떠한 cpuset이 mems_allowed의 값을 변경하면
mems_generation의 값을 증가시킨다
cpuset fs를 파일시스템으로 등록한다.
number_of_cpusets = 1
현재 시스템에 있는 cpuset개수를 세팅
taskstats_init_early() 함수의 주요 Operation
init/main.c
taskstats_init_early()
1. taskstats 구조체의 슬랩캐시 taskstats를 생성한다
2. i번째 cpu의 per_cpu 데이터인 listener_array[]의 list를
초기화
3. i번째 cpu의 per_cpu 데이터인 listener_array[]의
semaphore을 초기화
taskstats_cache =
KMEM_CACHE(taskstats, SLAB_PANIC)
taskstats 구조체의 슬랩캐시 taskstats를 생성한다
for_each_possible_cpu
INIT_LIST_HEAD(listener_array.list)
init_rwsem(listener_array.sem)
1. i번째 cpu의 per_cpu 데이터인 listener_array[]의
list를 초기화
2. i번째 cpu의 per_cpu 데이터인 listener_array[]의
semaphore을 초기화
taskstats
Format for per-task data returned to userland when a
task exits or listener requests stats for a task
taskstats_init_early() 함수의 주요 Operation
init/main.c
delayacct_init()
1. task_delay_info 구조체의 슬랩캐시 task_delay_info를 생
성한다.
2. tsk->delays를 NULL로 초기화.
3. delayacct_on이 true라면 task_delay_info 구조체하나를
슬랩캐시에서 할당받음
delayacct_cache =
KMEM_CACHE(task_delay_info, SLAB_PANIC);
delayacct_tsk_init(&init_task);
task_delay_info 구조체의 슬랩캐시 task_delay_info를
생성한다
1. tsk->delays를 NULL로 초기화
2. delayacct_on이 true라면 task_delay_info
구조체하나를 슬랩캐시에서 할당받음
__delayacct_tsk_init(struct
task_struct *tsk)
task_delay_info 구조체하나를
슬랩캐시에서 할당받음
CONFIG_TASK_DELAY_ACCT
Enable per-task delay accounting Collect information
on time spent by a task waiting for system resources
like cpu, synchronous block I/O completion and
swapping in pages. Such statistics can help in setting a
task's priorities relative to other tasks for cpu, io, rss
limits etc.
check_bugs() 함수의 주요 Operation
init/main.c
1. physical한 page 한개 할당받는다
2. page의 protection을
L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|
L_PTE_WRITE| L_PTE_MT_BUFFERABLE로 설정한다
3. page를 virtual address p1,p2로 매핑한다
4. 동일한 physical page에 대한 서로 다른 virtual address
를 갖는 p1, p2에 writing test. p1에 쓴값이 p2에 쓴값
으로 over write된다면 테스트 성공
check_bugs()
check_writebuffer_bugs
pgprot_t prot =
__pgprot(L_PTE_PRESENT|L_PTE_YOUNG|
L_PTE_DIRTY|L_PTE_WRITE|
L_PTE_MT_BUFFERABLE);
p1 = vmap(&page, 1, VM_IOREMAP, prot)
p2 = vmap(&page, 1, VM_IOREMAP, prot)
if (p1 && p2) {
v = check_writebuffer(p1, p2);
reason = "enabling work-around";
} else {
reason = "unable to map memory\n";
}
1.
2.
3.
physical한 page 한개 할당 받는다
page의 protection을 L_PTE_PRESENT |
L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE|
L_PTE_MT_BUFFERABLE로 설정한다
page를 virtual address p1, p2로 매핑한다
동일한 physical page에 대한 서로 다른 virtual
address를 갖는 p1, p2에 writing test를 하는
함수 p1에 쓴값이 p2에 쓴값으로 over
write된다면 테스트 성공
init/main.c
1. ACPI (Advanced Configuration and Power Interface)
Support
2. for X86 ref page  http://www.spinics.net/lists/armkernel/msg01205.html
acpi_early_init()
ftrace_init()
no-op으로 시작하는 함수의 주소 갯수
count = __stop_mcount_loc –
__start_mcount_loc
인자로 넘어온 num_to_init은 no-op으로
시작하는 함수의 총 주소 개수 num_to_init이
몇페이지에 들어갈 수 있는지를 계산하여
페이지를 할당 받음
ftrace_dyn_table_alloc(count)
1.
ftrace_convert_nops(NULL,
__start_mcount_loc,
__stop_mcount_loc)
2.
3.
4.
ftrace를 위한 entry 개수만큼 루프를 돌면서
dyn_ftrace page로부터 record를 담을 주소를
리턴받아 멤버세팅
ftrace의 entry인 dyn_ftrace의 ip는
MCOUNT_ADDR을 가리키고 있음 ip를 noop으로 변경한다
no-op으로 바꾸는 시간을 측정
변경된 횟수를 저장
ftrace(function trace)
컴파일 타임에 모든 커널함수의 앞에 5byte No-op instruction을
삽입 frace가 enable 되면 no-op instruction이 ftrace 에 의해 함
수를 trace 하는 코드로 교체된다.
init/main.c
copy_process() 함수의 주요 Operation
rest_init()
1.
2.
3.
kernel_thread(kernel_init)
kernel_thread()함수가 do_fork()
copy_process()를 통해 task를 복사하여
kernel_init 함수를 실행할 준비를 하게함
부모의 task_struct의 내용을 복사하여
새로운 프로세스를 생성한다.
레지스터, 프로세스환경변수들을 CLONE flags에 따라
적절하게 수정한다.
do_fork()
do_fork() 함수의 주요 Operation
1.
2.
3.
4.
5.
6.
7.
8.
copy_process()를 호출하여 부모의 task_struct 내용을 복사하여 새로운
프로세스를 생성한다. 레지스터, 프로세스환경변수들을 CLONE flags에 따라
적절하게 수정한다.
프로세스 생성에 문제가 없었다면 current task의 pid name space으로 부터
virtual id를 가져온다
CLONE_PARENT_SETTID가 셋되어있을때는 parent_tidptr을 NULL이 아닌 값을
인자로 넘겨 nr값을 써준다
CLONE_VFORK가 세팅되어있으면 vfork_done을 설정하고 vfork 구조체를
초기화한다
child p의 audit_context를 초기화한다. child p에 ptrace가 있으면
pending.signal에 SIGSTOP을 추가하고 thread_info->flag에
TIF_SIGPENDING을 세팅한다. PF_STARTING 플래그 제거
CLONE_STOPPED가 세팅되어있으면 pending.signal에 SIGSTOP을 추가하고
thread_info->flag에 TIF_SIGPENDING을 세팅하고 태스크 state에
TASK_STOPPED를 설정한다
CLONE_STOPPED가 세팅되어 있지 않으면 태스크를 깨운다. mask를 검사해서
event의 notification을 stop할 것인지를 확인, stop할 경우에는 message와
함께 ptrace의 parent에게 리포트한다.
CLONE_VFORK플래그가 설정되어있으면 PF_FREEZER_SKIP. 플래그를 설정,
vfork로 생성한 child가 끝날때까지 기다린다. 태스크가 다시 freeze될 수
있음을 마킹. vfork가 완료되었음을 알림
copy_process()
init/main.c
1.
2.
3.
4.
5.
6.
7.
8.
rest_init()
kernel_thread(
kernel_init)
do_fork()
구조체 task_struct, thread_info를 생성
부모의 task_struct 내용을 복사
tsk->stack에 thread_info를 세팅
tsk->dirties 초기화
부모의 thread_info를 tsk의 thread_info로 복사
thread_info->task를 tsk로 세팅
stackend를 stack의 end(thread_info주소+1byte)로 세팅
overflow detection을 위해 magic number, canary값
집어넣음
copy_process()
dup_task_struct(current)
rt_mutex_init_task(p)
spin_lock, pi list, pi_blocked_on을 초기화 한다.
rlim: resource limit
현재 프로세스가 관리할 수 있는 프로세스 개수보다 더 많은
프로세스가 생성되었다면, 그리고 CAP_SYS_ADMIN,
CAP_SYS_RESOURCE, INIT_USER 중 하나도 해당되지 않는다
면 fork를 허용하지 않는다
rlim check
1.
copy_creds(p, clone_flags)
2.
3.
4.
max_threads check
exec_domain->module
check
cred->thread_keyring이 NULL이고 CLONE_THREAD플래
그가 설정되어있는 경우에는 부모의 credential을 공유한
다
그렇지 않다면 새로운 credential 을 생성
부모가 cred->thread_keyring을 가지고 있다면 새로운 쓰
레드도 값을 새롭게 설정하여 갖는다
CLONE_THREAD 플래그가 설정되어 있지 않을 경우
부모의 tgcred를 버리고 새로운 tgcred를 설정한다
max_threads보다 많은 쓰레드를 생성했는지
execution domain
how system calls are invoked or how the various signals are
numbered. This information is stored in execution domain
descriptors of type exec_domain.
exec_domain->module의 context가 존재하지 않는지
init/main.c
Linux supports various organization formats for
executable files. The standard format is ELF
(Executable and Linkable Format).
rest_init()
kernel_thread(
kernel_init)
binfmt가 존재하는데 binfmt->module이 존재하지
않는다면 bad_fork_cleanup_count로 점프
do_fork()
copy_process()
P->binfmt &&
p->binfmt->moudle check
p->did_exe ….
P->vfork_done
init_sigpending(&p>pending)
p->utime ….
p->default_timer_slack_ns
task_io_accounting_init(
&p->ioac)
did_exec, delayacct, flags, children,sibling
rcu 관련 멤버, vfork_done, spin lock을 초기화한다
pending에는 부모 프로세스의 pending된 시그널이 기
록되어있음. 자식 프로세스에서는 필요없으므로
sigpending의 모든 signal clear
Time 관련 멤버 초기화
ioac를 0으로 셋팅
p의 accounting field를 클리어
acct_clear_integrals(p)
POSIX timer handling 초기화
posix_cpu_timers_init(p)
do_posix_clock_monoton
ic_gettime(
&p->start_time)
monotonic 시간(xtime+wall_to_monotonic)을
timespec포맷으로 구하여 start_time에 세팅
init/main.c
rest_init()
kernel_thread(
kernel_init)
do_fork()
monotonic 시간(xtime+wall_to_monotonic)을
timespec포맷으로 구하여 start_time에 세팅
copy_process()
monotonic_to_bootbased(&
p->real_start_time)
p->real_start_time =
p->start_time
monotonic_to_bootbased(
&p->real_start_time)
cgroup_fork(p)
부팅한 시점을 기준으로 p의 real_start_time을 설정
새로 fork된 task p를 parent의 cgroup에 포함시킨다
CONFIG_NUMA가 적용된 경우
부모의 memory policy 복사
p->mempolicy =
mpol_dup(p->mempolicy)
mpol_fix_fork_child_flag(p)
p->irq_events …
p->softirq_context
mempolicy가 존재하면 PF_MEMPOLICY 비트 set 그렇
지 않다면 clear. non-default memory policy가 아닌경
우에만 PF_MEMPOLICY를 셋함
irq 관련 멤버 초기화
init/main.c
Perform scheduler related setup. Assign this task to a CPU.
rest_init()
kernel_thread(
kernel_init)
do_fork()
copy_process()
sched_fork(p, clone_flags)
audit_alloc(p)
copy_semundo(
clone_flags, p)
copy_files(clone_flags, p)
copy_fs(clone_flags, p)
copy_sighand(clone_flags, p)
copy_signal(clone_flags, p)
copy_mm(clone_flags, p)
copy_namespaces(
clone_flags, p)
1.current의 preempt disable
2.새로 생성한 프로세스의 sched_entity의 멤버를 초기화.
프로세스의 state를 TASK_RUNNING으로 바꾼다 그렇지만
runqueue에는 아직 삽입하지 않은 상태
3.SMP일 경우 가장 로드가 적게걸린 CPU를 찾는다.
새롭게
assign된 cpu에 맞게 값을 세팅한다.
4.prio를 부모의 normal_prio로 세팅. rt priority가 아니라면,
프로세스의 스케쥴러를 fair_sched_class로 세팅.
5.CONFIG_SCHEDSTATS가 정의되어있으면 sched_info
초기화
6.pushable_tasks의 priority를 prio로 초기화 plist를 null
초기화
7.current의 preempt enable
audit context block을 할당한다
audit context block?? system call을 audit한다
CLONE_SYSVSEM이 설정되어 있으면 parent와 child 태스크간
에 SEM_UNDO를 공유할 수 있게 설정한다
File descriptor 복사
File system 관련 자료구조 복사
Signal handler 복사
p->sig 자료구조 할당하고 초기화, rlim은 current의 것을 복사
CLONE_VM 플래그가 설정되어있으면 부모와 mm공유
아니면 별도로 할당받아 초기화
namespace 복사
init/main.c
rest_init()
kernel_thread(
kernel_init)
do_fork()
CLONE_IO 플래그가 설정되어있으면 부모와 ioc공유 아니면
별도로 할당받아 초기화
copy_process()
copy_io(clone_flags, p)
copy_thread(clone_flags,
stack_start, stack_size, p,
regs)
child process의 register pointer의 값을 do_fork로부터 넘겨받
은 인자 regs로 세팅. child process의 thread_info를 얻어와서
sp, pc값을 설정한다. pc는 fork후 리턴된 주소로 세팅된다
pid를 할당 받음
alloc_pid(p->nsproxy>pid_ns)
p->nsproxy->pid_ns를 proc 파일시스템에 마운트
pid_ns_prepare_proc(p>nsproxy->pid_ns)
CONFIG_FUNCTION_GRAPH_TRACER가 정의되어있으면
trace용으로 사용할 스택을 할당받아 p->ret_stack에 assign
ftrace_graph_init_task(p)
init namespace에서 보이는 global id를 가져온다.
p->pid = pid_nr(pid)
p->tgid = p->pid
init/main.c
rest_init()
kernel_thread(
kernel_init)
부모 프로세스와 자식프로세스의 nsproxy가 다르다면
namespace cgroup 하나를 생성한 후 child 프로세스를
생성한 namespace cgroup으로 이동시킨다.
do_fork()
copy_process()
ns_cgroup_clone(p, pid)
thread_info->flag에서 TIF_SYSCALL_TRACE, IF_SYSCALL_EMU
를 클리어시킨다
clear_tsk_thread_flag(p,
TIF_SYSCALL_TRACE)
cgroup subsystem의 fork callback 함수를 수행
cgroup_fork_callbacks(p)
p에게 assign된 cpu가 cpus_allowed에 세팅되어있지 않거나
assign된 cpu가 online상태가 아닐 경우 현재 프로세서에
assign시킴
set_task_cpu(p,
smp_processor_id())
p->real_parent =
current->real_parent;
p->parent_exec_id =
current->parent_exec_id;
recalc_sigpending()
CLONE_PARENT, CLONE_THREAD 플래그가 설정되어있을 경
우 real_parent, parent_exec_id를 부모에게서 복사한다. 그렇
지 않을때는 각각 current, current->self_exec_id로 세팅한다
signal pending flag를 재설정할 때 호출된다
init/main.c
rest_init()
kernel_thread(
kernel_init)
TIF_SIGPENDING이 세팅되어있다면 에러처리
do_fork()
copy_process()
signal_pending(current)
pid가 존재한다면
1.부모의 children에 p->sibling에 추가한다
2.이를 통해 children에 자신을 등록한다
3.p의 ptrace 관련 멤버들을 초기화
p->pid exists?
thread_group_leader(p)?
attach_pid(p, PIDTYPE_PID,
pid);
total_forks++
p가 thread group의 leader라면
1.CLONE_NEWPID가 세팅되어있으면 child_reaper로 설정한다
2.pid를 leader_pid에 설정
3.signal->tty에 대한 참조카운트를 1감소
4.부모의 signal->tty를 다시 참조함
5.새로 생성된 프로세스는 current task group과 세션에 추가
되어진다.
6.p를 init_task의 task list에 추가
7. process_counts를 증가시킨다
pid를 id network에 추가한다
fork된 횟수를 1 증가시킴
proc_fork_connector(p)
cgroup_post_fork(p)
Provide a connector that reports process events to
userspace. Send events such as fork, exec, id change (uid,
gid, suid, etc), and exit. CONFIG_PROC_EVENTS가
설정되어있을 경우 사용
use_task_css_set_links가 true이고 child->cg_list가 비어있다면
child->cgroups->tasks에 child를 add한다
init/main.c
현재 프로세스의 memory policy를 초기화한다
(current->mempolicy=NULL)
rest_init()
kernel_thread(kernel_init)
numa_deafult_policy()
커널스레드 데몬인 kthreadd를 생성한다
kthread_create_list에 등록된 스레드를
create_kthread()함수를 호출하여 생성
kernel_thread(kthreadd)
kthreadd
find_task_by_pid_ns(
pid, &init_pid_ns)
init_idle_bootup_task(
current)
rcu_scheduler_starting()
schedule()
kthreadd의 task_struct를 찾는다
current의 sched_class를 idle_sched_class로 초기화
rcu_scheduler_active = 1
1.
2.
3.
cpu_idle()
Done!
4.
current의 need_resched flag를 clear
preemption에 의해 scheduling되지 않았고 pending된
시그널이 있다면 task의 state를 TASK_RUNNING으로
설정. pending된 시그널이 없다면 task를 deactivate한다
prev task가 여전히 active되어있을 경우만 run queue에
삽입. deactivate_task()가 호출되면 deactive
컨텍스트 스위칭을 한다. prev->mm과 register를 새로운
task의 mm과 register로 변경
함수를 호출한 프로세스가 TIF_NEED_RESCHED 플래그가
세팅될때까지 무한 idle()
init/main.c
kernel_init
현재 프로세스의 memory policy를 초기화한다
(current->mempolicy=NULL)
current task의 pid를 cad_pid에 할당
cad -> ctrl+alt+del reboot
INITCALLS(.initcall*.init)섹션의 함수를 모두
호출한다
current task를 실행시킬 cpu를 찾는다
set_cpus_allowed_ptr(current,
cpu_all_mask)
init_pid_ns.child_reaper =
current
cad_pid = task_pid(current)
smp_prepare_cpus(
setup_max_cpus)
do_pre_smp_initcalls()
cpu_active_bits에 current cpu의 id를 세팅
online되어있지 않은 cpu가 있다면
online시킨다
1.
2.
3.
4.
5.
ncores값의 sanity check하고 NR_CPUS보다 클 경우 값
조정
cpuid 에 해당하는 cpu의 cpu_date 자료구조를 가져와서
loops_per_jiffy세팅
cpu의 local clock event를 세팅한다
변경된 max_cpus에 맞게 cpu_present_map 재설정
멀티코어인 경우에 scu(snoop control unit)를 enable,
secondary cpu를 부팅시킨다
start_boot_trace()
smp_init()
ramdisk_execute_command가 없다면 /init 으로
초기화
child reaper로 등록
sched_init_smp()
부트 트레이서에게 pre_smp_initcalls이 전부 호출되었음을
알린다.
sched domain을 초기화하여 각 cpu에 설정한다. cpu hotplug
이벤트시에 domain을 업데이트할 수 있게 notifier를 설정한다
do_basic_setup()
/dev/console을 열어 fd 0,1,2를 만든다.
/sbin/init (or /etc/init or /bin/init or /bin/sh)을
실행한다
ramdisk_execute_command =
"/init"
init_post()
1.
2.
3.
4.
5.
6.
rcu_sched_grace_period 함수를 실행시킬 커널스래드를
생성한다
프로세서별로 워크큐 events를 생성
kernel thread cpuset, khelper를 만든다
sysfs의 트리구조에 각 서브시스템의 entry를 초기화한다.
platform_bus 디바이스를 등록
irq에 대한 proc entry를 초기화한다
initcall 섹션의 함수들을 호출한다
init/main.c
kernel_init 함수의 주요 Operation
1.
current task를 실행시킬 cpu를 찾고 current를 child reaper로 등록
2.
current task의 pid를 cad_pid에 할당. cad -> ctrl+alt+del reboot
3.
ncores값의 sanity check하고 NR_CPUS보다 클 경우 값 조정. cpuid 에 해당하는 cpu의 cpu_date 자료구조를
가져와서 loops_per_jiffy세팅. cpu의 local clock event를 세팅한다. 변경된 max_cpus에 맞게 cpu_present_map
재설정. 멀티코어인 경우에 scu(snoop control unit)를 enable, secondary cpu를 부팅시킨다
4.
INITCALLS(.initcall*.init)섹션의 함수를 모두 호출한다
5.
부트 트레이서에게 pre_smp_initcalls이 전부 호출되었음을 알린다.
6.
cpu_active_bits에 current cpu의 id를 세팅. online되어있지 않은 cpu가 있다면 online시킨다
7.
sched domain을 초기화하여 각 cpu에 설정한다. cpu hotplug 이벤트시에 domain을 업데이트할 수 있게 notifier를
설정한다
8.
rcu_sched_grace_period 함수를 실행시킬 커널스래드를 생성한다. 프로세서별로 워크큐 events를 생성. kernel thread
cpuset, khelper를 만든다. sysfs의 트리구조에 각 서브시스템의 entry를 초기화한다. platform_bus 디바이스를 등록.
irq에 대한 proc entry를 초기화한다. initcall 섹션의 함수들을 호출한다.
9.
ramdisk_execute_command 검사
10. /dev/console을 열어 fd 0,1,2를 만든다
11. init을 실행한다