sysfs & procfs 인터페이스

sungho·2025년 1월 8일

디바이스 드라이버

목록 보기
11/12

리눅스 커널과 유저 공간 간 데이터 교환을 위한 인터페이스에서  sysfs와 procfs에 대해 알아보도록 하겠습니다. 이 두 가지 인터페이스는 디바이스 드라이버 개발 및 시스템 관리에서 활용됩니다.

1. 유저 공간 인터페이스

  • 커널과 유저 공간 간 데이터 교환을 위해 중간 다리 역할을 하는 인터페이스가 필요
  1. /dev 노드를 통한 접근
    • open(), read(), write(), close(), ioctl() 등 시스템 콜을 사용
    • Character device, block device 등을 제어하는 기본적이고 널리 쓰이는 방식
  2. sysfs 파일 시스템
    • /sys 디렉터리 아래에 파일 형태로 커널 정보를 제공하는 가상 파일 시스템
    • read, write 시스템 콜을 사용하여 파일처럼 접근하며 문자열 데이터를 주고 받음
  3. Network sockets and related APIs
    • 네트워크 통신을 위한 인터페이스
    • socket API를 사용하여 데이터를 송수신
    • 네트워크 관련 디바이스 드라이버에서 사용

2. sysfs

sysfs의 개념

  • sysfs는 리눅스 커널 디바이스 드라이버 정보를 유저 공간에 파일 시스템 형태로 제공하는 가상 파일 시스템
  • /sys 디렉터리 아래에 계층적으로 구성되

sysfs 사용이유

  • 간편한 접근성
    • /dev 노드를 통한 방식보다 훨씬 간편하게 커널 정보에 접근
    • cat, echo 등 쉘 명령어로도 쉽게 정보를 읽고 쓸 수 있음
  • 표준화된 인터페이스
    • class, bus, devices 등 커널 객체 모델을 기반으로 정보를 제공하기 때문에 일관되고 체계적인 방식으로 디바이스 정보를 관리
  • 동적인 시스템 관리
    • 시스템 부팅 후에도 디바이스 정보를 동적으로 변경하고 관리

sysfs 마운트

  • sysfs를 사용하려면 먼저 마운트
  • 명령어 사용하여 sysfs를 /sys 디렉터리에 마운트
mount -t sysfs nodev /sys

/sys 디렉터리 탐험

  • /sys/class
    • 디바이스 클래스별로 정보를 제공
    • LED, PWM, IIO(Industrial I/O) 디바이스 관련 정보
      • /sys/class/leds, /sys/class/pwm, /sys/class/iio 에서 찾아볼 수 있음
  • /sys/bus
    • 디바이스 버스별로 정보를 제공
      • I2C, SPI, USB 등 버스에 연결된 디바이스 정보를 확인
  • /sys/devices
    • 시스템에 존재하는 모든 디바이스 정보를 계층적인 구조
  • /sys/kernel
    • 커널 자체에 대한 정보

커널 객체

  • sysfs는 커널 객체(kernel objects) 를 기반으로 동작
  • 커널 객체는 참조 카운터(reference counter) 를 효율적으로 관리
  • sysfs와 디바이스 모델 간 인터페이스 역할 수행하도록 설계
  • 참조 카운터는 자원 사용 횟수 추적하여 더 이상 사용되지 않는 자원 안전하게 해제하는 데 사용
  • kobject는 이러한 참조 카운터 관리를 위한 메커니즘 제공
  • kobject 구조체는 커널 객체 나타내는 핵심적인 자료 구조
    • kobject는 sysfs에서 디렉터리로 표현
    • 디렉터리 내 파일들은 해당 객체 속성(attributes)

sysfs in Linux Device Driver

  • 디바이스 드라이버 개발자는 sysfs 활용하여 드라이버 관련 정보 유저 공간에 쉽게 제공
  • /sys 아래에 디렉터리 생성하고 그 안에 sysfs 파일 생성하여 데이터 주고받기

/sys 아래 디렉터리 생성

  • kobject_create_and_add() 함수 사용하여 /sys 아래에 새로운 디렉터리 생성
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
  • name
    • 생성할 디렉터리 이름
  • parent
    • 상위 디렉터리 나타내는 kobject 구조체
    • kernel_kobj
      • /sys/kernel 아래에 생성
    • NULL
      • /sys 아래에 생성

sysfs 파일 생성

  • sysfs 파일 생성하기 위해서는 kobj_attribute 구조체 설정
  • sysfs_create_file() 함수 사용
struct kobj_attribute {
    struct attribute attr;
    ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
    ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);
};
  • attr
    • 파일 이름과 권한 설정.
  • show
    • 파일 읽을 때 호출되는 함수
    • 커널 데이터 유저 공간 버퍼(buf)에 복사
  • store
    • 파일에 쓸 때 호출되는 함수
    • 유저 공간 버퍼(buf)에서 데이터 읽어와 커널 데이터에 반영
  • __ATTR 매크로 사용하면 kobj_attribute 구조체 간편하게 생성
#define __ATTR(_name, _mode, _show, _store) { \
    .attr = {.name = __stringify(_name), .mode = _mode },   \
    .show   = _show,                                        \
    .store  = _store,                                        \
}
  • sysfs_create_file() 함수는 설정된 kobj_attribute 기반으로 sysfs 파일 생성
int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
  • kobj
    • sysfs 파일 생성될 디렉터리 나타내는 kobject 구조체
  • attr
    • sysfs 파일 속성 나타내는 attribute 구조체

sysfs 그룹 생성

  • 여러 개 sysfs파일을 한번에 생성하려면 attribute_group 구조체 설정과 sysfs_create_group() 함수 사용
struct attribute_group {
    const char              *name;
    umode_t                 (*is_visible)(struct kobject *,
                           struct attribute *, int);
    struct attribute        **attrs;
    struct bin_attribute    **bin_attrs;
};
  • attrs: 그룹으로 묶을 attribute 구조체 배열
  • name: 그룹 이름 나타내는 문자열

sysfs_create_group() 함수는 설정된 attribute_group 기반으로 sysfs 파일들을 생성

int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
  • kobj
    • sysfs 파일 생성될 디렉터리 나타내는 kobject 구조체
  • grp
    • sysfs 파일 속성 나타내는 attribute_group 구조체

sysfs 코드

  • /sys/kernel/ksys 디렉터리 생성
  • test 라는 파일 만들어 정수 값 읽고 쓰기
// k_sysfs.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/string.h>

static struct kobject *ksys_kobj;
static int sysfs_value;

static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    pr_info("show attribute: %s\n", attr->attr.name);
    return scnprintf(buf, PAGE_SIZE, "%d\n", sysfs_value);
}

static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    int val;

    if (!sscanf(buf, "%d", &val))
        return -EINVAL;

    sysfs_value = val;
    pr_info("store value: %d\n", val);
    return count;
}

static struct kobj_attribute test_attr = __ATTR(test, 0660, sysfs_show, sysfs_store);

static struct attribute *k_attrs[] = {
    &test_attr.attr,
    NULL,
};

static struct attribute_group k_attr_group = {
    .attrs = k_attrs,
};

static int __init k_module_init(void)
{
    int err = -1;

    pr_info("k_sysfs module init\n");

    ksys_kobj = kobject_create_and_add("ksys", kernel_kobj);
    if (!ksys_kobj)
        return -ENOMEM;

    err = sysfs_create_group(ksys_kobj, &k_attr_group);
    if (err)
        kobject_put(ksys_kobj);

    return err;
}

static void __exit k_module_exit(void)
{
    if (ksys_kobj)
        kobject_put(ksys_kobj);
    pr_info("k_sysfs module exit\n");
}

module_init(k_module_init);
module_exit(k_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple sysfs example");

sysfs 모듈 실행

insmod ./k_sysfs.ko
cd /sys/kernel/ksys/
ls
test
cat test
0
echo 23 > test
cat test
23
dmesg | tail -n 2
[  +0.000005] show attribute: test
[  +0.001859] store value: 23
lsmod | grep k_sysfs
k_sysfs                16384  0
rmmod k_sysfs

3. procfs

procfs의 개념

  • procfs는 /proc 디렉터리 아래에 프로세스 정보와 커널 정보를 파일 시스템 형태로 제공하는 또 다른 가상 파일 시스템
  • sysfs 등장하기 전까지 널리 사용되었지만 현재는 sysfs 사용 권장하는 추세

procfs 대신 sysfs를 권장이유

  • 혼재된 정보
    • procfs는 프로세스 정보와 커널 정보 혼재되어 있어 sysfs에 비해 체계적이지 않음
  • 보안 문제
    • procfs는 모든 유저에게 모든 프로세스 정보 노출할 위험.
  • 중복 기능
    • procfs에서 제공하는 많은 정보가 sysfs에서도 제공

procfs 구조

  • /proc/[PID]: 프로세스에 대한 정보 담고 있는 디렉터리
    • /proc/[PID]/cmdline: 프로세스 실행에 사용된 커맨드 라인 인자
    • /proc/[PID]/status: 프로세스 상태 정보
    • /proc/[PID]/maps: 프로세스 메모리 맵 정보
  • /proc/cpuinfo: CPU 정보 제공하는 파일
  • /proc/meminfo: 메모리 정보 제공하는 파일
  • /proc/modules: 로드된 모듈 목록 제공하는 파일

procfs 코드

  • /proc/k_proc/k_file 파일 생성하여 문자열 읽고 쓸 수 있는 간단한 procfs 모듈 예제
// k_procfs.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/seq_file.h>

static struct proc_dir_entry *proc_dir;
static struct proc_dir_entry *proc_file;

static int k_proc_show(struct seq_file *m, void *v) {
    seq_printf(m, "procfs read function\n");
    return 0;
}

static ssize_t k_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *offset)
{
    char buf[255];
    int to_copy, not_copied, delta;

    memset(buf, 0, sizeof(buf));

    to_copy = min(count, sizeof(buf));
    not_copied = copy_from_user(buf, user_buffer, to_copy);
    pr_info("procfs: %s\n", buf);

    delta = to_copy - not_copied;
    return delta;
}

static int k_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, k_proc_show, NULL);
};

static const struct proc_ops fops = {
    .proc_open = k_proc_open,
    .proc_read = seq_read,
    .proc_write = k_write,
    .proc_release = single_release,
};

static int __init k_module_init(void)
{
    proc_dir = proc_mkdir("k_proc", NULL);
    if (proc_dir == NULL) {
        pr_info("procfs_test - Error creating /proc/k_proc\n");
        return -ENOMEM;
    }

    proc_file = proc_create("k_file", 0666, proc_dir, &fops);
    if (proc_file == NULL) {
        pr_info("procfs_test - Error creating /proc/k_proc/k_file\n");
        proc_remove(proc_dir);
        return -ENOMEM;
    }

    return 0;
}

static void __exit k_module_exit(void)
{
    proc_remove(proc_file);
    proc_remove(proc_dir);
    pr_info("k_procfs module exit\n");
}

module_init(k_module_init);
module_exit(k_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple procfs example");

0개의 댓글