整体概述
由于自己水平和精力有限,本篇博文只涉及如何使用的问题,不涉及讲解原理,如果有朋友对如何创建命令的原理感兴趣,可以自己进行探讨。
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 <sys/types.h>
#include <sys/sysctl.h>
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<sys/param.h>
#include<sys/module.h>
#include<sys/kernel.h>
#include<sys/systm.h>
#include<sys/sysctl.h>
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);
poid = SYSCTL_ADD_NODE(&clist, SYSCTL_STATIC_CHILDREN(), OID_AUTO,
"top", CTLFLAG_RW, 0, "tree top");
if (poid == NULL) {
printf("SYSCTL_ADD_NODE failed\n");
return EINVAL;
}
SYSCTL_ADD_LONG(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,
"long", CTLFLAG_RW, &j, 0, "creat long leaf");
SYSCTL_ADD_INT(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,
"int", CTLFLAG_RW, &i, 0, "creat int leaf");
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;
}
SYSCTL_ADD_PROC(&clist, SYSCTL_CHILDREN(poid), OID_AUTO,
"proc", CTLFLAG_RD, 0, 0, sysctl_procedure, "A", "creat proc leaf");
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;
}
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 <bsd.kmod.mk>
在上面的例程当中注意看注释,这里有一点比较疑惑的地方就是根据参考链接的说明在使用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<sys/types.h>
#include<sys/sysctl.h>
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 <sys/sysctl.h>
#include <sys/types.h>
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;
int size = echo_message->buffer_size;
error = sysctl_handle_int(oidp, &size, 0, req);
if (error || !req->newptr || echo_message->buffer_size == size)
return (error);
echo_message->buffer_size = size;
return (error);
}
稍微总结一下,一般在工作当中最佳sysctl节点的步骤,先调用sysctl_ctx_init进行初始化,然后调用ADD系列的宏进行命令的追加,当然如果要进入某个函数当中,需要使用PORC宏, 最后就是调用sysctl_ctx_free函数进行释放了,这个是对简单的整数值的设置。如果有对结构体进行设置,使用相应的宏应该就可以了。
参考文章: 1:添加链接描述 2:添加链接描述 3:添加链接描述
devlink概述
这个命令的设置比较简单,参考下系统当中的其它源代码应该没啥问题。具体的命令含义以及使用方法可以参考: 添加链接描述
|