使用sysctl

作者: 网路元素 | 来源:发表于2019-10-04 20:54 被阅读0次

    前面说到的内核模块参数是在加载模块时使用,而这次我们要说一种在模块加载后修改数据的方法,其是基于后期要说到的procfs而设计的,它是应用程序设置和获取运行时内核配置参数的一种方法,一般相应的参数对应/proc/sys目录,除了使用系统内置的sysctl工具操作,也可直接对/proc/sys目录下的结点直接操作。 

    对于sysctl会使用到如下函数来注册和释放: 

    1.注册sysctl

    struct ctl_table_header *register_sysctl_table(struct ctl_table *table) ;

    2.释放sysctl

    void unregister_sysctl_table(struct ctl_table_header * header) ;

    这两个函数在fs/proc/proc_sysctl.c中实现,在include/linux/sysctl.h中声明,从两个函数可以看到两者是通过ctl_table_header结构体连通的,该结构体不详说,接下来看看ctl_table这个结构体,在include/linux/sysctl.h有如下内容:

    struct ctl_table
    {
            const char *procname; /* Text ID for /proc/sys, or zero */
            void *data;
            int maxlen;
            umode_t mode;
            struct ctl_table *child; /* Deprecated */
            proc_handler *proc_handler; /* Callback for text formatting */
            struct ctl_table_poll *poll;
            void *extra1;
            void *extra2;
    };

    其中,procname对应/proc/sys目录下显示的名称,data对应该节点在内核中的变量,maxlen为条目的最大长度(即当对应变量为字符串时,该字条串的长度,超过部分截掉),mode为在/proc/sys对应节点的访问权限(与《模块参数》里说到的八进制数一致,当该ctl_table的child不空时,该值为0555),child不为空则表示/proc/sys/$procname为目录,否则为文件节点(树状叶子),而proc_handler为该文件节点对应的处理回调函数指针,extra1和extra2是proc_handler的参数(在下面含有minmax的函数名对应的函数里会使用到),相应的函数可支持为proc_dostring()、 proc_dointvec()、proc_dointvec_jiffies()、proc_dointvec_userhz_jiffies()、proc_dointvec_minmax()、proc_doulongvec_ms_jiffies_minmax()、proc_doulongvec_minmax() ,这些在kernel/sysctl.c里定义,当然也可重构相应的处理函数,在等会的例子里可看到,poll暂时不使用,后期详细说明sysctl时再讨论,注意最后一个ctl_table数组成员要为空。

    下面还是实例感受吧:

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/sysctl.h>

    #define MAX_SLAM_STRING_SIZE 256

    static int slam_int;
    static char slam_string[MAX_SLAM_STRING_SIZE];

    static int slam_int_callback(ctl_table *table, int write,
            void __user *buffer, size_t *lenp, loff_t *ppos)
    {
            int rc;
            int *data = table->data;

            printk("Original data = %d\n", *data);

            rc = proc_dointvec(table, write, buffer, lenp, ppos);
            if (write)
                    printk("This is write operation,now data = %d\n", *data);

            return rc;
    }

    static struct ctl_table slam_child[] = {
            {
                    .procname = "slam_int",
                    .data = &slam_int,
                    .maxlen = sizeof(int),
                    .mode = 0666,
                    .proc_handler = slam_int_callback,
            },
            {
                    .procname = "slam_string",
                    .data = slam_string,
                    .maxlen = MAX_SLAM_STRING_SIZE,
                    .mode = 0666,
                    .proc_handler = &proc_dostring,
            },
            {},
    };

    static struct ctl_table sysctl_ctl_table[] = {
            {
                    .procname = "slam",
                    .mode = 0555,
                    .child = slam_child,
            },
            {},
    };

    static struct ctl_table_header * sysctl_header;

    static __init int sysctl_example_init(void)
    {
            sysctl_header = register_sysctl_table(sysctl_ctl_table);
            if (sysctl_header == NULL)
            {
                    printk("register_sysctl_table failed!\n");
                    return -1;
            }

            printk("sysctl_example_init success!\n");
            return 0;
    }

    static __exit void sysctl_example_exit(void)
    {
            unregister_sysctl_table(sysctl_header);
            printk("%s success!\n",__func__);
    }

    module_init(sysctl_example_init);
    module_exit(sysctl_example_exit);

    相应的Makefile内容如下:

    obj-m += sysctl_example.o

    CUR_PATH:=$(shell pwd)
    LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

    all:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

    clean:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean

    最终的源码文件目录树如下: 

    /home/xinu/xinu/linux_kernel_driver_l1/sysctl_example/
    ├── Makefile
    └── sysctl_example.c 

    关于源码里sysctl_example_init函数注册sysctl时用到的sysctl_ctl_table,如果其不是一个数组,而是一个struct ctl_table结构体变量,则注册参数要改为&sysctl_ctl_table,那么相应的变量里就不存在table数组最后一个量为空的时候,那么等会作为child的table里的结点都会作为相应/proc/sys/slam目录下的子结点外,还会作为/proc/sys目录的子结点,不过源码里已改为正常的情况了,你也可以尝试下。 在加载驱动后,由于在代码里相应的变量均未赋值,故而会得到如下值: 

    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ cat /proc/sys/slam/slam_int
    0
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ cat /proc/sys/slam/slam_string

    即slam_int初始化为0,而slam_string初始化为空。那么接下来设置并查看设置后其值: 

    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ echo 1 > /proc/sys/slam/slam_int xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ cat /proc/sys/slam/slam_int
    1
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ echo “hello” > /proc/sys/slam/slam_string xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ cat /proc/sys/slam/slam_string
    hello 

    相应的dmesg如下: 

    [22406.958394] sysctl_example_init success!
    [22418.675928] Original data = 0
    [22418.675961] Original data = 0
    [22576.226091] Original data = 0
    [22576.226115] This is write operation,now data = 1
    [22580.876721] Original data = 1
    [22580.876751] Original data = 1 

    看来每次读都会关联到所有子结点?留个问题后期细说。从上面的操作并未发挥sysctl的魅力,与平常的procfs操作没区别,接下来我们使用sysctl的规则来读写这些变量: 

    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ sysctl slam.slam_int
    slam.slam_int = 1
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ sysctl slam.slam_string
    slam.slam_string = hello
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ sysctl -w slam.slam_int=4
    slam.slam_int = 4
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ sysctl slam.slam_int
    slam.slam_int = 4
    xinu@slam:~/xinu/linux_kernel_driver_l1/sysctl_example$ cat /proc/sys/slam/slam_int

    看出其规则来了吧!就是去掉/proc/sys,然后每一级当一个量,“/”更换为“.”,其中读与写区别就在-w参数了,但这样操作仅仅是临时的,那我们可以修改/etc/sysctl.conf文件,将要设定为每次启动系统后的新值添加到该文件中,有如下例: 

    slam.slam_int=4 

    但这句话生效的前提是这个KO在先加载或者其编译到内核里了,这个就不再说了,自己好好研究下吧!

    好了,就先说到这了,有空使用sysctl -a看看系统里可支持的内核运行时变量有哪些吧! 

    参考网址: 

    http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/17-2547.html
    http://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/index.html
    http://m.blog.csdn.net/blog/iamonlyme/11180131
    http://www.ug.it.usyd.edu.au/~vnik5287/sysctl.pdf

    相关文章

      网友评论

        本文标题:使用sysctl

        本文链接:https://www.haomeiwen.com/subject/geilyctx.html