2017년 1월 25일 수요일

process descriptor 와 thread_info

커널은 프로세스 목록을 태스크 리스트(tast list)라고 하는 연결 리스트로 저장한다. 각 항목이 task_struct 구조체인 연결 리스트이다. 리눅스 시스템에서 생성된 전체 프로세스들이 모두 저장되기 때문에, 어느 한 task_struct를 선택하여 리스트를 따라가보면 전체 리스트를 탐색하고 다시 본인 task_struct 자리로 돌아온다. 모든 프로세스는 PID가 1 인 init_task 라는 initial task로부터 생성되어진 자식들이다. for_each_process 함수 코드를 보면 init_task부터 시작하여 모든 프로세스를 순회하는 코드를 확인할 수 있다.

이 구조체를 프로세스 서술자(process descriptor)라고 부르며, 프로세스 서술자에는 사용 중인 파일, 프로세서의 주소 공간, 대기 중인 시그널, 프로세스의 상태 등 실행 중인 프로그램을 설명하는 많은 정보가 들어있기 때문에 용량이 큰 구조체이다. 32비트 시스템에서는 약 1.7KB이며, 64비트 시스템에서는 약 3.2KB 정도이다.

프로세스 별로 프로세스 서술자를 할당하여, 프로세스에 관한 많은 정보를 저장한다고 하였다. 하지만 여기에 프로세스에 관련한 아직 말하지 않은 중요한 정보가 더 존재한다. 바로, 프로세스 커널 스택(process kernel stack)이다. 이 프로세스 커널 스택은 THREAD_SIZE 만큼의 메모리 크기로 할당되며, struct thread_info를 포함한다. 커널 스택이 상위 메모리 주소에서 아래로 스택을 쌓아사면서 자라나고, 그 반대 방향의 끝에는 thread_info가 자리하고 있다.

커널 스택과 thread_info task_struct 관계

THREAD_SIZE 는 ARM architecture에서 보통 32비트는 0x2000 를 사용했고, 64비트로 넘어오면서 0x4000로 2배로 커졌다.(아래 커널 코드 설명도 모두 ARM architecture 기반으로 설명하였다.)

프로세스에서 프로세스 서술자(혹은 커널 스택)에 접근하는 일은 매우 빈번한 일이기 때문에 latency가 없이 접근 가능한지 여부는 매우 중요하다. 이를 위해 ARM architecture에서는 sp 레지스터로 스택포인터(stack pointer)주소를 저장하고, 이를 참조하여 매우 빠르게 현재 스택 주소와 task_struct 를 접근 가능하다.

thread_info task_struct current_thread_info THREAD_SIZE

리눅스 커널 코드에서 sp, task_struct 와 thread_info 를 어떻게 접근하는 위 그림을 참고하여 각 코드를 확인해보자.

register unsigned long current_stack_pointer asm ("sp");

현재 스택 포인트가 가리키는 주소는 sp 레지스터를 읽어오면 확인 가능하다. 커널 코드에서 스택의 움직임에 따라(스택이 자라나고 줄어듦에 따라) sp 가 가리키는 주소는 계속 관리되고 있기 때문에, 항상 참조 가능하다.

static inline struct thread_info *current_thread_info(void)
{
    return (struct thread_info *)
        (current_stack_pointer & ~(THREAD_SIZE - 1));
}

thread_info 는 sp 레지스터에 하위 비트를 지워버리는 방법으로 참조 가능하다. THREAD_SIZE 만큼의 하위 비트를 0로 마스킹해버면, STACK 의 최하위 주소인 thread_info 의 주소를 얻어낼 수 있다. 위 current_thread_info 인라인 함수를 사용하면, 커널 코드에서 언제든지 현재 running 중인 thread 의 thread_info 정보를 가져올 수 있다.

#define get_current() (current_thread_info()->task)
#define current get_current()

task_struct 구조체는 thread_info->task 포인터 주소를 참고하는 방법으로 참조 가능하다. sp 를 이용하여 현재의 thread_info 를 빠르게 가져올 수 있으니, thread_info 의 멤버 중 task_struct 를 가리키고 있는 task 포인트를 이용하는 것이다. 커널 코드에서 current 라는 매크로로 현재 running 중인 process descriptor(task_struct)를 가져올 수 있다. 커널 코드를 보다보면 current 매크로를 사용하는 부분이 많이 나오는데, running 중인 process 정보를 가져오는 것이라고 생각하면 된다.

지금까지 살펴 본 위의 코드 내용은 ARM architecture v7 까지의 이야기이다. 즉, 32비트 계열에서의 주소 참조 방식이며, ARMv8 (64bit - Aarch64)에서는 sp_el0 레지스터를 하나 더 할당하여, thread_info 의 주소를 저장한다. 아래 그림을 참고 하자.


ARMv8 에서는 sp 는 스택의 현재 주소를 가리키며, sp_el0 는 thread_info 주소를 가리킨다. 따라서 current_thread_info 를 수행할때, sp 값의 마스킹없이 sp_el0 값을 읽어서 바로 참조하면 된다. thread_info 주소값 참조를 위해 레지스터가 하나 더 추가된 것이다.
/*
 * struct thread_info can be accessed directly via sp_el0.
 */
static inline struct thread_info *current_thread_info(void)
{
    unsigned long sp_el0;

    asm ("mrs %0, sp_el0" : "=r" (sp_el0));

    return (struct thread_info *)sp_el0;
}

sp_el0 레지스터의 추가로, current_thread_info 를 가져올때, 항상 실행하던 THREAD_SIZE 의 마스킹 동작이 없어졌다. 이점은 작은 변화이지만, 코드의 호출 빈도를 생각해보면 큰 이점이다.

위에서 확인한 task_struct 와 thread_info 의 참조 관계는 상당히 오랜시간을 지켜왔다. 하지만 이 관계를 깨는 변경 사항이 커널 4.9 에 반영(commit id : c65eacbe29) 되었다. THREAD_INFO_IN_TASK feature 이다. CONFIG_THREAD_INFO_IN_TASK 가 enable 되면 커널 스택에 포함되어 있던 thread_info 구조체 변수는 task_struct 속으로 들어간다.

CONFIG_THREAD_INFO_IN_TASK

위의 그림에서도 확인해보면, 커널 스택은 온전히 스택 데이터 만으로 채워지게 된다. task_struct 의 정의를 보면, thread_info 는 구조체의 맨 앞에 위치하도록 되어있다. task_struct 와 thread_info 접근을 동일한 주소값을 참조하여 접근하기 위함이다. 해당 주소값은 sp_el0 에 저장되어 있고, 해당 주소를 읽어서 task_struct 로 캐스팅하면 current task descriptor 를 접근할 수 있고, thread_info 로 캐스팅하면 thread_info 를 접근할 수 있다.

static __always_inline struct task_struct *get_current(void)
{
    unsigned long sp_el0;

    asm ("mrs %0, sp_el0" : "=r" (sp_el0));

    return (struct task_struct *)sp_el0;
}

sp_el0 레지스터를 읽어서, task_sturct 구조체로 캐스팅을 하면 current task_struct 구조체 변수가 된다.

#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
 * For CONFIG_THREAD_INFO_IN_TASK kernels we need  for the
 * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
 * including  can cause a circular dependency on some platforms.
 */
#include 
#define current_thread_info() ((struct thread_info *)current)
#endif

current는 sp_el0 레지스터가 가리키는 주소라고 하였다. CONFIG_THREAD_INFO_IN_TASK 가 세팅되면, task_sturct 구조체에 thread_info 구조체가 맨 앞에 위치하기 때문에 둘을 참조하는 메모리 주소는 동일하다. 이 주소를 thread_info 로 캐스팅하면 current_thread_info를 참조할 수 있다.

Navigation of this blog:이 블로그 한눈에 보기

2017년 1월 22일 일요일

process vs thread - 차이점 알아보기

프로세스는 실행 중인 프로그램의 객체(instance)를 의미한다고 했다.
그럼, 아직 말하지 않은 thread는 무엇인가?

Thread 란 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들을 대부분 공유하면서 실행되는 객체를 말한다. 근래의 대부분의 시스템은 multi thread program 이 기본이며, 이는 하나의 프로세스 내에 여러 개의 thread 들이 실행 가능하다는 의미이다.

기본적으로 하나의 process가 생성되면 하나의 thread가 같이 생성된다. 이를 mian thread라고 부르며, thread를 추가로 생성하지 않는 한 모든 프로그램 코드는 메인 thread에서 실행된다. 이 process 는 fork를 통해 child process를 가질 수 있으며 이를 multi thread 라고 한다. 리눅스에서 process 와 thread 를 구분을 하지 않는다. 둘 다 동일하게 task_struct 로 표현되며, process 와 thread 는 리눅스에서 둘 간이 구별되는 조금 다른 처리를 받을 뿐이다.

프로세스가 생성되고 그 프로세스 내의 쓰레드들은 프로세스 내의 자원을 공유하면서, 각 쓰레드의 프로그램을 실행한다. 리눅스 코드 영역과 라이브러리를 프로세스 내부로는 공유하며, 각 쓰레드들은 실행 위치를 나타내는 PC(Program Counter), register 정보들을 포함한 스택, 변수 할당으로 채워지는 데이터 세그먼트 각각 저장관리한다. 각 쓰레드들이 독립적으로 실행한다는 점을 생각하면, 실행 정보와 관련한 것들은 각각 정장되어 있어야 한다는 점은 너무 당연한 것이다.

아래 프로세스와 쓰레드 간의 자원 공유 상태를 그림으로 표현해봤다.


쓰레드는 프로세스 내에서 코드, 전역 변수, 파일등을 공유한다.(리눅스의 clone 함수로 스레드가 생성될때, flag에 의해 공유가 얼마정도 허용되는지를 정한다.) 이는 프로그램을 구현할 때, 매우 중요한 요소이다. 매번 프로세스를 생성하는 것보다 스레드를 생성하는 것이 효율적이며, 특히 멀티 프로세서 환경에서는 더욱 효과가 탁월하다. 스레드 간의 통신이 필요한 경우 전역 변수를 이용하여 쉽게 데이터를 주고받을 수 있다. 프로세스 간의 통신은 IPC 등의 인터페이스가 필요하며, 이는 훨씬 비효율적인 일이 될 것이다.
스레드의 장점을 정리하면 다음과 같다.  
- 응답성 : 사용자와의 대화형 프로그램을 스레드로 구현시, 상호작용에 대한 부분을 스레드로 구현하여 응답성이 뛰어남
- 자원 공유 : 프로세스 간의 통신에 큰 자원이 필요하다. 스레드 간에는 자원 공유가 가능하여, 이는 큰 이점임
- 경제성 : 프로세스를 생성하는 것보다 스레드를 생성하는 시간이 적게 걸림
- 규모 가변성 : multi processor 에서의 multi threading 은 병렬성을 증가 시킴 

이와 같이 프로세스 내의 스레드 간에는 전역 변수 및 메모리 공간을 공유할 수 있다. 이는 큰 장점이지만, 개발자로써는 큰 문제의 여지가 생긴다. 공유 되는 자원은 항상 충돌의 문제가 생긴다. 따라서 스레드 간에 통신 간에 충돌 문제가 발생하지 않도록 동기화 설계를 잘 고려하여 문제가 발생하지 않도록 하여야 한다. 동기화가 고려되지 않은 변수나 메모리의 공유는 오류를 발생시키며, multi thread 환경에서 자칫 디버깅이 어려운 상황을 만들 수 있다.

리눅스 커널에서는 이런 동기화를 해결하기 위한 여러 매커니즘을 제공한다. 이는 뒤에 다시 정리하기로 한다.

Navigation of this blog:이 블로그 한눈에 보기

2017년 1월 21일 토요일

process 개념 알아보기

Process management 에 대해 확인해보기 전에,  process 개념부터 확인해보자.

살짝  wikipedia 에 있는 정의를 빌려보자면 아래와같다.

In computing, a process is an instance of a computer program that is being executed. It contains the program code and its current activity. Depending on the operating system (OS), a process may be made up of multiple threads of execution that execute instructions concurrently.

프로세스는 실행 중인 프로그램으로, 커널 내부 에이터, 프로세서 상태, 메모리 주소 공간 등의 자원을 포함한다. 간단히 생각하면 실행(executing)을 하기 위한 모든 정보를 담고 있는 객체(instance)로 보는 것이 좋을 것이다.


리눅스에서는 process 정의를 task_struct 라는 구조체로 담아낸다.  이를 프로세스 서술자(process description)이라고 부른다. 이 안의 멤버들로 프로세스 실행을 하기 위한 많은 정보들을 담고 있는 것이다.
몇가지만 살펴보면 아래와같다.
state -> process 의 동작 상태
on_cpu -> SMP 에서의 현재 process 의 동작 cpu 번호
prio, static_prio, normal_prio; -> scheduling 에서의 priority 값
struct sched_entity se; -> sched entity 정보
pid_t pid; -> pid 값


task_struct 의 각 세부내역은 후에 코드를 볼때, 필요부분을 자세히 살펴보기로 하고, 아래는 구조체 전체 내역이다.


struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
 /*
  * For reasons of header soup (see current_thread_info()), this
  * must be the first element of task_struct.
  */
 struct thread_info thread_info;
#endif
 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
 void *stack;
 atomic_t usage;
 unsigned int flags; /* per process flags, defined below */
 unsigned int ptrace;

#ifdef CONFIG_SMP
 struct llist_node wake_entry;
 int on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK
 unsigned int cpu; /* current CPU */
#endif
 unsigned int wakee_flips;
 unsigned long wakee_flip_decay_ts;
 struct task_struct *last_wakee;

 int wake_cpu;
#endif
 int on_rq;

 int prio, static_prio, normal_prio;
 unsigned int rt_priority;
 const struct sched_class *sched_class;
 struct sched_entity se;
 struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
 struct task_group *sched_task_group;
#endif
 struct sched_dl_entity dl;

#ifdef CONFIG_PREEMPT_NOTIFIERS
 /* list of struct preempt_notifier: */
 struct hlist_head preempt_notifiers;
#endif

#ifdef CONFIG_BLK_DEV_IO_TRACE
 unsigned int btrace_seq;
#endif

 unsigned int policy;
 int nr_cpus_allowed;
 cpumask_t cpus_allowed;

#ifdef CONFIG_PREEMPT_RCU
 int rcu_read_lock_nesting;
 union rcu_special rcu_read_unlock_special;
 struct list_head rcu_node_entry;
 struct rcu_node *rcu_blocked_node;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
#ifdef CONFIG_TASKS_RCU
 unsigned long rcu_tasks_nvcsw;
 bool rcu_tasks_holdout;
 struct list_head rcu_tasks_holdout_list;
 int rcu_tasks_idle_cpu;
#endif /* #ifdef CONFIG_TASKS_RCU */

#ifdef CONFIG_SCHED_INFO
 struct sched_info sched_info;
#endif

 struct list_head tasks;
#ifdef CONFIG_SMP
 struct plist_node pushable_tasks;
 struct rb_node pushable_dl_tasks;
#endif

 struct mm_struct *mm, *active_mm;
 /* per-thread vma caching */
 u32 vmacache_seqnum;
 struct vm_area_struct *vmacache[VMACACHE_SIZE];
#if defined(SPLIT_RSS_COUNTING)
 struct task_rss_stat rss_stat;
#endif
/* task state */
 int exit_state;
 int exit_code, exit_signal;
 int pdeath_signal;  /*  The signal sent when the parent dies  */
 unsigned long jobctl; /* JOBCTL_*, siglock protected */

 /* Used for emulating ABI behavior of previous Linux versions */
 unsigned int personality;

 /* scheduler bits, serialized by scheduler locks */
 unsigned sched_reset_on_fork:1;
 unsigned sched_contributes_to_load:1;
 unsigned sched_migrated:1;
 unsigned sched_remote_wakeup:1;
 unsigned :0; /* force alignment to the next boundary */

 /* unserialized, strictly 'current' */
 unsigned in_execve:1; /* bit to tell LSMs we're in execve */
 unsigned in_iowait:1;
#if !defined(TIF_RESTORE_SIGMASK)
 unsigned restore_sigmask:1;
#endif
#ifdef CONFIG_MEMCG
 unsigned memcg_may_oom:1;
#ifndef CONFIG_SLOB
 unsigned memcg_kmem_skip_account:1;
#endif
#endif
#ifdef CONFIG_COMPAT_BRK
 unsigned brk_randomized:1;
#endif

 unsigned long atomic_flags; /* Flags needing atomic access. */

 struct restart_block restart_block;

 pid_t pid;
 pid_t tgid;

#ifdef CONFIG_CC_STACKPROTECTOR
 /* Canary value for the -fstack-protector gcc feature */
 unsigned long stack_canary;
#endif
 /*
  * pointers to (original) parent process, youngest child, younger sibling,
  * older sibling, respectively.  (p->father can be replaced with
  * p->real_parent->pid)
  */
 struct task_struct __rcu *real_parent; /* real parent process */
 struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
 /*
  * children/sibling forms the list of my natural children
  */
 struct list_head children; /* list of my children */
 struct list_head sibling; /* linkage in my parent's children list */
 struct task_struct *group_leader; /* threadgroup leader */

 /*
  * ptraced is the list of tasks this task is using ptrace on.
  * This includes both natural children and PTRACE_ATTACH targets.
  * p->ptrace_entry is p's link on the p->parent->ptraced list.
  */
 struct list_head ptraced;
 struct list_head ptrace_entry;

 /* PID/PID hash table linkage. */
 struct pid_link pids[PIDTYPE_MAX];
 struct list_head thread_group;
 struct list_head thread_node;

 struct completion *vfork_done;  /* for vfork() */
 int __user *set_child_tid;  /* CLONE_CHILD_SETTID */
 int __user *clear_child_tid;  /* CLONE_CHILD_CLEARTID */

 cputime_t utime, stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
 cputime_t utimescaled, stimescaled;
#endif
 cputime_t gtime;
 struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
 seqcount_t vtime_seqcount;
 unsigned long long vtime_snap;
 enum {
  /* Task is sleeping or running in a CPU with VTIME inactive */
  VTIME_INACTIVE = 0,
  /* Task runs in userspace in a CPU with VTIME active */
  VTIME_USER,
  /* Task runs in kernelspace in a CPU with VTIME active */
  VTIME_SYS,
 } vtime_snap_whence;
#endif

#ifdef CONFIG_NO_HZ_FULL
 atomic_t tick_dep_mask;
#endif
 unsigned long nvcsw, nivcsw; /* context switch counts */
 u64 start_time;  /* monotonic time in nsec */
 u64 real_start_time; /* boot based time in nsec */
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
 unsigned long min_flt, maj_flt;

 struct task_cputime cputime_expires;
 struct list_head cpu_timers[3];

/* process credentials */
 const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
 const struct cred __rcu *real_cred; /* objective and real subjective task
      * credentials (COW) */
 const struct cred __rcu *cred; /* effective (overridable) subjective task
      * credentials (COW) */
 char comm[TASK_COMM_LEN]; /* executable name excluding path
         - access with [gs]et_task_comm (which lock
           it with task_lock())
         - initialized normally by setup_new_exec */
/* file system info */
 struct nameidata *nameidata;
#ifdef CONFIG_SYSVIPC
/* ipc stuff */
 struct sysv_sem sysvsem;
 struct sysv_shm sysvshm;
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
/* hung task detection */
 unsigned long last_switch_count;
#endif
/* filesystem information */
 struct fs_struct *fs;
/* open file information */
 struct files_struct *files;
/* namespaces */
 struct nsproxy *nsproxy;
/* signal handlers */
 struct signal_struct *signal;
 struct sighand_struct *sighand;

 sigset_t blocked, real_blocked;
 sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
 struct sigpending pending;

 unsigned long sas_ss_sp;
 size_t sas_ss_size;
 unsigned sas_ss_flags;

 struct callback_head *task_works;

 struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
 kuid_t loginuid;
 unsigned int sessionid;
#endif
 struct seccomp seccomp;

/* Thread group tracking */
    u32 parent_exec_id;
    u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed,
 * mempolicy */
 spinlock_t alloc_lock;

 /* Protection of the PI data structures: */
 raw_spinlock_t pi_lock;

 struct wake_q_node wake_q;

#ifdef CONFIG_RT_MUTEXES
 /* PI waiters blocked on a rt_mutex held by this task */
 struct rb_root pi_waiters;
 struct rb_node *pi_waiters_leftmost;
 /* Deadlock detection and priority inheritance handling */
 struct rt_mutex_waiter *pi_blocked_on;
#endif

#ifdef CONFIG_DEBUG_MUTEXES
 /* mutex deadlock detection */
 struct mutex_waiter *blocked_on;
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
 unsigned int irq_events;
 unsigned long hardirq_enable_ip;
 unsigned long hardirq_disable_ip;
 unsigned int hardirq_enable_event;
 unsigned int hardirq_disable_event;
 int hardirqs_enabled;
 int hardirq_context;
 unsigned long softirq_disable_ip;
 unsigned long softirq_enable_ip;
 unsigned int softirq_disable_event;
 unsigned int softirq_enable_event;
 int softirqs_enabled;
 int softirq_context;
#endif
#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH 48UL
 u64 curr_chain_key;
 int lockdep_depth;
 unsigned int lockdep_recursion;
 struct held_lock held_locks[MAX_LOCK_DEPTH];
 gfp_t lockdep_reclaim_gfp;
#endif
#ifdef CONFIG_UBSAN
 unsigned int in_ubsan;
#endif

/* journalling filesystem info */
 void *journal_info;

/* stacked block device info */
 struct bio_list *bio_list;

#ifdef CONFIG_BLOCK
/* stack plugging */
 struct blk_plug *plug;
#endif

/* VM state */
 struct reclaim_state *reclaim_state;

 struct backing_dev_info *backing_dev_info;

 struct io_context *io_context;

 unsigned long ptrace_message;
 siginfo_t *last_siginfo; /* For ptrace use.  */
 struct task_io_accounting ioac;
#if defined(CONFIG_TASK_XACCT)
 u64 acct_rss_mem1; /* accumulated rss usage */
 u64 acct_vm_mem1; /* accumulated virtual memory usage */
 cputime_t acct_timexpd; /* stime + utime since last update */
#endif
#ifdef CONFIG_CPUSETS
 nodemask_t mems_allowed; /* Protected by alloc_lock */
 seqcount_t mems_allowed_seq; /* Seqence no to catch updates */
 int cpuset_mem_spread_rotor;
 int cpuset_slab_spread_rotor;
#endif
#ifdef CONFIG_CGROUPS
 /* Control Group info protected by css_set_lock */
 struct css_set __rcu *cgroups;
 /* cg_list protected by css_set_lock and tsk->alloc_lock */
 struct list_head cg_list;
#endif
#ifdef CONFIG_INTEL_RDT_A
 int closid;
#endif
#ifdef CONFIG_FUTEX
 struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
 struct compat_robust_list_head __user *compat_robust_list;
#endif
 struct list_head pi_state_list;
 struct futex_pi_state *pi_state_cache;
#endif
#ifdef CONFIG_PERF_EVENTS
 struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
 struct mutex perf_event_mutex;
 struct list_head perf_event_list;
#endif
#ifdef CONFIG_DEBUG_PREEMPT
 unsigned long preempt_disable_ip;
#endif
#ifdef CONFIG_NUMA
 struct mempolicy *mempolicy; /* Protected by alloc_lock */
 short il_next;
 short pref_node_fork;
#endif
#ifdef CONFIG_NUMA_BALANCING
 int numa_scan_seq;
 unsigned int numa_scan_period;
 unsigned int numa_scan_period_max;
 int numa_preferred_nid;
 unsigned long numa_migrate_retry;
 u64 node_stamp;   /* migration stamp  */
 u64 last_task_numa_placement;
 u64 last_sum_exec_runtime;
 struct callback_head numa_work;

 struct list_head numa_entry;
 struct numa_group *numa_group;

 /*
  * numa_faults is an array split into four regions:
  * faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer
  * in this precise order.
  *
  * faults_memory: Exponential decaying average of faults on a per-node
  * basis. Scheduling placement decisions are made based on these
  * counts. The values remain static for the duration of a PTE scan.
  * faults_cpu: Track the nodes the process was running on when a NUMA
  * hinting fault was incurred.
  * faults_memory_buffer and faults_cpu_buffer: Record faults per node
  * during the current scan window. When the scan completes, the counts
  * in faults_memory and faults_cpu decay and these values are copied.
  */
 unsigned long *numa_faults;
 unsigned long total_numa_faults;

 /*
  * numa_faults_locality tracks if faults recorded during the last
  * scan window were remote/local or failed to migrate. The task scan
  * period is adapted based on the locality of the faults with different
  * weights depending on whether they were shared or private faults
  */
 unsigned long numa_faults_locality[3];

 unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */

#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
 struct tlbflush_unmap_batch tlb_ubc;
#endif

 struct rcu_head rcu;

 /*
  * cache last used pipe for splice
  */
 struct pipe_inode_info *splice_pipe;

 struct page_frag task_frag;

#ifdef CONFIG_TASK_DELAY_ACCT
 struct task_delay_info *delays;
#endif
#ifdef CONFIG_FAULT_INJECTION
 int make_it_fail;
#endif
 /*
  * when (nr_dirtied >= nr_dirtied_pause), it's time to call
  * balance_dirty_pages() for some dirty throttling pause
  */
 int nr_dirtied;
 int nr_dirtied_pause;
 unsigned long dirty_paused_when; /* start of a write-and-pause period */

#ifdef CONFIG_LATENCYTOP
 int latency_record_count;
 struct latency_record latency_record[LT_SAVECOUNT];
#endif
 /*
  * time slack values; these are used to round up poll() and
  * select() etc timeout values. These are in nanoseconds.
  */
 u64 timer_slack_ns;
 u64 default_timer_slack_ns;

#ifdef CONFIG_KASAN
 unsigned int kasan_depth;
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 /* Index of current stored address in ret_stack */
 int curr_ret_stack;
 /* Stack of return addresses for return function tracing */
 struct ftrace_ret_stack *ret_stack;
 /* time stamp for last schedule */
 unsigned long long ftrace_timestamp;
 /*
  * Number of functions that haven't been traced
  * because of depth overrun.
  */
 atomic_t trace_overrun;
 /* Pause for the tracing */
 atomic_t tracing_graph_pause;
#endif
#ifdef CONFIG_TRACING
 /* state flags for use by tracers */
 unsigned long trace;
 /* bitmask and counter of trace recursion */
 unsigned long trace_recursion;
#endif /* CONFIG_TRACING */
#ifdef CONFIG_KCOV
 /* Coverage collection mode enabled for this task (0 if disabled). */
 enum kcov_mode kcov_mode;
 /* Size of the kcov_area. */
 unsigned kcov_size;
 /* Buffer for coverage collection. */
 void  *kcov_area;
 /* kcov desciptor wired with this task or NULL. */
 struct kcov *kcov;
#endif
#ifdef CONFIG_MEMCG
 struct mem_cgroup *memcg_in_oom;
 gfp_t memcg_oom_gfp_mask;
 int memcg_oom_order;

 /* number of pages to reclaim on returning to userland */
 unsigned int memcg_nr_pages_over_high;
#endif
#ifdef CONFIG_UPROBES
 struct uprobe_task *utask;
#endif
#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
 unsigned int sequential_io;
 unsigned int sequential_io_avg;
#endif
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 unsigned long task_state_change;
#endif
 int pagefault_disabled;
#ifdef CONFIG_MMU
 struct task_struct *oom_reaper_list;
#endif
#ifdef CONFIG_VMAP_STACK
 struct vm_struct *stack_vm_area;
#endif
#ifdef CONFIG_THREAD_INFO_IN_TASK
 /* A live task holds one reference. */
 atomic_t stack_refcount;
#endif
/* CPU-specific state of this task */
 struct thread_struct thread;
/*
 * WARNING: on x86, 'thread_struct' contains a variable-sized
 * structure.  It *MUST* be at the end of 'task_struct'.
 *
 * Do not put anything below here!
 */
};

Navigation of this blog:이 블로그 한눈에 보기

2017년 1월 15일 일요일

리눅스 커널 소스 다운로드

커널 분석을 하기에 앞서 커널 소스를 다운로드 하여 커널 분석을 할 수 있는 준비를 해본다.
git 의 clone 커맨드로 아래와같이 kernel.org 의 stable 커널 소스를 다운로드 가능하다.

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
$ cd linux-stable
<커널 소스 다운로드 화면>

소스 다운로드가 완료되면 끝.
이제 커널 코드 분석을 시작할 수 있다.

그외 참고 git repositories
kernel.org 에서 제공하는 git repository 목록을 확인 가능함
http://git.kernel.org/cgit

안드로이드(android) 소스 repository 다운로드

$ git clone https://android.googlesource.com/kernel/common.git

- 커널 분석을 시작하기 위한 준비사항
만약, 리눅스를 막 시작한 사람이라면 git, ctags, cscope, vi 대해서는 기본적인 사용법 정도는 꼭 알고 시작하는 것이 좋다.

Navigation of this blog:이 블로그 한눈에 보기