캐릭터 디바이스
- file 로 존재하는 .ko 모듈 파일이 커널 내부로 메모리에 올라간다.
- 올라간 모듈은 필요할 때 커널에 요청으로 호출된다.
컨텍스트 스위칭

- CPU
- stack : 내 개인적인 공간 (프로그램 혼자 쓰는 공간)
- OS는 많은 Process 처리하기 힘들다.
- Task Control Block -> 똑같이 생긴 옷을 입힌다. (번호)
- 이 정보로 어떤 프로세스가 무엇을하고 있고 무엇인지를 식별한다.
- Process Id 부여해서 많은 프로그램들을 통제한다.
- System Context 라고 한다. (운영체제가 실행하는 공간)

- 실행 중이던 프로세스를 잠시 멈추면(더중요한게 있을 때, 상위우선순위),
CPU의 Regieter 값들을 stack에 내려놓고, OS안의 Task Control Block 메모리에 그 위치를 저장을 해놓는다.
- 잠시 냉동 되는 것과 비슷하다. (스위칭이 많아도, 프로그램에는 영향이없다. -> 결과가 동일)
- 필요해지면 다시 그 값들이 Load 된다.
void * OSTaskStkInit(void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{
OS_STK* stk;
stk = (OS_STK *) ptos;
*--stk = (INT32U) pdata;
*--stk = (INT32U) TaskBucket;
*--stk = 0x00000202;
*--stk = CS_SELECTOR;
*--stk = (INT32U) task;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
return stk;
}
- 운영 체제에서 Context Switching을 프로그래밍한다.
실습1. 뼈대만들기
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
static int sk_init(void)
{
printk("SK Module is up... \n");
return 0;
}
static void sk_exit(void)
{
printk("The module is down...\n");
}
module_init(sk_init);
module_exit(sk_exit);
obj-m += sk.o
startstop-objs := start.o stop.o
KDIR := /tftpboot/kernel-mds2450-3.0.22
all:
make -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
make -C $(KDIR) SUBDIRS=$(PWD) clean
- make file 작성 후 make

- insmod, lsmod, rmmod 동작 확인
캐릭터 디바이스 등록
포인터 배열과 배열 포인터
- 디바이스 드라이버가 이렇게 동작해서 중요하다고 한다.
포인터 배열 및 배열 포인터 활용사례
1. main 함수 argv : 명령어 라인 인풋
2. 드라이버 등록 함수 -> register_chardev
3. 시그날 함수 -> signal
- 응용프로그램이 상황을 인지할 때 (OS 빼고는 알 수가 없으므로.. 북한)
4. 문자열 간접 정렬 - DataBase index 정렬..
5. 인터럽트 핸들링(벡터링) - 펌웨어 c_handler 함수
6. 동적 배열 활용 - dynamic array
7. 기타 교재내의 예제들 etc ...

- major 넘버를 알면 device의 name을 알게되고, 동작하는 함수들의 주소를 알고 있으므로, file_operations를 사용할 수 있게 된다.

API
- 함수 명이 바뀌었다.
regstister _chrdev P : 문자 디바이스 등록
unregister_chrdev : 문자 디바이스 해제
=========> 이전 방식들
cdev_init, cdev_add... register_chrdev_region.. 등등
register_chrdev_region()
- 카운트 값을 가지는 디바이스 드라이버 등록
- int allokc_chrdev_regino(dev_t first, unsigned int count, char* name)
alloc_chrdev_region()
- 디바이스 번호를 동적으로 할당하는 함수
- int alloc_chrdev_region(dev_t dev, unsinged int firstminor, unsinged int count, char name)
- *dev : 성공적경우 디바이스 번호 할당
- firstminor : 디바이스에 할당될 첫번째 minor number, 일반적 0
- count : 부 번호로 디바이스 개수
- *name : 디바이스 이름 (/proc/devices와 sysfs에 나타난다.)
Manual allcoation
Dynamic allocation
- alloc_chrdev_region() 함수를 이용하여 동적으로 할당
- 미리 디바이스 파일 만들어 둘수 없다.
- 장치 번호 등록 후 /proc/devices를 읽어 major 번호를 얻고, 장치 파이리을 만드는 스크립트 활용
Major/Minor number가 조합된 수형
- Major(12bits), Monor(20bits)
cdev 구조체
struct cdev
{
struct kobject kobj;
struct module *owner;
const struct file_operations *pos;
struct list_head list;
dev_t dev;
unsigned int count;
}
- linux/cdev.h에 포함
- 내부적 캐릭터 디바이스 표현을 위해 cdev 구조체를 사용한다.
- 모든 driver가 kobj 를 갖고있다.
- file_operations : open, read 등 실제 디바이스를 움직이는 함수들 등록되어있는 것
cdev_init() 캐릭터 디바이스 등록
cdev_add() 캐릭터 디바이스 등록
cdev_del() 캐릭터 디바이스 해제
실습 2. 디바이스 등록
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;
static struct file_operations sk_fops;
static struct cdev sk_cdev;
static int sk_register_cdev(void);
static int sk_init(void)
{
printk("SK Module is up... \n");
if((result = sk_register_cdev()) < 0)
{
return result;
}
return 0;
}
static void sk_exit(void)
{
printk("The module is down...\n");
cdev_del(&sk_cdev);
unregister_chrdev_region(sk_dev, 1);
}
static int sk_register_cdev(void)
{
int error;
if(sk_major)
{
sk_dev = MKDEV(sk_major, sk_minor);
error = register_chrdev_region(sk_dev, 1, "sk");
} else
{
error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
sk_major = MAJOR(sk_dev);
}
if(error < 0)
{
printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
return result;
}
printk("major number=%d\n", sk_major);
cdev_init(&sk_cdev, &sk_fops);
sk_cdev.owner = THIS_MODULE;
sk_cdev.ops = &sk_fops;
error = cdev_add(&sk_cdev, sk_dev, 1);
if(error)
printk(KERN_NOTICE "sk Register Error %d\n", error);
return 0;
}
module_init(sk_init);
module_exit(sk_exit);
실습 3. 디바이스 파일 생성
lsmod sk2.ko
mknod /dev/SK c 253 0
ls /dev -al | grep SK
rm /dev/SK
rmmod sk
file_operations
- 이 구조체는 함수 포인터 집합이다.
- int (ioctl) (struct inode , struct file *, unsinged int, unsinged long);
- H/W의 예상하지 못하는 기능을 사용하기 위한 함수 중요
- loff_t (llseek) (struct file , loff_t, int)
- int (*open)
실습 4. open & release
#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;
static struct cdev sk_cdev;
static int sk_register_cdev(void);
static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_open(struct inode *inode, struct file *filp)
{
printk("Device has been opened...\n");
return 0;
}
static int sk_release(struct inode *inode, struct file *filp)
{
printk("Device has been closed...\n");
return 0;
}
struct file_operations sk_fops = {
.open = sk_open,
.release = sk_release,
};
static int __init sk_init(void)
{
printk("SK Module is up... \n");
if((result = sk_register_cdev()) < 0)
{
return result;
}
return 0;
}
static void __exit sk_exit(void)
{
printk("The module is down...\n");
cdev_del(&sk_cdev);
unregister_chrdev_region(sk_dev, 1);
}
static int sk_register_cdev(void)
{
int error;
if(sk_major)
{
sk_dev = MKDEV(sk_major, sk_minor);
error = register_chrdev_region(sk_dev, 1, "sk");
}
else
{
error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
sk_major = MAJOR(sk_dev);
}
if(error < 0)
{
printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
return result;
}
printk("major number=%d\n", sk_major);
cdev_init(&sk_cdev, &sk_fops);
sk_cdev.owner = THIS_MODULE;
sk_cdev.ops = &sk_fops;
error = cdev_add(&sk_cdev, sk_dev, 1);
if(error)
printk(KERN_NOTICE "sk Register Error %d\n", error);
return 0;
}
module_init(sk_init);
module_exit(sk_exit);
- open의 수행 순서
- open -> library -> S/W Interrupt (OS call : 이것좀 해줘, 응용SW에서 하지 못하는 것) -> System call -> VFS(Virtual File System, /dev/SK 찾아 Major Number 확인 하러) -> CHR. Device File -> Device Driver
- Open에는 File 이름 밖에 모른다. -> System call, VFS로 가서 Major Number와 File의 Type을 알아내어 HW를 움직이는 함수를 찾아간다.
실습 5. write 추가
#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;
static struct cdev sk_cdev;
static int sk_register_cdev(void);
static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int sk_open(struct inode *inode, struct file *filp)
{
printk("Device has been opened...\n");
return 0;
}
static int sk_release(struct inode *inode, struct file *filp)
{
printk("Device has been closed...\n");
return 0;
}
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
char data[11];
copy_from_user(data, buf, count);
printk("data >>>>> = %s\n", data);
return count;
}
struct file_operations sk_fops = {
.open = sk_open,
.release = sk_release,
.write = sk_write,
};
static int __init sk_init(void)
{
printk("SK Module is up... \n");
if((result = sk_register_cdev()) < 0)
{
return result;
}
return 0;
}
static void __exit sk_exit(void)
{
printk("The module is down...\n");
cdev_del(&sk_cdev);
unregister_chrdev_region(sk_dev, 1);
}
static int sk_register_cdev(void)
{
int error;
if(sk_major)
{
sk_dev = MKDEV(sk_major, sk_minor);
error = register_chrdev_region(sk_dev, 1, "sk");
} else {
error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
sk_major = MAJOR(sk_dev);
if(error < 0)
{
printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
return result;
}
printk("major number=%d\n", sk_major);
cdev_init(&sk_cdev, &sk_fops);
sk_cdev.owner = THIS_MODULE;
sk_cdev.ops = &sk_fops;
error = cdev_add(&sk_cdev, sk_dev, 1);
if(error)
printk(KERN_NOTICE "sk Register Error %d\n", error);
return 0;
}
module_init(sk_init);
module_exit(sk_exit);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int retn;
int fd;
char buf[100] = "write...\n";
fd = open("/dev/SK", O_RDWR);
printf("fd = %d\n", fd);
if (fd<0)
{
perror("/dev/SK error");
exit(-1);
}
else
printf("SK has been detected...\n");
retn = write(fd, buf, 10);
printf("\nSize of written data : %d\n", retn);
close(fd);
return 0;
}
- sk4_app.c
- sk4 make 및 app cross compile 후 teraterm 에서 app 실행
./sk4

갑분 리눅스 동기화
A task -> -1 -> 1
↓
Some Register -> 1 (A의 1 저장)
↑
B task -> -1 -> 0 (B의 0 저장?)
- 저장하는 부분에서 Lock 하고 최종 점검한다.
- 자신이 가져온 값과 저장하려는 레지스터의 값이 동일하면 최종점검 Yes
- 최종점검 No ? -> Roll back 처음부터 다시
- Dead Lock
- X Register A가 값변경, Y Register B가 값변경 Y를 X Register가 값 변경하면 ? -> 최종점검에서 No나와서 아무것도 못하고 둘다 A,B 가 멈춰있는다. -> System 뻗는다.
- 둘다 가져가던지 둘다 못가져 가던지
- 프로그램이 엄청많을 때, Loop가 생성되면 Dead Lock 걸린다.
- 아직까지 해결되지 않은 숙제
- 꼼꼼한 설계 후 개발 (OS가 모든걸 해결해 주지는 않는다. 개발자도 노력해야한다.)
해결 방법

- 한 10가지 된다.
- Atomic operations
- 위의 1번 해결법 하나가 가져갔으면 다른 하나가 작업 Stop
- 하드웨어적으로 되어진 경우가 많다.
- Spin Locks
- CPU 들 끼리도 하나의 H/W 가지고 경쟁한다.
- 끝날 때 까지 계속 기다린다.
- 매우 빠르게 무언가를 처리할 때 쓴다.
- Reader-Writer Semaphores
- Update 중 함수 정수
- RO, RW 끼리 최대한 정리
- RO 파일은 동기화 문제가 없지만, 동시 Write가 문제가 된다.
- 최대한 Write 하는 파일을 줄여서 프로그램 작동 효율을 높이려 한다.
- Semaphores
- H/W 사용할 권리를 준다. 열쇠 개념
- Semaphores를 만들면 변수가 생긴다. 0 : 불가능, 1 : 접근 가능
- Mutexes
- Light Weight Semaphores
- 동시 수행만 못하도록 하는 개념이라 프로그램 처리 속도가 빨라진다.
- Barriers
- Compiler -> Reordering 실행 순서를 바꾼다.
- 바꿔도 문제가 안생기고, 속도가 빨라 지는 Code 명령어의 순서를 바꾼다.
- 잠구고, 푸는 코드가 바뀔 수 있다.
- 잠구고 푸는 부분의 벽을 생성해서 이런 경우를 막는다.
실습 6. read 추가
#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;
static struct cdev sk_cdev;
static int sk_register_cdev(void);
static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
static int sk_open(struct inode *inode, struct file *filp) {
printk("Device has been opened...\n");
return 0;
}
static int sk_release(struct inode *inode, struct file *filp) {
printk("Device has been closed...\n");
return 0;
}
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
char data[11];
copy_from_user(data, buf, count);
printk("data >>>>> = %s\n", data);
return count;
}
static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) {
char data[20] = "this is read func...";
copy_to_user(buf, data, count);
return 0;
}
struct file_operations sk_fops = {
.open = sk_open,
.release = sk_release,
.write = sk_write,
.read = sk_read,
}
;
static int __init sk_init(void) {
printk("SK Module is up... \n");
if((result = sk_register_cdev()) < 0) {
return result;
}
return 0;
}
static void __exit sk_exit(void) {
printk("The module is down...\n");
cdev_del(&sk_cdev);
unregister_chrdev_region(sk_dev, 1);
}
static int sk_register_cdev(void) {
int error;
if(sk_major) {
sk_dev = MKDEV(sk_major, sk_minor);
error = register_chrdev_region(sk_dev, 1, "sk");
} else {
error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
sk_major = MAJOR(sk_dev);
}
if(error < 0) {
printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
return result;
}
printk("major number=%d\n", sk_major);
cdev_init(&sk_cdev, &sk_fops);
sk_cdev.owner = THIS_MODULE;
sk_cdev.ops = &sk_fops;
error = cdev_add(&sk_cdev, sk_dev, 1);
if(error)
printk(KERN_NOTICE "sk Register Error %d\n", error);
return 0;
}
module_init(sk_init);
module_exit(sk_exit);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void) {
int retn;
int fd;
char buf[100] = {
0
}
;
fd = open("/dev/SK", O_RDWR);
printf("fd = %d\n", fd);
if (fd<0) {
perror("/dev/SK error");
exit(-1);
} else
printf("SK has been detected...\n");
retn = read(fd, buf, 20);
printf("\ndata : %s\n", buf);
close(fd);
return 0;
}
- sk5_app.c
- make 후 insmod 후 결과 확인

ioctl
- int (ioctl) (struct inode inode, struct file *flip, unsigned int cmd, unsinged long arg);
- cmd 인수 값에 따라 올바른 동작을 선택하는 switch 문으로 구성한다.
- SMP 아키텍처로 인한 커널 코드 실행에 효율을 높이고자 만들어졌다.