task_struct 를 이용한 시스템 콜을 만드는 방법을 설명하고 있다. 프로세스가 생성되면, 커널은 프로세스 관리를 위해 어떤 특정한 장치를 마련해 프로세스에 대한 정보를 저장한다. 여기서 특별한 장치는 곧 task_struct 라고 볼 수 있다.
내가 나중에 어떤 프로세스의 정보를 출력하거나 사용해야 할 일이 생긴다면, task_struct 를 반드시 사용해야 한다.
include/linux/sched.h 파일을 보면 아래와 같이 task_struct 를 선언하고 있다.
struct task_struct { /* * offsets of these are hardcoded elsewhere - touch with care */ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ unsigned long flags; /* per process flags, defined below */ int sigpending; mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ struct exec_domain *exec_domain; volatile long need_resched; unsigned long ptrace; int lock_depth; /* Lock depth */ /* * offset 32 begins here on 32-bit platforms. We keep * all fields in a single cacheline that are needed for * the goodness() loop in schedule(). */ long counter; long nice; unsigned long policy; struct mm_struct *mm; int processor; /* * cpus_runnable is ~0 if the process is not running on any * CPU. It's (1 << cpu) if it's running on a CPU. This mask * is updated under the runqueue lock. * * To determine whether a process might run on a CPU, this * mask is AND-ed with cpus_allowed. */ unsigned long cpus_runnable, cpus_allowed; /* * (only the 'next' pointer fits into the cacheline, but * that's just fine.) */ struct list_head run_list; unsigned long sleep_time; struct task_struct *next_task, *prev_task; struct mm_struct *active_mm; struct list_head local_pages; unsigned int allocation_order, nr_local_pages; /* task state */ struct linux_binfmt *binfmt; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ /* ??? */ unsigned long personality; int did_exec:1; unsigned task_dumpable:1; pid_t pid; pid_t pgrp; pid_t tty_old_pgrp; pid_t session; pid_t tgid; /* boolean value for session group leader */ int leader; /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid) */ struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; struct list_head thread_group; /* PID hash table linkage. */ struct task_struct *pidhash_next; struct task_struct **pidhash_pprev; wait_queue_head_t wait_chldexit; /* for wait4() */ struct completion *vfork_done; /* for vfork() */ unsigned long rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; struct tms times; unsigned long start_time; long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1; /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; gid_t groups[NGROUPS]; kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; char comm[16]; /* file system info */ int link_count, total_link_count; struct tty_struct *tty; /* NULL if no tty */ unsigned int locks; /* How many file locks are being held */ /* ipc stuff */ struct sem_undo *semundo; struct sem_queue *semsleeping; /* CPU-specific state of this task */ struct thread_struct thread; /* filesystem information */ struct fs_struct *fs; /* open file information */ struct files_struct *files; /* namespace */ struct namespace *namespace; /* signal handlers */ spinlock_t sigmask_lock; /* Protects signal and blocked */ struct signal_struct *sig; sigset_t blocked; struct sigpending pending; unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; /* Thread group tracking */ u32 parent_exec_id; u32 self_exec_id; /* Protection of (de-)allocation: mm, files, fs, tty */ spinlock_t alloc_lock; /* journalling filesystem info */ void *journal_info; };
시스템 콜 작성
가장 먼저할 것은 새로운 시스템 콜을 위한 시스템 호출 번호를 할당하는것이다. 먼저 unistd.h 파일에 다음을 추가한다.
#define __NR_gettaskinfo 260
entry.S 파일에 다음을 추가한다.
.long SYMBOL_NAME(sys_ni_syscall) /* sys_set_tid_address */ .long SYMBOL_NAME(sys_newsyscall) /* sys_set_tid_address */ .long SYMBOL_NAME(sys_gettaskinfo) /* sys_set_tid_address */ // 추가 .rept NR_syscalls-(.-sys_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) .endr
이제 시스템 콜 함수를 작성할 차례다. 파일이름은 newfile1.c 이고, 위치는 kernel 디렉토리 아래다.
#include <linux/unistd.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/sched.h> asmlinkage int sys_gettaskinfo() { int i, cnt = 0; printk("PID : %d\n", current->pid); printk("PPID : %d\n", current->p_pptr->pid); if(current -> state == -1) printk("Unrunable state\n"); else if(current -> state == 0) printk("Runable state\n"); else if(current -> state == 1) printk("Interruptable state\n"); else if(current -> state == 2) printk("Uninterruptable state\n"); else if(current -> state == 4) printk("Zombie state\n"); else if(current -> state == 8) printk("Stopped state\n"); else if(current -> state == 16) printk("Swapping state\n"); else printk("Unknown state\n"); printk("Priority : %lu\n", current->rt_priority); // 커널 2.4 에서 바뀌었음!! printk("Scheduling Policy : %lu\n", current->policy); printk("User CPU time : %lu ticks\n", current->times.tms_utime); printk("System CPU time : %lu ticks\n", current->times.tms_utime); printk("Start time : %lu\n", current->start_time); printk("Number of major faults: %lu\n", current->maj_flt); printk("Number of minor faults: %lu\n", current->min_flt); for(i=0; i<256; i++) if(current->files->fd_array[i] != NULL) cnt++; printk("Number of opened file : %d\n", cnt); return(0); }
이후에 Makefile 에 newfile1.o 를 추가해주고, 커널 컴파일 후에, 새로 만든 커널이미지로 부팅한다.
Application 작성
파일이름은 test1.c 이고 위치는 /root 아래다.
#include <linux/unistd.h> #include <errno.h> _syscall0(int, gettaskinfo); main() { int i; i = gettaskinfo(); }
이제 컴파일 후에 실행시켜보자!!
Jul 16 14:14:44 localhost kernel: PID : 1909 Jul 16 14:14:44 localhost kernel: PPID : 1742 Jul 16 14:14:44 localhost kernel: Runable state Jul 16 14:14:44 localhost kernel: Priority : 0 Jul 16 14:14:44 localhost kernel: Scheduling Policy : 0 Jul 16 14:14:44 localhost kernel: User CPU time : 0 ticks Jul 16 14:14:44 localhost kernel: System CPU time : 0 ticks Jul 16 14:14:44 localhost kernel: Start time : 114765 Jul 16 14:14:44 localhost kernel: Number of major faults: 60 Jul 16 14:14:44 localhost kernel: Number of minor faults: 11 Jul 16 14:14:44 localhost kernel: Number of opened file : 51
위와 같이 출력될 것이다.
주의할 점
나의 경우, 위에서 만든 test1.c 를 컴파일하다가 다음과 같은 에러가 발생했다.
test1.c: In function `gettaskinfo': test1.c:4: `__NR_gettaskinfo' undeclared (first use in this function) test1.c:4: (Each undeclared identifier is reported only once test1.c:4: for each function it appears in.)
몇 번이나 확인을 해봤지만, 이상한 점을 발견할 수 없었다. 삽질끝에 결국 원인을 알아냈다.
/usr/include/asm/unistd.h 라는 파일에
#define __NR_gettaskinfo 260
추가해줘야 한다. 그리고 나서 컴파일했더니, 에러없이 잘 되었다. 위의 파일은 커널과는 무관한 파일이다. 단지 시스템에서는 /usr/include/asm 아래에 있는 파일들을 참조하는 것 같다. 그래서 커널 아래에 있는 unistd.h 와 /usr/include/asm/unistd.h 파일의 시스템 콜 번호를 일치시켜주어야 한다.
동작 설명
current(include/asm-i386/current.h) 라는 전역변수가 task_struct(include/linux/sched.h) 자료구조를 포인팅한다.