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) 자료구조를 포인팅한다.

  • computer/embedded/시스템_콜_이해하기_-_2.task_struct.txt
  • Last modified: 3 years ago
  • by likewind