* 시스템 리소스의 표현
- 시스템 리소스를 파일로 표현 및 제공하는 것을 의미
- 시스템에 있는 H/W는 물론, 논리적인 시스템 리소스 전반을 파일로 표현
- /proc, /dev, /sys 내에 디바이스 파일이나 프로세스와 관련된 파일, 시스템 리소스를 표현하는 파일 위치
- /dev/sda 파일은 하드디스크 추상화 파일
* 추상화된 파일의 역할
- Application 을 위한 인터페이스 도구
- 리눅스는 사용자 공간과 커널 공간이 가상주소를 기반으로 분리된 시스템 구조
- 이는 Application 이나 커널은 서로 직접적인 접근이 불가능
- Application 이 커널의 리소스를 사용하기 위해서는 S/W 인터럽트인 system call 로 커널의 작업 수행을 요청 하는 구조를 제공
### udev 란 ?
* 탄생배경
- /dev 밑에 디바이스 파일 노드를 만드는 데 어려움
- RedHat 9 버전인 경우 /dev/에는 18,000 개의 디바이스 노드 파일 존재
- 이후에 생성될 가능성이 있는 모든 디바이스에 대한 모든 노드 파일을 미리 생성
*udev란
- A Userspace Implementation of devfs 의 약자
- 실제로 있는 디바이스에 대한 파일 노드 생성을 사용자 공간에서 자동화하여 처리하도록 구성
- /dev 디렉토리에 장치 노드 파일을 만들거나 제거하는 작업을 동적으로 수행
- 안드로이드에서는 시스템이 부팅될 때 두 개의 데몬이 동작
- udev 와 uevent
- uevent 데몬은 특정 디바이스가 hotplug 되는 경우 커널 영역에서는 모듈과 같은 형태의 device driver 가 로딩되며,
이 때 해당 디바이스에 대한 정보가 uevent 로 udev 데몬에 전달하며 디바이스 노드 생성시 사용되도록 /sys 디렉토리
내부에 대항 디바이스에 대한 uevent 파일을 생성
- udev 데몬은 수신된 uevent 데이터와 /sys/class/device_name/uevent 파일을 비교하여 /dev 디렉토리 아래에 디바이스
노드 생성 여부를 결정 및 수행
### udev 의 특징
* hotplug
- udev 는 hotplug 서브시스템의 일부로 장치가 시스템에 추가되거나 제거되면 hotplug가 udev를 호출
- udev 는 hotplug 이벤트를 받아 sysfs 에서 필요한 정보를 얻어 /dev 아래에 장치 파일 생성
- 리눅스 부팅 시 init 의 실행 시 /sbin/init 프로세스가 /etc/inittab 로딩 후 각 스크립트 등을 실행
- 이 때 sysfs 를 /sys 로 마운트하여 udev 데몬이 동작
- udev 데몬이 실행 된 후 시스템에서 디바이스 노드 생성
- 실제 디바이스가 detect 되면 sysfs 에 등록되고 해당 디바이스는 사용자 공간에서 /sys에 등록
- udev 데몬으로 netlink socket 을 이용하여 새로운 디바이스가 생성되었다는 메시지를 전송
- udev 데몬은 /sys의 디바이스 내용 (Major, Minor 번호 및 생성할 디바이스 파일의 이름 등) 을
이용해서 /dev 에 디바이스 노드를 생성
### udeventd
* Android 에서의 디바이스 관리 방법
- udev 및 uevent 에 대한 내용은 거의 동일
- 시스템 특성상 이를 변형하여 사용
- Froyo 버전 까지 init 프로세스에 의해서 전반적인 시스템 메니지먼트를 이룸
- Gingerbrad 버전부터는 init 프로세스에서 처리하던 디바이스의 관리를 udeventd 데몬으로 분리
./system/core/init/init.c |
int main(int argc, char **argv) { .... if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); ....
} |
- init 프로세스의 main 함수는 시작 부분에 ueventd_main 함수를 호출하여 ueventd 데몬 실행
- ueventd 데몬이나 udevd 데몬은 시스템이 초기화되는 시점에 동작하며 /dev 디렉토리 아래에 디바이스 파일을 동적으로 생성
* ueventd 데몬의 동작 구조
ueventd.%hardware%.rc Nexus Sdml /ueventd.herring.rc 내용 |
/dev/pvrsrvkm 0666 system system /dev/uwibro 0660 system system /dev/swmxctl 0660 system system /dev/video0 0660 system camera /dev/video1 0660 system camera /dev/video2 0660 system camera /dev/s3c-jpg 0660 system camera /dev/s3c-mem 0660 system system /dev/s3c-mfc 0660 media media
/dev/modem_ctl 0660 radio radio /dev/modem_fmt 0660 radio radio /dev/modem_rfs 0660 radio radio /dev/s3c2410_serial3 0660 radio radio /dev/block/mtdblcok5 0660 radio radio /dev/mtd/mtd5ro 0660 radio radio /dev/mtd/mtd5 0660 radio radio
# for Sensor HAL /dev/akm 8973 0660 system system /dev/accelerometer 0660 system system
# for GPS /dev/s3c2410_serial1 0600 gps gps
|
- ueventd 데몬은 위 내용 대로 디바이스 파일 생성
- 시스템 부팅 과정에서 기본적으로 /dev 로 마운트하여 사용하는 tmpfs의 내용 이외에 ueventd 데몬에 의해서 관리 될 시스템 디바이스
파일을 의미
./system/core/init/ueventd.c |
int ueventd_main(int argc, char **argv) { struct pollfd ufd; int nr; char tmp[32];
umask(000);
signal(SIGCHLD, SIG_IGN);
open_devnull_stdio(); klog_init(); INFO("starting ueventd\n");
/* Respect hardware passed in through the kernel cmd line, Here we will look * for androidboot.hardware param in kernel cmdline, and save its value in * hardware[]. */ import_kernel_cmdline(0, import_kernel_nv); get_hardware_name(hardware, &revision);
ueventd_parse_config_file("/ueventd.rc");
snprintf(tmp, sizeof(tmp),"/ueventd.%s.rc", hardware); ueventd_parse_config_file(tmp);
device_init();
ufd.events = POLLIN; ufd.fd = get_device_fd();
while(1) { ufd.revents = 0; nr = poll(&ufd, 1, -1); if (nr <= 0) continue; if (ufd.revents == POLLIN) handle_device_fd(); } }
|
- 위는 ueventd 데몬의 메인 함수
- ueventd.rc 파일이나 ueventd.%hardware%.rc 파일의 내용을 읽어 해당 내용을 초기화
- device_init() 함수를 호출하여 sysfs 의 내용을 토대로한 /dev 디렉토리 업데이트를 처리하는 coldboot 수행
./system/core/init/devices.c
|
void device_init(void) { suseconds_t t0, t1; struct stat info; int fd; #ifdev HAVE_SELINUX struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } };
if(is_selinux_enabled() > 0) sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); #endif /* is 64K enough? udev uses 16MB! */ device_fd = uevent_open_socket(64*1024, true); if(device_fd < 0) return; fcntl(device_fd, F_SETFD, FD_CLOEXEC); fcntl(device_fd, F_SETFD, O_NONBLOCK);
if (stat(coldboot_done, &info) < 0) { t0 = get_usecs(); coldboot("/sys/class"); coldboot("/sys/block"); coldboot("/sys/devices"); t1 = get_usecs(); fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); close(fd); //log_event_print("coldboot%ld uS\n", ((long)(t1 -t0))); ERROR("coldboot %ld uS\n", ((long)(t1 -t0))); } else { //log_event_print("skipping coldboot, already done\n") ERROR("skipping coldboot, already done\n"); } }
|
- 위는 device_init() 함수
- coldboot는 내부적으로 do_coldboot를 호출
./system/core/init/devices.c
|
static void do_coldboot(DIR *d) { struct dirent*de; int dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, "uevent", O_WRONLY); if(fd >=0) { write(fd, "add\n", 4); close(fd); handle_device_fd(); } while((de = readdir(d))) { DIR *d2;
if(de ->d_type != DT_DIR || de->d_name[0] =='.') continue;
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); if(fd < 0) continue;
f2 = fdopendir(fd); if(d2 == 0) close(fd); else { do_coldboot(d2); closedir(d2); } } } |
- 위는 do_coldboot() 의 내용
- ueventd 데몬은 이오ㅔ에도 커널 2.6에서 지원하는 hotplug 기능을 지원
- hotplug 기능은 커널의 uevent 발생을 실시간 모니터링하며 새로 업데이트된 sysfs 의 내용 (/sys)을 이용하여 디바이스 파일을 생성
- ueventd_main 함수에서 while 루프로 처리하는 부분이 커널에서 발생하는 uevent 를 모니터링 하는 부분이며 처리해야 이벤트 발생 시
handle_device_fd() 호출
./system/core/init/devices.c |
#define UEVENT_MSG_LEN 1024 void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ continue;
msg[n] = '\0'; msg[n+1] = '\0';
struct uevent uevent; parse_event(msg, &uevent); handle_device_event(&uevent); handle_firmware_event(&uevent); } }
|
- handle_device_event(&uevent), handle_firmware_event(&uevent) 함수 호출
./system/core/init/devices.c |
static void handle_device_event(struct uevent *uevent) { if (!strcmp(uevent->action,"add")) fixup_sys_perms(uevent->path);
if (!strncmp(uevent->subsystem, "block",5)) { handle_block_device_event(uevent); } else if (!strncmp(uevent->subsystem, "platform", 8)) { handle_platform_device_event(uevent); } else { handle_generic_device_event(uevent); } } |
- handle_device_event() 함수는 각각 action 에 대해서 수행 (add, remove, link)
- add 인 경우 make_device(devpath, path, block, major, minor); 호출하여 mknod 수행
./system/core/init/devices.c |
static void handle_firmware_event(struct uevent *uevent) { pid_t pid; int ret; if(strcmp(uevent->subsystem, "firmware")) return;
if(strcmp(uevent->action, "add")) return;
/* we fork, to avoid making large memory allocations in init proper */ pid = fork(); if (!pid) { process_firmware_event(uevent); exit(EXIT_SUCCESS); } }
|
- handle_firmware_event() 함수는 firmware event 처리
출처 : http://hoonycream.tistory.com/entry/udev