Download presentation
Presentation is loading. Please wait.
1
Linux System Call
2
Kernel Programming Linux kernel core 기능 추가 Linux kernel 알고리즘 개선
3
시스템 호출 개요 권한 레벨을 설정하여 사용자 공간과 커널 공간 구분
서로 다른 공간에 대한 접근은 시스템 호출(system call)로만 가능하도록 제한 컴퓨팅 자원을 안전하게 보호 커널 수행을 안전하게 유지 시스템 호출은 사용자 영역에서 커널 영역의 자원을 사용하기 위하여 사용하는 함수 트랩(trap)과 인터럽트(interrupt)
4
시스템 호출 개요 프로세스 관리: fork(), execve(), getpid(), signal() 등
리눅스에서 지원하는 시스템 호출 프로세스 관리: fork(), execve(), getpid(), signal() 등 파일 시스템: open(), read(), write() close() 등 메모리 관리: brk() 등 네트워크 관리: socket(), bind(), connect() 등 기타 시스템 정보나 제어: time() 등
5
시스템 호출 동작 과정 사용자 프로그램의 번역 과정
6
시스템 호출 동작 과정 fork 시스템 호출 과정
① fork() 시스템 호출 함수를 사용하려면 fork() 함수 정보가 있는 <linux>/include/linux/unistd.h 헤더 파일을 include. 링커로 라이브러리에서 대응하는 함수와 연결 ② 라이브러리 내부의 fork() 함수: “swi ” ③ 호출 번호 2에 해당하는 시스템 호출 함수 sys_fork() 실행 ④ 가상 파일 시스템을 통해 커널 자원인 파일, 디렉토리, 디바이스 등을 하나의 파일처럼 접근
7
시스템 호출 구현
8
Kernel vs Application Namespace pollution
Application Program: 현재 개발하는 프로그램에서만 각 함수와 변수의 이름을 구별하여 주면 된다. Kernel Program: 현재 개발하는 모듈 외에도 커널 전반적으로 함수화 변수의 이름이 충돌하지 않도록 하여야 한다.
9
Kernel programming 주의 사항
Library stdio.h 와 같은 일반 프로그램에서 사용하는 헤더 파일을 include해서는 안된다. 오직 /usr/include/linux 와 /usr/include/asm 아래에 선언된 헤더파일 만을 include 한다. Namespace pollution 외부 파일와 link하지 않을 모든 심볼을 static으로 선언 또는 외부 파일과 link할 symbol을 symbol table 등록 EXPORT_NO_SYMBOLS; EXPORT_SYMBOL(name); 전역 변수는 잘 정의된 prefix를 붙여 준다. Ex: sys_open() symbol(함수나 변수등의 identifier)을 static 으로 선언하면 link 시에 다른 object에서 참조 되지 않는다. static으로 선언하지 않는 경우 대부분 다른 object에서 참조할 수 있게 된다. 심볼을 심볼 테이블에 등록해주는 아래의 두 매크로를 사용할 경우에는 static으로 선언하지 않아도 외부에서 symbol을 참조할 수 없고 명시적으로 export해준 symbol만을 외부에서 참조할 수 있게된다. symbol table은 전역 커널 항목 -- 함수와 변수 – 을 담고 있다. /proc/ksyms에 text 형태로 저장되어 있으므로 사용자가 읽어 볼 수도 있다. Kernel 에 모듈을 추가할 경우 insmod 프로그램은 이 symbol table을 검사하여 추가하려는 모듈에서 아직 연결 되지 않은 (unresolved) symbol 을 연결 시켜 준다. EXPORT_NO_SYMBOLS; 링크할때 어떤 심볼도 다른 오브젝트에서 참조하지 못하도록 한다. EXPORT_SYMBOL(name) name에 해당하는 심볼을 다른 오브젝트에서 참조 할 수 있도록 한다.
10
Kernel programming 주의 사항
Fault handling Kernel 은 하드웨어 접근에 대해 어떠한 제한도 없기 때문에 커널에서의 에러는 시스템에 치명적인 결과를 발생시킨다. 함수 호출 등의 작업 시 모든 에러 코드를 검사하고 처리해야 한다. Address space 커널이 사용하는 stack의 크기는 제한되어 있고, 인터럽트 핸들러도 동일한 스택을 사용하므로 큰 배열을 사용하거나, recursion이 많이 일어나지 않도록 주의해야 한다. Application 과 data를 주고 받기 위해(call by reference) 특별한 함수를 사용 하여야 한다. 커널과 application은 서로 다른 주소 공간을 사용한다. 때문에 call by reference를 사용할 수 없고 이를 위해 특별한 함수를 사용하여 커널과 application에서 data를 주고 받는다. 뒷 장에서 이러한 함수들을 소개한다. MMX[엠엠엑스]는 멀티미디어 응용프로그램들의 실행을 좀더 빠르게 할 수 있도록 설계된 인텔 펜티엄 프로세서이다. 인텔에 따르면, MMX 기능이 부가된 마이크로프로세서는 동일한 클록속도의 MMX 기능이 없는 프로세서에 비해, 멀티미디어 응용프로그램을 최고 60%까지 빠르게 실행할 수 있다고 한다. 그 외에도, MMX 마이크로프로세서는 일반적으로 다른 프로그램들도 약 10% 정도 더 빠르게 수행한다
11
Kernel programming 주의 사항
기타 실수연산 이나 MMX 연산을 사용할 수 없다. 커널과 application은 서로 다른 주소 공간을 사용한다. 때문에 call by reference를 사용할 수 없고 이를 위해 특별한 함수를 사용하여 커널과 application에서 data를 주고 받는다. 뒷 장에서 이러한 함수들을 소개한다. MMX[엠엠엑스]는 멀티미디어 응용프로그램들의 실행을 좀더 빠르게 할 수 있도록 설계된 인텔 펜티엄 프로세서이다. 인텔에 따르면, MMX 기능이 부가된 마이크로프로세서는 동일한 클록속도의 MMX 기능이 없는 프로세서에 비해, 멀티미디어 응용프로그램을 최고 60%까지 빠르게 실행할 수 있다고 한다. 그 외에도, MMX 마이크로프로세서는 일반적으로 다른 프로그램들도 약 10% 정도 더 빠르게 수행한다
12
Kernel Interface 함수 주의 사항 Kernel interface 함수 분류
Kernel program은 일반적인 library를 사용하지 못하고 kernel에서 export 한 함수들 만을 사용할 수 있다. Kernel interface 함수 분류 Kernel 에서 제공하는 함수 중 kernel programming에 자주 사용되는 함수는 다음과 같이 분류할 수 있다. Port I/O Interrupt Memory Synchronization Kernel message 출력 Device Driver register
13
Kernel Interface 함수 I/O device 와 data를 주고 받기 위한 함수들
unsigned inb(unsigned port) Port에서 1byte를 읽는다. unsigned inw(unsigned port) Port에서 2byte를 읽는다. unsigned inl(unsigned port) Port에서 4byte를 읽는다. unsigned outb(char value, unsigned port) Port에 1byte value를 쓴다. unsigned outw(short int value, unsigned port) Port에 2byte value를 쓴다. unsigned outl(long int value, unsigned port) Port에 4byte value를 쓴다. 주변 장치에는 cpu 의 I/O 주소 영역에 mapping 되어 사용되는 장치가 있고 Memory 영역에 mapping 되어 사용되는 장치가 있다. 위 함수들은 I/O 주소 영역에 mapping 된 장치를 위한 것이고, Memory 영역에 mapping 된 장치는 일반적으로 memory를 사용하는 방식으로 접근할 수 있다.
14
Kernel Interface 함수 I/O device 와 data를 주고 받기 위한 함수들
void insb(unsigned port, void *addr, unsigned long count) Port에서 count bytes를 읽어서 메모리의 addr 주소부터 저장 void insw(unsigned port, void *addr, unsigned long count) Port에서 16bit * count 만큼 읽어서 메모리의 addr 주소부터 저장 void insl(unsigned port, void *addr, unsigned long count) Port에서 32bit * count 만큼 읽어서 메모리의 addr 주소부터 저장 void outsb(unsigned port, void *addr, unsigned long count) Memory의 addr번지 에서부터 count bytes를 읽어서 port에 쓴다. void outsw(unsigned port, void *addr, unsigned long count) Memory의 addr번지 에서부터 count * 16bit를 읽어서 port에 쓴다. void outsl(unsigned port, void *addr, unsigned long count) Memory의 addr번지 에서부터 count * 32bit를 읽어서 port에 쓴다.
15
Kernel Interface 함수 I/O device 와 data를 주고 받기 위한 함수들 Pausing I/O
예) inb() 함수의 경우 inb_p()
16
Kernel Interface 함수 인터럽트의 설정 및 처리에 관한 함수(or 매크로) cli()/sti()
clear/set interrupt enable save_flags(unsigned long flag), restore_flags(unsigned long flag) status register의 내용을 저장하고 복원 save_flags(), restore_flags() 두 매크로는 같은 함수 안에서 호출 되어야 한다. flag를 다른 함수로 pass해서는 안된다. int requst_irq(unsigned int irq, void (*handler)(int),unsigned long flags, const char *device) 커널로부터 IRQ를 요청하고, 이 IRQ에 대한 interrupt handler를 install cli()/sti() 는 전체 인터럽트를 금지하거나 가능하게 해주는 매크로이다. Status register는 system의 각 상태를 가지고 있는 레지스터이다. Processor 마다 차이가 있지만 일반적으로Interrupt 가 enable 상태 인지, 직전의 연산에서 carry가 발생하였는 가 등의 정보를 가지고 있다. requst_irq 는 사용자가 인터럽트를 추가하고자 할 때 사용한다. 인터럽트가 발생할 수 있는 source 를, 현재 사용하고 있지 않은 irq 에 등록하고, 이러한 인터럽트가 발생했을 때 수행될 interrupt handler 함수를 등록한다.
17
Kernel Interface 함수 인터럽트의 설정 및 처리에 관한 함수(or 매크로)
void free_irq(unsigned int irq) request_irq()에서 획득한 irq를 반납함
18
Kernel Interface 함수 Kernel에서 동적 메모리를 할당할 때 사용하는 함수들
void * kmalloc(unsigned int len, int priority) 커널 메모리 할당. 128~131056byte까지 가능 priority:GFP_BUFFER, GFP_ATOMIC, GFP_USER, GFP_KERNEL 물리적으로 연속적인 메모리를 할당한다. void kfree(void *obj) kmalloc()에서 할당 받은 커널 메모리를 반납 GFP_KERNEL : 일반적인 커널 메모리 할당. 할당 가능한 Memory가 부족할 경우 sleep할 수도 있다. GFP_BUFFER : 버퍼 캐쉬를 관리할때 사용되므로 할당자가 sleep상태로 갈수 있다. I/O 서브 시스템이 스스로 메모리를 필요로 할때 데드락과을 피하도록 하기 위해 디스크에 dirty page를 disk에 플러쉬함으로서 메모리를 free한다는 점에서 GFP_KERNEL과 다르다. GFP_ATOMIC : 인터럽트 핸들러 등 프로세스 컨텍스트 외부 코드에서 메모리를 할당할때 사용한다. 결코 sleep상태가 되지 않는다. GFP_USER : 사용자들이 메모리 할당할때 사용. 낮은 우선순위를 가진다. GFP_HIGHUSER High memory에서 할당할때 사용한다.
19
Kernel Interface 함수 Kernel에서 동적 메모리를 할당할 때 사용하는 함수들
void * vmalloc(unsigned int len) 커널 메모리 할당 크기 제한 없음 가상 주소 공간에서 연속적인 메모리 영역을 할당 void vmfree(void *addr) vmalloc()에서 할당 받은 커널 메모리를 반납
20
Kernel Interface 함수 사용자 공간과 커널공간 사이에 데이터를 공유하기 위한 함수
unsigned long copy_from_user(void *to, const void *from, unsigned long n) 사용자 주소공간에서 n byte만큼 data 복사. unsigned long copy_to_user(void *to, const void *from, unsigned long n) 사용자 주소 공간에 n byte만큼 data 복사 void * memset(void *s, char c, sizt_t count) 메모리 s에 c를 count만큼 복사 put_user(datum,ptr) / get_user(ptr) 사용자 공간에 datum을 전달하거나 가져오기 위한 매크로
21
Kernel Interface 함수 동기화 void sleep_on(struct wait_queue **q)
q의 번지를 event로 sleep하며, uninterruptible void sleep_in_interruptible(struct wait_queue **q) q의 번지를 event로 sleep하며, interruptible void wake_up(struct wait_queue **q) sleep_on(q)에 의해 sleep한 task를 wakeup void wake_up_interruptible(struct wait_queuq **q) sleep_on_interruptible(q)에 의해 sleep한 task를 wakeup uninterruptible 이란 말은 wake_up 함수의 호출 외에 다른 signul 에 의해서 깨어날 수 없음을 의미하는 것이다. Interruptible 은 wake_up 함수가 호출되지 않아도 임의로 process에 signul을 주어 깨어나게 할 수 있다는 의미이다.
22
Kernel Interface 함수 예 Standard output 으로 메시지를 출력
printk(const char *fmt,… .) printf의 커널 버전 printk(LOG_LEVEL_ message) LOG_LEVEL:KERN_EMERG, KERN_ALERT, KERN_ERR,KERN_WARNING, KER_INFO, KERN_DEBUG 예 printk(“<1>Hello, World”); printk(KERN_WARNING ”warning… \n”); LOG_LEVEL은 메시지의 우선 순위를 정의하는 것이다. LOG_LEVEL에 기초하여 커널을 메시지를 현재의 문자 콘솔에 출력하게 되는데 만약 console_loglevel 의 값 보다 우선 순위가 낮다면 console에 출력되지 않는다. console_loglevel은 sys_syslog 시스템 콜로 값을 바꿀 수 있다. LOG_LEVEL 은 <linux/kernel.h> 헤더에 정의 되어 있다.
23
Kernel Interface 함수 Device Driver register int register_xxxdev
(unsigned int major, const char *name,struct file_operations *fops) character/block driver를 xxxdev[major]에 등록 xxx:blk/chr int unregister_xxxdev(unsigned int major, const char *name) xxxdevs[major]에 등록되 있는 device driver를 제거 int register_netdev(const char *name) int unregister_netdev(const char *name) MAJOR(kdev_t dev)/MINOR(kdev_t dev) 장치번호dev로부터 major/minor 번호를 구함
24
System Call User Application API(시스템 라이브러리) User Level Kernel Level
System Call Interface User Level Kernel Level File System Buffer Cache Charater Device Driver Block Process Management (IPC-interprocess Communication, scheduling, Memory Management Process Acounting, etc) Hardware Interface
25
What’s System Calls? user mode process와 kernel 간의 interface
user mode process는 일반적으로 kernel 영역에 직접적으로 접근할 수 없다. kernel의 자료구조 및 hardware 에 대한 접근 불가 user mode process가 kernel이 가지고 있는 시스템의 상태 정보를 열람하거나 hardware에 접근하여 hardware를 통제하기 위해서는 kernel 과의 communication channel이 필요. User Application API(시스템 라이브러리) System Call Interface User Level Kernel Level File System Buffer Cache Charater Device Driver Block Process Management (IPC-interprocess Communication, scheduling, Memory Management Process Acounting, etc) Hardware Interface 일반적으로 프로세스는 커널에 직접적으로 접근할 수 없다. 다시 말해, 프로세스는 커널 메모리에 접근한다거나 커널의 함수를 호출할 수 없다. 보호모드. 시스템 콜은 user program에게 커널 영역에 대한 접근을 허용하는 유일한 chanel이다. user program은 시스템 콜을 사용해서만 hardware 및 커널 자료구조에 접근할 수 있다.
26
System Call 추가 목적 기존 kernel에서 제공하지 않는 service를 user application에 제공 새로운 System Call 을 만든다. 단계 커널 수정 System Call 번호 할당 System Call 호출 테이블 등록 System Call 호출 함수 구현 Kernel 컴파일 및 target board에 적재 user application 제작 새로 구현한 System Call을 사용하는 application 제작 library 작성(반드시 필요한 것은 아니다.) Ramdisk에 포함시켜서 target board에 적재
27
System Call 추가 가정 /PXA270/kernel/linux h270-tku_v1.1/ 에 target system용 kernel source code가 있다. 위 디렉토리를 앞으로의 설명에서 [kernel]로 대치한다.
28
System Call 추가 System Call 번호 할당
Linux 커널이 제공하는 모든 시스템 호출은 각각 고유한 번호를 가지고 있다. [kernel]/include/asm-arm/unistd.h 에 각 시스템 호출의 고유 번호에 정의 되어 있다. 새로 추가할 system call의 고유번호 정의 추가 [kernel]/include/asm-arm/unistd.h 파일을 vi로 연다.
29
System Call 추가 다음과 같이 추가할 System Call에 고유번호를 할당한 후 저장하고 나온다.
unistd.h 파일의 위 내용을 보면 현재 system call 은 280 개가 있는 것을 확인할 수 있다. 추가할 system call 은 위 화면과 같이281번으로 할당한다. __NR_은 System Call 고유번호를 나타내는 접두어이며 그 뒤의 hybuscall이 추가할 System Call 처리 함수의 이름이다. (sys_hybuscall 이 아님을 주의 깊게 봐 둔다.) 현재 리눅스에서는 256개까지의 시스템 콜을 지원한다.
30
System Call 추가 System Call 테이블에 System Call 처리 함수 등록
[kernel]/arch/arm/kernel/entry-commom.S 에 sys_call_table이라는 entry로 구현되어 있다. sys_call_table에는 system call 처리함수의 시작 주소들이 들어있고 각 함수들은 unistd.h에 정의 되어있는 system call 번호를 인덱스로 하여 접근된다. sys_call_table에 추가할 System Call 처리함수 등록 entry-common.S 파일을 vi로 연다.
31
System Call 추가 entry-common.S 에서 다음과 같은 부분을 확인할 수 있다.
ENTRY(sys_call_table) 다음에 calls.S를 include하고 있다. vi를 나와서 다시 calls.S를 열어 보자.
32
System Call 추가 calls.S 의 내용은 다음과 같다. * <kernel>/arch/arm/kernel
33
System Call 추가 다음과 같이 추가할 System Call 처리함수를 등록한 후 저장 하고 나온다.
unistd.h에 정의한 번호와 일치
34
이 함수가 어셈블리 언어로 구현된 함수에서 호출될 때 사용하는 keyword
System Call 추가 처리 함수 구현하기 System Call 이 발생했을 때 수행될 함수를 구현한다. [kernel]/kernel/test_syscall.c 를 다음과 같이 만든다. calls.S 에 등록한 이름과 동일한 이름 # 이 함수가 어셈블리 언어로 구현된 함수에서 호출될 때 사용하는 keyword
35
System Call 추가 Makefile 수정
만들어진 처리함수를 커널이 컴파일 될 때에 함께 컴파일 될 수 있도록 [kernel]/kernel/Makefile에 다음과 같이 추가 한다.
36
System Call 추가 Kernel image를 /tftpboot에 복사
[kernel] 디렉토리에서 커널을 컴파일하고 생성된 커널 이미지를 target에 전송하기 위해 /tftpboot로 복사. # cd [kernel] # make menuconfig; make zImage # cp [kernel]/arch/arm/boot/zImage /tftpboot
37
System Call 추가 System Call을 호출하는 user application작성
library를 만들지 않고 user application을 만드는 경우 unistd.h 에 정의된 매크로로 System Call 처리함수의 타입과 이름을 인자로 넘겨준다. *sys 접두어를 붙이지 않는다. /* linux/unistd.h */ #define _syscall0(type,name) type name(void){ \ register long __res __asm__(“r0”); \ __asm__ __volatile__{ \ __syscall(name) \ : “=r” (__res) \ : “ \ : “lr”); \ __syscall_return(type,__res); \ } 아래쪽에서 오른쪽 창에 보면 __sys1(__NR_##name) 이라고 된 부분이 있는데 여기서 ##은 두 토큰을 결합한다는 의미이다. 즉 name에 “mysyscall” 을 넘겨준 경우 sys1(__NR_mysyscall) 과 같이 된다. #else #define __syscall(name) “swi\t” __sys1(__NR_##name)”\n\t” Software interrupt를 발생시키는 assemblier Unistd에서 정의한 symbol로 바꿔준다.
38
System Call 추가 * Source 코드 #include <linux/unistd.h>
#include <errno.h> _syscall0(int, hybuscall); int main() { hybuscall(); return 0; }
39
System call 추가 * Makefile
KDIR := /PXA270/kernel/linux h270-tku_v1.1/include CFLAGS := -I$(KDIR) syscall_test: syscall_test.c arm-linux-gcc $(CFLAGS) –o syscall_test syscall_test.c
40
System Call 추가 Test 앞서 system call을 추가하여 다시 컴파일한 커널
이미지(현재 /tftpboot에 zImage로 존재) 를 target에 download하고 target 을 리눅스로 부팅 시스템콜을 사용하는 프로그램을 실행 ]# ./syscall_test Welcome to the embedded World!!
41
Kernel 수정 Application 작성
System Call 추가 Kernel 수정 Application 작성 unistd.h 에 system call 번호 정의 calls.S 에 System Call 처리함수 등록 system call을 호출하는 application 작성 Application 을 Target system에 download System Call 처리 함수 구현 Kernel 재 컴파일 Applicatin 실행 Kernel을 Target system에 download
42
System Call 원리 Linux 에서의 system call 처리 Interrupt Interrupt 처리 매커니즘 사용
주변 장치와 커널이 통신하는 방식 중 하나로 주변 장치가 자신에게 발생한 비동기적 사건을 kerenl에게 알리는 메커니즘. PIC disk tty network cdrom RTC CPU Kernel IDT(IVT) Interrupt handlers el3_interrupt() tty_interrupt() hd_interrupt() timer_interrupt() 1 2 3 4 … 인터럽트를 발생시킬 수 있는 주변 장치들은 하드웨어 적으로 PIC(Programmable Interrupt Controller)라는 칩의 각 핀에 연결되어 있다. PIC는 CPU의 한 PIN에 연결되어 있다. KERNEL은 부팅 과정에서 PIC를 초기화 시킨다. 주변장치에서 인터럽트를 발생시키면 PIC는 CPU에게 인터럽트가 발생되었음을 알리고 CPU는 PIC에게 몇번 인터럽트가 발생되었는가를 문의하여 해당 장치의 IRQ를 알려 준다. 3번 인터럽트가 발생했다고 가정하면 CPU는 KERNEL에게 3번 장치에서 인터럽트가 발생했음을 알린다. KERNEL은 IDT(Interrupt Descriptor Table) 에서 index 3 의 entry에 등록되어 있는 handler 함수를 호출하게 되며 위 그림에서는 el3_interrupt() 를 호출하게 된다. IDT: Interrupt Vector Tale(IVT)라고도 하는 entry table로 각 인터럽트가 발생했을 때 이를 처리해 주기위한 루틴의 시작 주소를 가지고 있다.
43
System Call 원리 System Call User task Kernel sys_exit() sys_fork()
main () { …. hyubscall() } hybuscall() swi __NR_##name User task sys_hybuscall() … … … sys_write() sys_read() sys_fork() sys_exit() System_call_table 1 2 3 4 281 Name hybuscall __NR_hybuscall 281 Kernel printk(“…”); /* [kernel]/arch/arm/kernel/entry-common.S*/ ENTRY(vector_swi) get_scno … adr tbl, sys_call_table ldrcc pc, [tbl,scno, lsl #2] swi 는 software 인터럽트를 발생키셔주는 어셈블리 인스트럭션이다. .macro get_scno #ifdef CONFIG_ARM_THUMB tst r8, #T_BIT @ this is SPSR from save_user_regs addne scno, r7, #OS_NUMBER << put OS number in ldreq scno, [lr, #-4] #else mask_pc lr, lr ldr scno, [lr, #-4] @ get SWI instruction #endif .endm
Similar presentations