41일차 - 차량용 OS

JeongChaeJin·2021년 5월 27일
0

캐릭터 디바이스

  • file 로 존재하는 .ko 모듈 파일이 커널 내부로 메모리에 올라간다.
    • 올라간 모듈은 필요할 때 커널에 요청으로 호출된다.

컨텍스트 스위칭

  • CPU
    • Register Context 라고 한다.
  • stack : 내 개인적인 공간 (프로그램 혼자 쓰는 공간)
    • User Context 라고한다.
  • 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;			// Load stack pointer

	*--stk = (INT32U) pdata;			// Simulate a function call (to pass the parameter)
	*--stk = (INT32U) TaskBucket;	// Return address in case the task exits.

	*--stk = 0x00000202;			// Eflags (interrupt flag enabled)
	*--stk = CS_SELECTOR;			// CS
	*--stk = (INT32U) task;			// Entry point

	*--stk = 0;						// EAX
	*--stk = 0;						// ECX
	*--stk = 0;						// EDX
	*--stk = 0;						// EBX
	*--stk = 0;						// ESP (unused)
	*--stk = 0;						// EBP
	*--stk = 0;						// ESI
	*--stk = 0;						// EDI

    return stk;
}
  • 운영 체제에서 Context Switching을 프로그래밍한다.

실습1. 뼈대만들기

/***************************************
 * Filename: sk.c
 * Title: Skeleton Device
 * Desc: module_init, module_exit
 ***************************************/
#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);  
  • sk.c 작성
obj-m += sk.o  #startstop.o#hello-1.o hello-2.o hello-3.o hello-4.o
startstop-objs := start.o stop.o

#KDIR   := /lib/modules/$(shell uname -r)/build
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 동작 확인

캐릭터 디바이스 등록

  • 제공하는 API를 사용해서 코딩해야된다.

포인터 배열과 배열 포인터

  • 디바이스 드라이버가 이렇게 동작해서 중요하다고 한다.
포인터 배열 및 배열 포인터 활용사례

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. 디바이스 등록

/***************************************
   * Filename: sk.c
   * Title: Skeleton Device
   * Desc: register and unregister chrdev
***************************************/
#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;
/* file_operations의 레코드를 할당한다. */
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;

	/* allocation device number */
	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);

	/* register chrdev */
	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 # device name, type, major number, minor nubmber
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





/***************************************
   * Filename: sk.c
    * Title: Skeleton Device
	 * Desc: Implementation of system call
	  ***************************************/
#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);

/* TODO: Define Prototype of functions */
static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);

/* TODO: Implementation of functions */
static int sk_open(struct inode *inode, struct file *filp)
{
	printk("Device has been opened...\n");
		 
	/* H/W Initalization */
		    
	//MOD_INC_USE_COUNT;  /* for kernel 2.4 */	    
	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;

	/* allocation device number */
	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);


	/* register chrdev */
	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 추가

/***************************************
* Filename: sk.c
* Title: Skeleton Device
* Desc: Implementation of system call
***************************************/
#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);

/* TODO: Define Prototype of functions */
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);

/* TODO: Implementation of functions */
static int sk_open(struct inode *inode, struct file *filp)
{
	printk("Device has been opened...\n");
		    
	/* H/W Initalization */
		    
	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;

	/* allocation device number */
	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);

	/* register chrdev */
	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);
  • sk4.c
/***************************************
* Filename: sk_app.c
* Title: Skeleton Device Application
* Desc: Implementation of system call
***************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(void)
{
	int retn;
	int fd;
	/* write에서 사용할 버퍼 */
	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");
								    
	/* fd가 가르키는 파일에 buf에 있는 10바이트를 쓰라는 의미 */
	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
  1. 저장하는 부분에서 Lock 하고 최종 점검한다.
  • 자신이 가져온 값과 저장하려는 레지스터의 값이 동일하면 최종점검 Yes
  • 최종점검 No ? -> Roll back 처음부터 다시
  1. 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 추가

/***************************************
   * Filename: sk.c
    * Title: Skeleton Device
	 * Desc: Implementation of system call
	  ***************************************/
#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);
/* TODO: Define Prototype of functions */
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);
/* TODO: Implementation of functions */
static int sk_open(struct inode *inode, struct file *filp) {
	printk("Device has been opened...\n");
	/* H/W Initalization */
	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) {
	/* App. 에 전달할 문자열을 담은공간 */
	char data[20] = "this is read func...";
	/* App. 으로 전달 받은 주소로부터 count까지의 내용을 buf로 옮긴다 */
	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;
	/* allocation device number */
	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);
	/* register chrdev */
	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);
  • sk5.c
/***************************************
   * Filename: sk_app.c
    * Title: Skeleton Device Application
	 * Desc: Implementation of system call
	  ***************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void) {
	int retn;
	int fd;
	// char buf[100] = "write...\n";
	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 = write(fd, buf, 10);
	retn = read(fd, buf, 20);
	// fd가 가르키는 파일에 buf에서 20byte 읽음
	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로 무슨 함수 쓸지를 결정한다.
  • cmd 인수 값에 따라 올바른 동작을 선택하는 switch 문으로 구성한다.
  • SMP 아키텍처로 인한 커널 코드 실행에 효율을 높이고자 만들어졌다.
profile
OnePunchLotto

0개의 댓글

관련 채용 정보