当前位置:Linux教程 - Linux - 如何在proc中加入一个档案

如何在proc中加入一个档案



        

    简介

    PROC是一个特殊的档案系统,核心藉由这个档案系统可以提供应用程式一个 安全的界面来存取某些特别的资料。例如实体记忆体的使用状况,或者是某 个行程开启了那些档案。

    当我们在核心中加入某个新的功能或是驱动程式後,可能会想提供一个方法 让应用程式可以取得驱动程式中某些状态,一般来说这个功能可能需要经由写作 一个处理ioctl的呼叫来达成。这个界面对於一些比较『功能性』的资料可能是很 适合的,因为使用者程式可能必须将这些资料读出後再做一定的处理。但对於一 些『资讯性』的资料,例如记忆体的使用状况,或者是驱动装置的统计资料等。 我们可能需要一个比较简单易用的界面来取得它们。

    PROC档案系统硬提供了我们这样的一个界面,我们可以用cat,more或任何的 文书编辑程式来观看这些资料,例如来观看一个系统记忆体的使用状况可以用

    # cat /proc/meminfo
    total: used: free: shared: buffers: cached:
    Mem: 31698944 12648448 19050496 6770688 1581056 6815744
    Swap: 0 0 0
    MemTotal: 30956 kB
    MemFree: 18604 kB
    MemShared: 6612 kB
    Buffers: 1544 kB
    Cached: 6656 kB
    SwapTotal: 0 kB
    SwapFree: 0 kB

    目前在Linux中几乎所有的资料都可以经由PROC档案系统取得一些统计性甚或功能 性的资料。有些驱动程式甚至允许经由PROC档案系统改变某个功能变数的值,例 如JAVA执行档格式的驱动程式允许我们经由PROC档案系统设定JAVA直译程式的执 行档名称。

    加入一个档案

    如果你的驱动程式不是很复杂,那直接在PROC的根目录底下加入一个档案是比较简 单的做法。下面举一个例子告诉大家这是多麻简单的一件事,但如果你想要提供比 较复杂的资讯,後面会有更进一步的说明。

    接下来我以一个很简单的例子做说明,在这个例子之中我们在PROC的根目录底 下加入一个叫做test的档案,你可以用下面的命令读出其中的值。

    # cat /proc/test
    test status

    要达到这个目的我们必须改变三个档案,第一个是include/linux/proc_fs.h这个 档
    案。

    enum root_directory_inos {
    PROC_ROOT_INO = 1,
    PROC_LOADAVG,
    PROC_UPTIME,
    PROC_MEMINFO,
    PROC_KMSG,
    PROC_VERSION,
    PROC_CPUINFO,
    PROC_PCI,
    PROC_SELF, /* will change inode # */
    PROC_NET,
    PROC_SCSI,
    PROC_MALLOC,
    PROC_KCORE,
    PROC_MODULES,
    PROC_STAT,
    PROC_DEVICES,
    PROC_INTERRUPTS,
    PROC_FILESYSTEMS,
    PROC_KSYMS,
    PROC_DMA,
    PROC_IOPORTS,
    #ifdef __SMP_PROF__
    PROC_SMP_PROF,
    #endif
    PROC_PROFILE, /* whether enabled or not */
    PROC_CMDLINE,
    PROC_SYS,
    PROC_MTAB,
    PROC_MD,
    PROC_RTC,
    PROC_LOCKS
    #ifdef CONFIG_TESTPROC
    ,PROC_TEST
    #endif
    };

    在CONFIG_TESTPROC中的程式是我们新加入的部份,这个enumerate中宣告的是在PROC根目录下的一些档案代码。PROC是一个虚拟的档案系统,所以实际上的资料可能散见於各驱动程式之中,而非在任何装置之上。那INODE中的位置栏位记载些什麽呢? 对 一般的档案系统而言它们应该是装置的逻辑位置,而在PROC中记载的就是上列的数值了。

    PROC提供了一些其本的支援给驱动程式可以很容易的在档案系统中加入一个档案, 而不必各自去做一些处理INODE的动作。在我们的要求中只需在根目录中加入一个叫 test的档案就可以了。所以我们可以直接修改root.c这个档案的proc_root_init这 个函数,这个函数负责在根目录中加入各种不同驱动程式需要的档案。

    #ifdef CONFIG_TESTPROC
    proc_register(&proc_root, &(struct proc_dir_entry) {
    PROC_TEST, 4, \"test\",
    S_IFREG | S_IRUGO, 1, 0, 0,
    });
    #endif

    请注意\"test\"和PROC_TEST这二个引数,前者是在目录中的档案名称,而後者是这个 档案所对映的代码。接下来我们会看到,这个代码被用来呼叫定义於驱动程式中的函数。这个函数的功能是提供这个档案的内容。

    到此PROC档案系统中的修改已经完成了,当核心被重新建立後已经可以在档案 系统中看见test这个档案了。但这个档案的内容是空的,如何为它加入内容呢? 这 件工作应该由驱动程式来做,但如何将二者连起来呢? 这就要靠刚才定义的PROC_TEST 这个代码来完成,在array.c中的get_root_array是用来将代码转换为函式的位置, 它的程式为

    static int get_root_array(char * page, int type, char **start, off_t offset, int length)
    {
    switch (type) {
    case PROC_LOADAVG:
    return get_loadavg(page);

    case PROC_UPTIME:
    return get_uptime(page);

    case PROC_MEMINFO:
    return get_meminfo(page);

    #ifdef CONFIG_PCI
    case PROC_PCI:
    return get_pci_list(page);
    #endif

    case PROC_CPUINFO:
    return get_cpuinfo(page);

    case PROC_VERSION:
    return get_version(page);

    #ifdef CONFIG_DEBUG_MALLOC
    case PROC_MALLOC:
    return get_malloc(page);
    #endif

    #ifdef CONFIG_MODULES
    case PROC_MODULES:
    return get_module_list(page);

    case PROC_KSYMS:
    return get_ksyms_list(page, start, offset, length);
    #endif

    case PROC_STAT:
    return get_kstat(page);

    case PROC_DEVICES:
    return get_device_list(page);

    case PROC_INTERRUPTS:
    return get_irq_list(page);

    case PROC_FILESYSTEMS:
    return get_filesystem_list(page);

    case PROC_DMA:
    return get_dma_list(page);

    case PROC_IOPORTS:
    return get_ioport_list(page);
    #ifdef CONFIG_BLK_DEV_MD
    case PROC_MD:
    return get_md_status(page);
    #endif
    #ifdef __SMP_PROF__
    case PROC_SMP_PROF:
    return get_smp_prof_list(page);
    #endif
    case PROC_CMDLINE:
    return get_cmdline(page);

    case PROC_MTAB:
    return get_filesystem_info( page );
    #ifdef CONFIG_RTC
    case PROC_RTC:
    return get_rtc_status(page);
    #endif
    #ifdef CONFIG_TESTPROC
    case PROC_TEST:
    return get_test_status(page);
    #endif
    case PROC_LOCKS:
    return get_locks_status(page);
    }
    return -EBADF;
    }

    这段程式我们不需要解释了吧! 如果各位有兴趣看看核心是如果由档案系统 连结到这个函数的过程,请看下一节的说明。接下来我们就看一下,我们所 加入的get_test_status这个函数的内容。通常这个函数应该位於驱动程式或 核心之中,所以我把它放在drivers/char之中,这个位置只是一个任意的选 择,没有其它的意义,你可以因自已的需要做不同的选择。

    我将档案的名称取做testproc.c,内容是

    #include

    int get_test_status(char *buf)
    {
    char *p;

    p = buf;

    p += sprintf(p,\"test status\\n\");
    return p - buf;
    }

    这个程式好像....好像....太简单了一点吧,不过如果你去找一下其它的函数,结 构几乎是一样的,我们只要将字串或任何资料填入buf之中,并且将实际的长度传 回即可。因为buf是一个传入的缓冲区,那一定有长度的限制吧! 当然有,它的长 度是一个记忆体页的长度,在X86中就是4096个位元组。超过了可能会毁了核心, 一定要小心。

    至此整个工作已经完成,但如果你此时就去做编译核心的工作,你会得到一个\get_test_status undefined\的错误,这是因为我们并没有告诉核心多了testproc 这个档案。我们必须修改在drivers/char中的Config.in和Makefile二个档案,在Config.in中加入

    bool \Test proc\ CONFIG_TESTPROC

    这会让我们在做make menuconfig或make config时会多出一个选项,让我们可以设定是否要加入这个功能。还记得前面的码吗? 所有的码都被放在

    #ifdef CONFIG_TESTPROC

    #endif

    之间。但testproc.c这个档案如何加入呢? 这个要靠在Makefile中加入

    ifeq ($(CONFIG_TESTPROC),y)
    L_OBJS += testproc.o
    endif

    你可以看到只有在CONFIG_TESTPROC被选择的情况下testproc.c才会被编译且 连结至核心之中。至此你便可以重新建立一个核心了,记得在编译前先执行 make menuconfig并将CONFIG_TESTPROC的选项打开。

    发布人:netbull 来自:中国软件