Linux 커 널 모듈 자동 로드 메커니즘

커 널 을 시작 하 는 과정 에서 어떤 모듈 을 자동 으로 불 러 오 려 면 어떻게 해 야 합 니까?가장 생각 나 는 방법 은 / etc / init. d / 에 시작 스 크 립 트 를 추가 한 다음 / etc / rcN. d / 디 렉 터 리 에 심 볼 릭 링크 를 만 드 는 것 입 니 다. 이 링크 의 이름 은 S 로 시작 합 니 다. 이 커 널 이 시작 되면 이 스 크 립 트 가 자동 으로 실 행 됩 니 다. 그러면 스 크 립 트 에서 modprobe 를 사용 하여 자동 으로 불 러 올 수 있 습 니 다.그러나 커 널 에 많은 하드웨어 장치 의 드라이버 를 불 러 왔 고 / etc 디 렉 터 리 를 검색 하 였 으 나 이 하드웨어 장치 드라이버 를 불 러 오 는 모듈 을 불 러 오 는 스 크 립 트 는 발견 되 지 않 았 습 니 다.그렇다면 이 모듈 들 은 어떻게 불 러 옵 니까?
  • 모든 장치 에는 Verdon ID, Device ID, SubVendor ID 등 정보 가 있다.그리고 모든 장치 드라이버 는 자신 이 어떤 Verdon ID, Deviece
  • 를 위해
    ID, SubVendor ID 의 장치 가 서 비 스 를 제공 합 니 다.PCI 장 치 를 예 로 들 면 pcidevice_id 의 데이터 구조 로 이 기능 을 실현 합 니 다.예 를 들 어 RTL 8139 의 Pcidevice_id 정의:
    static struct pci_device_id rtl8139_pci_tbl[] = {
            {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
            {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
            ......
    }
    MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
    

    위의 정보 에 따 르 면 베 르 돈 ID 가 0x10EC 이 고 Device ID 가 0x 8139 이 며 0x 8138 인 PCI 장치 (SubVendor ID 와 SubDeviceID 가 PCI ANY ID 로 제한 되 지 않 음 을 표시 함) 는 모두 이 드라이버 (8139 too) 를 사용 할 수 있다.
  • 모듈 을 설치 할 때 depmod 는 모듈 의 rtl 8139 에 따라pci_tbl 의 정 보 는 아래 의 정 보 를 생 성하 여 / lib / modules / uname - r / modules. alias 파일 에 저장 합 니 다. 그 내용 은 다음 과 같 습 니 다.
  • alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139too
    alias pci:v000010ECd00008139sv*sd*bc*sc*i* 8139too
    ......
    

    v 뒤의 000010 EC 는 벤 더 ID 가 10EC 이 고 d 뒤의 00008138 은 Device ID 가 8139 이 며 sv 는 sd 와 SubVendor ID 와 SubDevice ID 이 며 뒤의 별 번 호 는 임 의 일치 임 을 나타 낸다.
    또한 / lib / modules / uname - r / modules. dep 파일 에 이 모듈 간 의존 관 계 를 저장 합 니 다. 그 내용 은 다음 과 같 습 니 다.
    (         。)
    8139too.ko:mii.ko
    
  • 커 널 시작 과정 에서 버스 드라이버 는 버스 프로 토 콜 을 통 해 버스 매 거 진 을 진행 합 니 다 (버스 드라이버 는 항상 커 널 에 통합 되 어 모듈 방식 으로 불 러 올 수 없습니다. make menuconfig 를 통 해 Bus
  • 에 들 어 갈 수 있 습 니 다.
    options, 이 안의 각종 버스 는 Y 나 N 만 선택 할 수 있 고 M.) 을 선택 할 수 없 으 며 모든 장치 에 장치 대상 을 만 들 수 있 습 니 다.모든 버스 대상 은 kset 대상 이 있 습 니 다. 모든 장치 대상 은 하나의 kobject 대상 을 포함 하고 kobject 는 kset 대상 에 연결 되 어 있 습 니 다. 이렇게 버스 와 버스 사이 에 버스 와 장치 장치 간 에 하나의 트 리 구조 로 구성 되 어 있 습 니 다.버스 드라이버 가 스 캔 된 장치 에 장치 대상 을 만 들 때 kobject 대상 을 초기 화하 고 장치 트 리 에 연결 하 며 kobject 를 호출 합 니 다.uevent () 는 이 (새 장 치 를 추가 하 는) 이벤트 와 관련 정보 (장치 의 VendorID, DeviceID 등 정보 포함) 를 netlink 를 통 해 사용자 상태 로 보 냅 니 다.사용자 상태의 udevd 에서 이 사건 을 감지 하면 이 정보 에 따라 / lib / modules / uname - r / modules. alias 파일 을 열 수 있 습 니 다.
    alias pci:v000010ECd00008138sv*sd*bc*sc*i* 8139too
    

    새로 스 캔 한 장치 구동 모듈 이 8139 too 라 는 것 을 알 게 되 었 습 니 다.그래서 modprobe 는 8139 too 라 는 모듈 을 불 러 올 줄 알 았 습 니 다. 또한 modprobe 는 modules. dep 파일 에 따 르 면 8139 too 는 mii. ko 에 의존 하고 mii. ko 가 불 러 오지 않 으 면 modprobe 는 먼저 mii. ko 를 불 러 오고 이어서 8139 too. ko 를 불 러 옵 니 다.
    테스트
    셸 에서 실행:
    # ps aux | grep udevd
    
    root     25063  ...... /sbin/udevd --daemon
    

    udevd 의 프로 세 스 ID 25063 을 받 았 습 니 다. 이 프로 세 스 를 끝 냅 니 다.
    # kill -9 25063
    

    그리고 udevd 를 추적 하여 셸 에서 실행 합 니 다:
    # strace -f /sbin/udevd --daemon
    

    이때, 우 리 는 udevd 의 출력 을 다음 과 같이 보 았 다.
    ......
    close(8)                                = 0
    munmap(0xb7f8c000, 4096)                = 0
    select(7, [3 4 5 6], NULL, NULL, NULL
    

    우 리 는 udevd 가 여기에서 select () 함수 에 막 혀 있 는 것 을 발견 했다.
    select 함수 원형 은 다음 과 같 습 니 다.
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
         :nfds           ,   7(   6 ?)。
         :readfds         ,   3,4,5,6.
         :writefds         ,   NULL。
         :exceptfds          ,   NULL。
         :timeout      ,   NULL。
    

    select 함수 의 역할 은 readfds 의 모든 파일 에 데 이 터 를 읽 을 수 있 거나 witefds 의 모든 파일 을 기록 할 수 있 거나 exceptfds 의 모든 파일 에 이상 이 있 을 때 되 돌아 오 는 것 입 니 다.그렇지 않 으 면 현재 프로 세 스 를 막 습 니 다. 항소 조건 이 만족 하거나 차단 시간 이 timeout 에서 지정 한 시간 을 초과 하여 현재 프로 세 스 가 깨 어 나 select 로 돌아 갑 니 다.
    그래서 여기 서 udevd 는 3, 4, 5, 6 이 몇 개의 파일 을 읽 을 수 있 는 데이터 가 있어 야 깨 어 납 니 다.현재 셸 에서 실행:
    # ps aux | grep udevd
    root     27615  ...... strace -o /tmp/udevd.debug -f /sbin/udevd --daemon
    root     27617  ...... /sbin/udevd --daemon
    

    udevd 의 프로 세 스 id 는 27617 입 니 다. 이제 select 가 기다 리 는 몇 개의 파일 을 보 겠 습 니 다.
    # cd /proc/27615/fd
    # ls -l
    
    udevd     ,    ,       /dev/null.
    0 -> /dev/null
    1 -> /dev/null
    2 -> /dev/null
    
    udevd           。
    3 -> /inotify
    4 -> socket:[331468]
    5 -> socket:[331469]
    6 -> pipe:[331470]
    7 -> pipe:[331470]
    

    실행 중 8139 의 네트워크 카드 를 삽입 하 는 것 이 불편 하기 때문에 지금 우 리 는 U 디스크 로 시험 을 하고 있 습 니 다. U 디스크 를 삽입 하면 strace 의 출력 을 볼 수 있 습 니 다. 출력 에서 udevd 가 select 에서 돌아 온 후에 modprobe 로드 드라이브 모듈 을 호출 하고 sys 를 호출 하 는 것 을 볼 수 있 습 니 다.mknod, dev 디 렉 터 리 에 해당 하 는 노드 를 만 들 었 습 니 다.
    execve("/sbin/modprobe", ["/sbin/modprobe", "-Q", "usb:v05ACp1301d0100dc00dsc00dp00"...]
    ......
    mknod("/dev/sdb", S_IFBLK|0660, makedev(8, 16)) = 0
    ......
    

    여기 modprobe 의 인자 "usb: v05AC..." 는 modules. alias 의 한 모듈 에 대응 합 니 다.
    udevmonitor 를 통 해 커 널 이 netlink 를 통 해 udevd 에 보 낸 메 시 지 를 볼 수 있 고 셸 에서 실 행 됩 니 다.
    # udevmonitor --env 
    

    그리고 USB 를 삽입 하면 관련 udevd 에 보 내 는 메 시 지 를 볼 수 있 습 니 다.
    = = 커 널 처리 과정 = =:
    여기 서 PCI 버스 를 예 로 들 어 이 과정 에서 커 널 이 어떻게 처리 되 는 지 살 펴 보 자.PCI 버스 드라이버 가 새로운 장 치 를 검색 할 때 장치 대상 을 만 들 고 Pci 를 호출 합 니 다.bus_add_device () 함수, 이 함 수 는 최종 적 으로 kobject 를 호출 합 니 다.uevent () 는 netlink 를 통 해 사용자 상태의 udevd 에 메 시 지 를 보 냅 니 다.
    int pci_bus_add_device(struct pci_dev *dev)
    {
            int retval;
            retval = device_add(&dev->dev);
    
            ......
    
            return 0;
    }
    

    device_add () 코드 는 다음 과 같 습 니 다.
    int device_add(struct device *dev)
    {
            struct device *parent = NULL;
    
            dev = get_device(dev);
    
            ......
    
            error = bus_add_device(dev);
            if (error)
                    goto BusError;
            kobject_uevent(&dev->kobj, KOBJ_ADD);
            ......
    }
    

    device_add () 관련 데이터 구 조 를 준비 한 후 kobject 를 호출 합 니 다.uevent (), 이 메 시 지 를 사용자 공간 에 보 내 는 udevd.
    int kobject_uevent(struct kobject *kobj, enum kobject_action action)
    {
            return kobject_uevent_env(kobj, action, NULL);
    }
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
    {
            struct kobj_uevent_env *env;
            const char *action_string = kobject_actions[action];
            const char *devpath = NULL;
            const char *subsystem;
            struct kobject *top_kobj;
            struct kset *kset;
            struct kset_uevent_ops *uevent_ops;
            u64 seq;
            int i = 0;
            int retval = 0;
    
            ......
    
            /* default keys */
            retval = add_uevent_var(env, "ACTION=%s", action_string);
            if (retval)
                    goto exit;
            retval = add_uevent_var(env, "DEVPATH=%s", devpath);
            if (retval)
                    goto exit;
            retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
            if (retval)
                    goto exit;
    
            /* keys passed in from the caller */
            if (envp_ext) {
                    for (i = 0; envp_ext[i]; i++) {
                            retval = add_uevent_var(env, envp_ext[i]);
                            if (retval)
                                    goto exit;
                    }
            }
    
            ......
    
            /*   netlink    ,      udevd     select()    ,       。 */
    #if defined(CONFIG_NET)
            /* send netlink message */
            if (uevent_sock) {
                    struct sk_buff *skb;
                    size_t len;
    
                    /* allocate message with the maximum possible size */
                    len = strlen(action_string) + strlen(devpath) + 2;
                    skb = alloc_skb(len + env->buflen, GFP_KERNEL);
                    if (skb) {
                            char *scratch;
    
                            /* add header */
                            scratch = skb_put(skb, len);
                            sprintf(scratch, "%s@%s", action_string, devpath);
    
                            /* copy keys to our continuous event payload buffer */
                            for (i = 0; i < env->envp_idx; i++) {
                                    len = strlen(env->envp[i]) + 1;
                                    scratch = skb_put(skb, len);
                                    strcpy(scratch, env->envp[i]);
                            }
    
                            NETLINK_CB(skb).dst_group = 1;
                            netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
                    }
            }
    #endif
    
            ......
            return retval;
    }

           현재 우 리 는 / dev 디 렉 터 리 아래 의 장치 파일 이 udevd 가 만 든 것 을 알 고 있 습 니 다. 그러나 커 널 시작 과정 에서 mount 루트 디 렉 터 리 가 필요 합 니 다. 보통 우리 의 루트 디 렉 터 리 는 하 드 디스크 에 있 습 니 다. 예 를 들 어 / dev / sda 1 이지 만 하 드 디스크 에 대응 하 는 드라이버 가 불 러 오기 전에 / dev / sda 1 은 존재 하지 않 습 니 다. / dev / sda 1 이 없 으 면...루트 디 렉 터 리 를 mount / dev / sda 1 / 로 마 운 트 할 수 없습니다.다른 한편, udevd 는 실행 가능 한 파일 입 니 다. 하 드 디스크 드라이버 가 불 러 오지 않 았 을 때 루트 디 렉 터 리 가 존재 하지 않 으 면 udevd 는 실행 할 수 없습니다.udevd 가 실행 되 지 않 으 면 디스크 드라이버 를 자동 으로 불 러 오지 않 고 / dev / sda 1 을 자동 으로 만 들 수 없습니다.이거 자물쇠 아니 야?그럼 리 눅 스 는 어떻게 시 작 했 어 요?

    좋은 웹페이지 즐겨찾기