sysctl详解

简介

sysctl可以直接查询内核参数,获取重要的运行时诊断信息,在某种情况下,修改参数的值可以改变内核行为,但是在内核大量的参数中,只有少量的参数是可以通过sysctl修改的。

为什么sysctl可以修改内核参数呢?

sysctl命令对sysctl函数调用的封装,在用户空间调用sysctl()函数时,会触发一个软中断(trap),将控制权交给操作系统内核。

在内核中,有一些与sysctl()相关的处理函数,它们负责解析传递的参数,执行的相应的操作,并将结果返回给用户程序。

在C语言中,sysctl()函数的原型如下:

perl 复制代码
/// 用于查询和修改系统参数
/// - Parameters:
///   - name: 一个整型数组,指定了要查询或修改的系统参数
///   - namelen: 指定了参数数组 name 的长度
///   - oldp: 用于存储查询到的参数值的缓冲区
///   - oldlenp: 指向一个整型变量的指针,用于传递 oldp 缓冲区的大小,并在调用结束后返回实际读取的参数值的大小
///   - newp: 用于传递新值的缓冲区,用于修改系统参数
///   - newlen: 指定了新值缓冲区 newp 的大小

如何在用户程序中使用sysctl功能呢

要想使用sysctl函数,首先就得了解name参数,它是一个整形数组,它通常有一系列的整数值组成,每个整数值代表一个特定的系统参数或者系统信息,这些整数值按照特定的规则构成一个层级结构,用于区分不同类型的参数和信息。

这些整数值一般被称为OID(Object Identifer),它们以特定的数字编码来标识系统中的不同信息。在BSD系统中,有一个OID树(Object Identifier Tree),其中的每个节点代表一个特定的系统参数或信息,通过沿着树的路径来访问和修改系统参数。

在 BSD 系统中,OID 一般由一系列数字组成,比如 1.3.6.1.2.1 这样的形式。这些数字代表着不同层级的节点,每个节点都有特定的含义和对应的系统参数。不同的操作系统版本可能有不同的 OID 命名规则和层级结构。 最顶级的名称空间如下:

名称空间 用途
CTL_KERN 1 用于访问关于内核的参数和信息,比如系统信息、内核配置、进程和调度等
CTL_VM 2 用于访问虚拟内存子系统相关的参数和信息,比如内存使用、页表信息等。
CTL_VFS 3 用于访问虚拟文件系统(VFS)的参数和信息,比如文件系统类型、挂载点等。
CTL_NET 4 用于访问网络相关的参数和信息,比如网络接口、路由表等。
CTL_DEBUG 5 用于访问调试相关的参数和信息,比如内核调试选项、调试日志等。
CTL_HW 6 用于访问硬件相关的参数和信息,比如系统硬件信息、CPU信息等。
CTL_MACHDEP 7 用于访问机器相关的参数和信息,根据不同的硬件平台可能有不同的子项。
CTL_USER 8 用于用户定义的参数和信息
CTL_MAXID 9 标识 sysctl 类型的数量,用于循环遍历所有的类型。

二级名称查看sys/sysctl.h文件里面的定义吧,不想写了!!举个代码例子吧

ini 复制代码
static struct kinfo_proc* get_proc_list(size_t* proc_count) {
  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
  size_t size;
  struct kinfo_proc* proc_list;
  int st;

  st = sysctl(mib, 4, NULL, &size, NULL, 0);
  if (st == -1) {
    return NULL;
  }

  proc_list = (struct kinfo_proc*)malloc(size);
  st = sysctl(mib, 4, proc_list, &size, NULL, 0);
  if (st == -1) {
    free(proc_list);
    return NULL;
  }

  *proc_count = size / sizeof(struct kinfo_proc);
  return proc_list;
}

如何在内核中使用sysctl功能呢

在内核中sysctl是通过sysctl_oid数据维护的,这个数据结构定义在Kernel.framework中的<sys/sysctl.h>中

代码如下:

c 复制代码
struct sysctl_oid {
	struct sysctl_oid_list * OS_PTRAUTH_SIGNED_PTR("sysctl_oid.oid_parent") oid_parent;
	SLIST_ENTRY(sysctl_oid) oid_link;
	int             oid_number;
	int             oid_kind;
	void            *oid_arg1;
	int             oid_arg2;
	const char      *oid_name;
	int             (*oid_handler)SYSCTL_HANDLER_ARGS;
	const char      *oid_fmt;
	const char      *oid_descr; /* offsetof() field / long description */
	int             oid_version;
	int             oid_refcnt;
};

的sysctl_oid可以通过宏定义SYSCTL_PROC创建,这个宏定义了新的sysctl_oid、初始化其字段、并且进行链接器相关的设置。还有了一些宏定义定义在这个宏之上,方便了一些操作如下表:

sysctl宏 用途
SYSCTL_DECL 声明一个顶级名称空间,跟CTL_KERN类似
SYSCTL_OID 用于定义 sysctl 树结构中的节点,指定节点的名称、类型、访问权限等信息,原始OID。很少直接使用。根据这个表中的 SYSCTL_* 常量,可以将类型指定为 "N""A"、"T"、"TU"、"L"或"Q"
SYSCTL_NODE 用于创建 sysctl 节点,允许在 sysctl 树中添加新的节点。
SYSCTL_STRING 用于创建一个表示字符串的 sysctl 变量,调用sysctl_handle_string()处理
SYSCTL_COMPAT_INT SYSCTL_INT 用于创建一个兼容模式下的整数类型的 sysctl 参数。这个宏允许在不同系统版本之间保持参数的兼容性,并提供了一种方法来处理参数值的变化或不同实现之间的差异
SYSCTL_COMPAT_UINT SYSCTL_UINT 用于创建一个兼容模式下的无符号整数类型的 sysctl 参数。这个宏允许在不同系统版本之间保持参数的兼容性,并提供了一种方法来处理参数值的变化或不同实现之间的差异
SYSCTL_LONG 用于创建一个 long 类型的 sysctl 参数,允许读取和写入长整型数据。使用sysctl_handle_long()处理
SYSCTL_QUAD 用于创建一个 quad 类型的 sysctl 参数,这是一个 64 位整数类型,调用sysctl_handle_quad()处理程序
SYSCTL_OPAQUE 创建一个不透明的(opaque)sysctl 参数,可以用于存储不定长度的数据块,这些数据块的内容不会被 sysctl 解释。调用sysctl_handle_opaque()处理程序
SYSCTL_STRUCT 创建一个结构体类型的 sysctl 参数,允许定义一个复杂的数据结构,可以包含多个字段和信息,调用sysctl_handle_opaque()处理程序
SYSCTL_PROC 创建一个处理器(procedure)类型的 sysctl 参数,允许注册一个函数,用于处理特定的 sysctl 请求,调用者指定自定义的处理程序

SYSCTL_PROC宏的作用是声明叶子节点的处理程序,也就是用户发出sysctl请求时,内核调用的回调函数,定义自己的处理程序是一件非常简单的事,只需要以下的两个步骤:

1.定义调用处理程序的SYSCTL_NODE节点

scss 复制代码
//parant:_kern、_debug或自己通过SYSCTL_DECL(myname)定义的顶层名称,
//nbr:OID_AUTP,请求内核进行oid分配
//name:自己提供的名称
//access:访问标识位:CTLFLAG *,按位OR
//handler,0 处理程序
//descr:描述文本
SYSCTL_NODE(parent, nbr, name, access, handler, descr) 

2.定义处理程序要实现的实际sysctl叶子节点,这里有两种方法

  • 使用表 14-5 中定义的一种类型。这样会安插一个默认的处理程序,开发者只需要指定保存sysctl 数据的变量即可。然而,使用这种方法的缺点在于:当变量值被读取或发生变化时收不到回调通知。这些固定类型的宏都差不多。例如,如果想要一个整型变量,可以使用以下代码:
scss 复制代码
//parent:步骤(1)中创建或使用的节点
//nbr:OID_AUTO:不需要考虑编号,内核自动分配
//name:叶子节点的名称
//access:CTL_ *标志位,_RW、_ANYEODY
//ptr:保存这个数据的地址
//val:如果ptr为NULL则需要这个值,如果使用了这个值,则叶子节点为只读
//descr:描述性文本
SYSCTL_INT(parent, nbr, name, access, ptr, val, descr);
  • b 将叶子节点定义为SYSCTL_PROC,指定自定义处理函数,如下
scss 复制代码
//parent:步骤(1)中创建或使用的节点
//nbr:OID_AUTO:不需要考虑编号,内核自动分配
//name:叶子节点的名称
//access:CTL_*标志位:_RW、_AWXBODY 等
//ptr:指向变量数据的指针 
//arg:处理程序的参数
//handler:指向自定义处理程序的指针
//fmt:"A"、"工"和"IU"等格式描述符
//descr:描述性文本
SYSCTL_PROC(parent, nbr, name, access, ptr, arg, handler, fmt, descr)

后一种方法的好处在于当用户针对 sysctl 进行任何操作时能够获得通知。这一点有点像 Linux,在Linux 中,pproc 和/sys 文件系统处理程序能监听这些导出数据的访问和变化情况,当这些数据被访问时可以执行一些操作。

最后调用sysctl_register_oid函数注册自定义的变量

举个例子如下:

objectivec 复制代码
int  sysctl_arg = 0;
//实现处理函数
static int sysctl_hideproc SYSCTL_HANDLER_ARGS {

}
//注册处理函数
SYSCTL_PROC(_hw, OID_AUTO, hideprocess,CTLTYPE_INT|CTLFLAG_ANYBODY|CTLFLAG_WR,  
                    &sysctl_arg, 0, &sysctl_hideproc , "I", "Hide a process");
//注册了新的sysctl变量,hw.hideprocess
sysctl_register_oid(&sysctl__hw_hideprocess);  
相关推荐
SoraLuna8 小时前
「Mac玩转仓颉内测版10」PTA刷题篇1 - L1-001 Hello World
算法·macos·cangjie
_可乐无糖14 小时前
mac终端使用pytest执行iOS UI自动化测试方法
macos·pytest
后端常规开发人员16 小时前
在 Mac 上使用 Docker 安装宝塔并部署 LNMP 环境
macos·docker·容器·宝塔
nukix19 小时前
Mac Java 使用 tesseract 进行 ORC 识别
java·开发语言·macos·orc
SoraLuna20 小时前
「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)
算法·macos·动态规划·cangjie
Algorithm157621 小时前
mac上使用docker搭建gitlab
macos·docker·gitlab
余清歌21 小时前
macOS解决U盘装完系统容量变小的问题
macos
SoraLuna21 小时前
「Mac玩转仓颉内测版8」入门篇8 - Cangjie函数与方法
算法·macos·cangjie
zhlx283521 小时前
【免越狱】iOS砸壳 可下载AppStore任意版本 旧版本IPA下载
macos·ios·cocoa
Mac分享吧1 天前
【iStat Menus for MacBook状态栏菜单系统监控工具--安装教程【简单操作,随时了解电脑情况】
macos·mac·istat menus·软件分享·系统数据监控