시스템 콜 이해하기 - 2.task_struct
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) 자료구조를 포인팅한다.