整体概述

由于自己水平和精力有限,本篇博文只涉及如何使用的问题,不涉及讲解原理,如果有朋友对如何创建命令的原理感兴趣,可以自己进行探讨。

sysctl概述

在FreeBSD系统上,设备通信和控制主要通过sysctl和ioctl接口,也就是在用户层(shell)配置的方式,进行对底层驱动参数的一个设置。 具体使用方式例如: sysctl -A : 通过此命令查看系统当中都有那些可以配置的参数。 比如输入sysctl -A命令后有以下输出, a.b.c : 1 a.b.c是一种二进制的表示方式,1代表某个参数的值,当然也可以是字符类型或者结构体等。具体的后面遇到代码再讲解。 随后,如果我们输入sysctl a.b.c命令是查看该参数的值, 而sysctl a.b.c=2是将此参数的值进行修改,如果你看到有 0 -> 2的输出就代表值从0修改为了2,已经成功修改了。

sysctl的创建

freebsd提供了以下的宏供我们创建sysctl

#include

#include

struct sysctl_oid *

SYSCTL_ADD_OID(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int kind, void *arg1, int arg2, int (*handler) (SYSCTL_HANDLER_ARGS),const char *format, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_NODE(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, int (*handler) (SYSCTL_HANDLER_ARGS), const char *descr);

struct sysctl_oid *

SYSCTL_ADD_STRING(struct sysctl_ctx_list *ctx,struct sysctl_oid_list *parent, int number, const char *name,int access, char *arg, int len, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_INT(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, int *arg, int len, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_UINT(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, unsigned int *arg, int len, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_LONG(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, long *arg, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_ULONG(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, unsigned long *arg, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_OPAQUE(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, void *arg, int len, const char *format,const char *descr);

struct sysctl_oid *

SYSCTL_ADD_STRUCT(struct sysctl_ctx_list *ctx,

struct sysctl_oid_list *parent, int number, const char *name,int access, void *arg, STRUCT_NAME, const char *descr);

struct sysctl_oid *

SYSCTL_ADD_PROC(struct sysctl_ctx_list *ctx,struct sysctl_oid_list *parent, int number, const char *name,int access, void *arg, int len,int (*handler) (SYSCTL_HANDLER_ARGS), const char *format,

const char *descr);

SYSCTL_ADD_OID 宏将创建一个能处理任意数据类型的 sysctl。如果调用成功,宏 将返回一个指向该 sysctl 的指针,否则返回 NULL。 其他 SYSCTL_ADD_* 宏都是 SYSCTL_ADD_OID 的变种,用于创建能处理特殊数据类型变量的 sysctl。这些宏的解释如下:

SYSCTL_ADD_NODE 创建一个新结点(或类别),该类别可以有子类

SYSCTL_ADD_STRING创建一个处理空结尾(null-terminated)字符串的新sysctl

SYSCTL_ADD_INT创建一个处理整型变量的新sysctl

SYSCTL_ADD_UINT创建一个处理无符号整型变量的新sysctl

SYSCTL_ADD_LONG创建一个处理长整型变量的新 sysctl

SYSCTL_ADD_ULONG创建一个处理无符号长整型变量的新 sysctl

SYSCTL_ADD_OPAQUE创建一个处理一块数据的新sysctl,所处理的数据类型可调整,大小通过参数len指定

SYSCTL_ADD_STRUCT创建一个处理结构体的新sysctl

SYSCTL_ADD_PROC创建一个能使用函数来处理其读写请求的新 sysctl,该“处理函数”通常 用于在导入或导出前处理相应数据

参数的意义如下:

sysctl例程感性认识

我们通过一个例程对怎么创建sysctl配置命令的有一个感性认识。如下 my_sysctl.c

#include

#include

#include

#include

#include

static struct sysctl_ctx_list clist;

static struct sysctl_oid *poid;

static int i = 10;

static long j = 20;

static const char *c = "hello init";

static int

sysctl_procedure(SYSCTL_HANDLER_ARGS)

{

const char *buf = "sysctl_procedure call";

return sysctl_handle_string(oidp, buf, strlen(buf), req);

}

static int

sysctl_modevent(module_t mod_unused, int event, void *arg __unused)

{

int error = 0;

switch (event) {

case MOD_LOAD:

sysctl_ctx_init(&clist);

/*SYSCTL_STATIC_CHILDREN里面的参数为空,那么top为顶层节点*/

poid = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(/*top name*/), OID_AUTO,

"top", CTLFLAG_RW, 0, "tree top");

if (poid == NULL) {

printf("SYSCTL_ADD_NODE failed\n");

return EINVAL;

}

/*这里注意第二个参数,poid是上一个函数(SYSCTL_ADD_NODE)的返回值,所以有top.long -> 20*/

SYSCTL_ADD_LONG(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,

"long", CTLFLAG_RW, &j, 0, "creat long leaf");

/*top.int -> 10*/

SYSCTL_ADD_INT(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,

"int", CTLFLAG_RW, &i, 0, "creat int leaf");

/*这里也创建了一个节点,使用的是top为父节点,所以有top.node*/

poid = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(poid), OID_AUTO,

"node", CTLFLAG_RW, 0, "tree node");

if (poid == NULL) {

printf("SYSCTL_ADD_NODE failed\n");

return EINVAL;

}

/*top.node.proc -> sysctl_procedure call*/

SYSCTL_ADD_PROC(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,

"proc", CTLFLAG_RD, 0, 0, sysctl_procedure, "A", "creat proc leaf");

/*这里创建了一个新的节点,所以有_debug.top, 这里没有显示指定poid, 可以设定连接到哪一个父节点上去*/

poid = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO,

"top", CTLFLAG_RW, 0, "tree node");

if(poid == NULL) {

printf("SYSCTL_ADD_NODE failed\n");

return EINVAL;

}

/*_debug.top.string -> c*/

SYSCTL_ADD_STRING(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,

"string", CTLFLAG_RD, c, 0, "new string leaf");

printf("module loaded\n");

break;

case MOD_UNLOAD:

if (sysctl_ctx_free(&clist)) {

printf("sysctl_ctx_free failed.\n");

return -1;

}

printf("module unloaded\n");

break;

default:

error = -1;

break;

}

return error;

}

static moduledata_t sysctl_mod = {

"sysctl_modevent",

sysctl_modevent,

NULL

};

DECLARE_MODULE(sysctl_modevent, sysctl_mod, SI_SUB_EXEC, SI_ORDER_ANY);

makefile:

SRCS=my_sysctl.c

KMOD=my_sysctl

.include

在上面的例程当中注意看注释,这里有一点比较疑惑的地方就是根据参考链接的说明在使用SYSCTL_STATIC_CHILDREN时如果传递的参数为空,我是编译错误的,创建不了顶层节点,这里先不管它了,实际工作当中我们应该不会创建顶层节点,而是在顶层节点的基础上进行的追加。

假设上面的代码如果不会编译错误的话,那么在kldload my_sysctl.ko以后,使用sysctl -A命令时,应该会有如下字样的出现

top.long : 10

top.int : 20

top.node.proc:sysctl_procedure call

debug.top.string : hello init

注意上面的初始值。

动态创建sysctl宏总结

SYSCTL_STATIC_CHILDREN 宏

原型如下:

#include

#include

struct sysctl_oid_list *

SYSCTL_STATIC_CHILDREN(struct sysctl_oid_list OID_NAME);

我们在追加节点时,可以通过使用SYSCTL_STATIC_CHILDREN或者SYSCTL_CHILDREN,进行对父节点的连接,在使用SYSCTL_STATIC_CHILDREN时,传递的参数是父节点的名称,比如现在系统有一个dev名称的父节点,我们可以传递_dev,这里必须要待下划线,如果要创建一个顶层的节点,那么传递的参数要为空(这点我没想明白,编译出错了)。

SYSCTL_CHILDREN宏

原型

#include

#include

struct sysctl_oid_list *

SYSCTL_CHILDREN(struct sysctl_oid *oidp);

一般在使用SYSCTL_ADD_系列的宏追加节点时,传递的都是SYSCTL_CHILDREN。

sysctl接口的简单使用

例如我们想在系统当中增加一个对内存缓冲区控制的参数。

static int

sysctl_set_buffer_size(SYSCTL_HANDLER_ARGS)

{

int error = 0;

/*假设echo_message->buffer_size是我们要设置的参数*/

int size = echo_message->buffer_size;

/*这里size是命令行设置下来的参数*/

error = sysctl_handle_int(oidp, &size, 0, req);

if (error || !req->newptr || echo_message->buffer_size == size)

return (error);

//.....省略其它处理步骤

//最后会有这个赋值当中,系统当中都是这样做的,如果我们设置成功了,那么会有提示,比如0 -> 5表示我们把值从0修改为了5.

echo_message->buffer_size = size;

//....

return (error);

}

稍微总结一下,一般在工作当中最佳sysctl节点的步骤,先调用sysctl_ctx_init进行初始化,然后调用ADD系列的宏进行命令的追加,当然如果要进入某个函数当中,需要使用PORC宏, 最后就是调用sysctl_ctx_free函数进行释放了,这个是对简单的整数值的设置。如果有对结构体进行设置,使用相应的宏应该就可以了。

参考文章: 1:添加链接描述 2:添加链接描述 3:添加链接描述

devlink概述

这个命令的设置比较简单,参考下系统当中的其它源代码应该没啥问题。具体的命令含义以及使用方法可以参考: 添加链接描述

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。