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);  
相关推荐
AD钙奶-lalala7 小时前
在 macOS 上搭建 Flutter 开发环境
flutter·macos
~二向箔~12 小时前
Mac,苹果电脑移动硬盘不显示
macos
vastgrassland14 小时前
对WWDC 2025 Keynote 内容的预测
macos·ios·wwdc
fukai772214 小时前
WWDC 2025 macOS 26有哪些更新点
macos·ios·wwdc
ReadThroughLife19 小时前
【已解决】MACOS M4 芯片使用 Docker Desktop 工具安装 MICROSOFT SQL SERVER
microsoft·macos·docker·容器
獨枭1 天前
配置 macOS 上的 Ruby 开发环境
开发语言·macos·ruby
库奇噜啦呼1 天前
push [特殊字符] present
macos·ios·cocoa
安和昂2 天前
【iOS】多线程NSOperation,NSOperationQueue
macos·ios·cocoa
咕噜签名分发冰淇淋2 天前
Flutter 打包 iOS 苹果 IPA 应用有哪些优势?如何实现?
macos·objective-c·cocoa
st紫月2 天前
用虚拟机安装macos系统之后进入Boot Manager页面
macos