BSD daemon

NetBSD 문서:

커널 프로그래밍 문답

기타

그 밖의 관련된 링크


기타


KNF는 무엇입니까? (맨 위)

KNF는 "Kernel Normal Form"의 약자입니다. - /usr/share/misc/style에 문서화 되어있는 C 코딩 스타일입니다. 소스 트리에는 /usr/src/share/misc/style에 포함되어 있습니다.

`pack' 형식의 사용 (맨 위)

wire 프로토콜 데이터 포맷의 구조체에서는 언제나 `pack' 형식을 사용하고 있습니다.

디버깅을 위한 printf() 사용 (맨 위)

커널 드라이버의 디버깅 정보를 출력하는 가장 간단한 방법은 printf()를 사용 하는것입니다. 커널의 printf는 콘솔에 출력되므로 너무 많이 출력되어 시스템을 사용할 수 없을 정도가 되지 않도록 주의해 주십시오.

강제로 DDB 사용하기. (맨 위)

커널 설정 파일에서 'options DDB'이 설정되어 있는지 확인 해 주십시오. 파일에 '#include "opt_ddb.h"'를 포함하고 'Debugger()'를 사용하면 됩니다.

커널에 새로운 드라이버 추가 (맨 위)

모든 드라이버에 최소한 필요한 것들

probe와 attach 루틴을 쓰려면

/usr/src/sys/arch/<your-arch>/<your-arch>/conf.c에 엔트리를 추가해 주십시오.

아래에 두개의 테이블이 있습니다:

대부분의 엔트리는 cdev_xxx_init()라고 하는 형식이 됩니다. 이것은 표준 Unix 장치 스위치 루틴의 프로토타입을 위한 매크로입니다.

probe와 attach 루틴은 부팅 할때 호출 됩니다. open(), close(), read(), write() 루틴은 주 번호와 테이블 인덱스가 일치하는 장치 파일이 열렸을 때 호출 됩니다. 예를 들면 cdevsw[]/bdevsw 안의 장치 번호 18의 "open" 루틴이 호출 됩니다.

대부분 드라이버는 버스의 고유 attach 코드와 머신 독립적인 코어로 나누어져 있습니다. 예를 들면 PCI lance 이더넷 드라이버는 아래 파일로 구성되어 있습니다:

autoconf 설명 참고.

모든 autoconf는 어떻게 작동 되는가? (맨 위)

autoconf의 작동 방식을 이해 한다면 autoconf는 아주 간단합니다. 장치 probe 트리 구축 및 동작의 세부 사항을 무시하고 각각의 "leaf" 드라이버를 위해 필요한 것들은 다음과 같습니다:

  1. 각각의 드라이버는 세가지의 구조를 정의합니다. - 그 드라이버의 구조체 크기, probe 함수, attach 함수; 이것은 컴파일 되고 실행 됩니다. - 예제:

    struct cfattach foo_baz_ca = {
        sizeof(struct foo_baz_softc), foo_baz_match, foo_baz_attach
    };
    
  2. 커널이 시작되면 장치에 접속(attach)할 시간이 옵니다. autoconf 코드는 디바이스의 probe 루틴을 호출하고 포인터를 부모(struct device *parent)에게 넘깁니다, 접속(attach) 태그 구조체(void *aux)를 가르킵니다, 그리고 autoconf 노드 (struct cfdata *cf)를 자기것으로 만듭니다. 드라이버는 장치를 찾기를 기대합니다. (일반적으로 위치, 설정 정보는 접속(attach) 태그를 무시합니다.) 만약 그렇다면 probe 루틴은 1을 리턴해야 합니다. 장치가 없다면 probe 루틴은 0을 리턴합니다. 어느 쪽의 경우에도 상태가 유지되지 않아야 합니다
  3. probe가 성공을 리턴 했다면 autoconf는 장치의 *_ca와 그것의 접속(attach)루틴 호출을 정의한 메모리 크기를 할당 하고, 그것을 지나쳐 부모(struct device *parent) 에게 포인터를 가르킵니다. 그리고 새로이 메모리(struct device *self)와 접속(attach) 태그(void *aux)를 할당한 곳에 포인터를 가르킵니다. 따라서 드라이버는 정확한 포트와 메모리, 할당된 리소스를 찾는것과 내부 구조체 초기화를 하는 것을 기대합니다. 모든 드라이버의 경우 세부 정보가 메모리에 할당되어 유지되었어야 합니다.

예제: PCI ethernet 장치 'baz'의 커널 설정을 살펴 봅시다. 다음 보기와 같습니다:

pci*    at mainbus?
baz*    at pci? dev ? function ?

autoconf는 물리 장치가 pci 버스에 접속하는 것을 반복 합니다. 각 물리 장치는 pci 버스를 커널 안에 등록 하고 드라이버의 probe 루틴을 호출 합니다. probe 루틴이 1을 리턴 한다면 autoconf는 작업 반복을 멈추고 위의 작업 3)을 실행 합니다. 접속(attach)에서 되돌아 와서 autoconf는 다음 물리 장치에 대한 루틴을 계속합니다.

커널에 새로운 드라이버 추가를 참고하십시오.

새로운 시스템 콜 추가 (맨 위)

syscalls.master에 새로운 엔트리를 추가하고 syscall 루틴을 /usr/src/lib/libc/sys/Makefile.inc에 작성합니다.

새로운 sysctl 추가 (맨 위)

이 질문은 posting을 보시고 tech-kern에 질문해 주십시오.

참고. NetBSD 1.6과 그 이상에서는 특별한 "vendor" sysctl 카테고리를 가지고 있습니다. 그것들은 vendor 상세 엔트리를 위해 예약되어 있습니다. 자세한 정보는 sysctl(8)을 참고하십시오.

가상 장치(pseudo-device)에 mmap을 사용하는 방법 (맨 위)

당신이 만든 장치는 아마 문자 장치일것입니다. 그렇다면 장치 pager를 사용하고 있을 것입니다.(VM 시스템은 모든 것을 숨기고 있으므로 걱정하지 마십시오.)

먼저 mmap 인터페이스를 위해서 적당한 오프셋 몇개를 선택해 주십시오. "mmap 오프셋 0-M은 오브젝트 A에게 주고, N-O는 오브젝트 B에게 준다", 기타. 처럼 합니다.

이것이 끝나면 mmap 루틴은 다음과 같습니다:

int
foommap(dev, off, prot)
        dev_t dev;
        int off, prot;
{

        if (off & PAGE_MASK)
                panic("foommap");

        if ((u_int)off >= FOO_REGION1_MMAP_OFFSET &&
            (u_int)off < (FOO_REGION1_MMAP_OFFSET + FOO_REGION1_SIZE))
                return (atop(FOO_REGION1_ADDR + ((u_int)off -
                    FOO_REGION1_MMAP_OFFSET)));

        if ((u_int)off >= FOO_REGION2_MMAP_OFFSET &&
            (u_int)off < (FOO_REGION2_MMAP_OFFSET + FOO_REGION2_SIZE))
                return (atop(FOO_REGION1_ADDR + ((u_int)off -
                    FOO_REGION2_MMAP_OFFSET)));

        /* Page not found. */
        return (-1);
}

그런데, 실제로 단순한 커널 메모리 오브젝트를 mmap 하므로 코드는 좀 더 복잡하게 됩니다.(결국 가상 장치(pesudo-device)이기 때문입니다.)

이것을 동작시키기 위해서는 할당한 메모리 오브젝트를 페이지된 경계에 확실히 mmap 해야 합니다. 만약 할당한 메모리 크기 >= PAGE_SIZE 라면 괜찮지만, 그렇지 않으면 uvm_km_alloc()를 사용해 할당 크기를 페이지 크기에 맞게 해주십시오.

약간 수정한 것은 다음과 같습니다:

int
foommap(dev, off, prot)
        dev_t dev;
        int off, prot;
{
        paddr_t pa;

        if (off & PAGE_MASK)
                panic("foommap: offset not page aligned");

        if ((u_int)off >= FOO_REGION1_MMAP_OFFSET &&
            (u_int)off < (FOO_REGION1_MMAP_OFFSET + FOO_REGION1_SIZE)) {
                if ((vaddr_t)foo_object1 & PAGE_MASK)
                        panic("foommap: foo_object1 not page aligned");
                if (pmap_extract(pmap_kernel(), foo_object1 +
                    (u_int)off - FOO_REGION1_MMAP_OFFSET, &pa) == FALSE)
                        panic("foommap: foo_object1 page not mapped");
                return (atop(pa));
        }

        if ((u_int)off >= FOO_REGION2_MMAP_OFFSET &&
            (u_int)off < (FOO_REGION2_MMAP_OFFSET + FOO_REGION2_SIZE)) {
                if ((vaddr_t)foo_object2 & PAGE_MASK)
                        panic("foommap: foo_object2 not page aligned");
                if (pmap_extract(pmap_kernel(), foo_object2 +
                    (u_int)off - FOO_REGION2_MMAP_OFFSET, &pa) == FALSE)
                        panic("foommap: foo_object2 page not mapped");
                return (atop(pa));
        }

        /* Page not found. */
        return (-1);
}

유저영역에서 커널 구조체에 접근 (맨 위)

좋은 예가 /usr/src/usr.bin/vmstat/dkstats.c 에 있습니다. 여기에서는 디스크의 통계 정보를 읽고 있습니다.

참고할 간단한 예제 드라이버는 없습니까? (맨 위)

sus/dev/pci/puc.c를 참고해 주십시오. 이것은 가장 간단한 드라이버의 하나입니다. PUC는 하나 이상의 직렬 혹은 병렬 포트를 가지는 장치로 일반적으로 표준 칩을 사용하고 있습니다.(예를 들면 직렬의 경우 16550 UART). 드라이버는 단지 직렬 혹은 병렬 콘트롤러 레지스터의 I/O 주소를 찾아 그것을 직렬, 병렬 드라이버에게 건내주는 일을 합니다.

상위 문서(NetBSD 문서: 커널)로
홈페이지
최상위 문서로

(연락하는 방법) $NetBSD: programming.html,v 1.6 2006/06/22 15:49:21 jschauma Exp $
Copyright © 1994-2003 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.