我们都知道linux下用户态与内核态通信的方法有

  • procfs(/proc)
  • sysctl(/proc/sys)
  • sysfs(/sys)
  • netlink 套接口
  • 内核启动参数
  • 模块参数

而这些方法是如何实现,又是怎样的一个原理,接下来就让我们来了解下。

一、什么是systcl

sysctl是一个用于运行时配置内核参数的命令,这些内核参数位于/proc/sys目录下。sysctl配置与显示在/proc/sys目录中的内核参数.可以用sysctl来设置或重新设置联网等功能,如IP转发、IP碎片去除以及源路由检查等。用户只需要编辑/etc/sysctl.conf文件,即可手工或自动执行由sysctl控制的功能。

1、常见用法

首先sysctl命令是系统开源软件包提供的,我们目前主要发行版的的linux系统都默认预装了procps包,提供了sysctl命令供用户配置sysctl实现的内核参数。

tangm@tangm-PC:~$ which sysctl
/sbin/sysctltangm@tangm-PC:~$ apt-file search /sbin/sysctl
procps: /sbin/sysctl

这个命令我们常用的方法用

1)列出所有的变量并查看

sysctl -a | less

2)修改某变量的值

sysctl -w 变量名=变量值
#sysctl -w vm.max_map_count=262144

3)读一个指定的变量,例如 kernel.msgmnb:

[tangm@tangm] $ sysctl kernel.msgmnb
kern.maxproc: 65536

要设置一个指定的变量,直接用 variable=value 这样的语法:

[tangm@tangm]$ sudo sysctl kernel.msgmnb=1024
kernel.msgmnb: 1024

可以使用sysctl修改系统变量,也可以通过编辑sysctl.conf文件来修改系统变量。

sysctl 变量的设置通常是字符串、数字或者布尔型。 (布尔型用 1 来表示’yes’,用 0 来表示’no’)。

sysctl -w kernel.sysrq=0
sysctl -w kernel.core_uses_pid=1
sysctl -w net.ipv4.conf.default.accept_redirects=0
sysctl -w net.ipv4.conf.default.accept_source_route=0
sysctl -w net.ipv4.conf.default.rp_filter=1
sysctl -w net.ipv4.tcp_syncookies=1
sysctl -w net.ipv4.tcp_max_syn_backlog=2048
sysctl -w net.ipv4.tcp_fin_timeout=30
sysctl -w net.ipv4.tcp_synack_retries=2
sysctl -w net.ipv4.tcp_keepalive_time=3600
sysctl -w net.ipv4.tcp_window_scaling=1
sysctl -w net.ipv4.tcp_sack=1

除此之外,我们还可以通过文件的读写操作进行设置。

tangm@tangm-PC:~$ cat /proc/sys/kernel/sysrq
438tangm@tangm-PC:~$ cat /proc/sys/net/ipv4/tcp_sack
1

二、sysctl的实现

1、sysctl实现流程

框架

2、内核中sysctl的代码实现

首先,在内核源码树中,sysctl的主体实现代码存放在fs/proc/目录下,它的目录情况如下:

tangm@tangm-PC:~/work/community/update-kernel/linux-next$ ls fs/proc/
array.c       fd.h          loadavg.c      proc_tty.c     uptime.c
base.c        generic.c     Makefile       root.c         util.c
bootconfig.c  inode.c       meminfo.c      self.c         version.c
cmdline.c     internal.h    namespaces.c   softirqs.c     vmcore.c
consoles.c    interrupts.c  nommu.c        stat.c
cpuinfo.c     Kconfig       page.c         task_mmu.c
devices.c     kcore.c       proc_net.c     task_nommu.c
fd.c          kmsg.c        proc_sysctl.c  thread_self.c

sysctl实现的总入口在我们的fs/proc/proc_sysctl.c文件中,首先我们来了解一下sysctl实现中的主要数据结构和函数接口

tangm@tangm-PC:~$ cat include/linux/sysctl.h.../* A sysctl table is an array of struct ctl_table: */
/* ctl_table结构体,是sysctl中最重要最常用的一个结构体 */
struct ctl_table {/* sysctl接口名称,最终我们在系统文件系统中能够看到的名字 */const char *procname;           /* Text ID for /proc/sys, or zero */void *data;int maxlen;umode_t mode;/* 是否存在子ctl_table,不存在不需要复制,存在需要指向子ctl_table地址 *、struct ctl_table *child;        /* Deprecated *//* systcl处理接口,处理当接口的值被用户重新设置后的一些操作 */proc_handler *proc_handler;     /* Callback for text formatting */struct ctl_table_poll *poll;void *extra1;void *extra2;
} __randomize_layout;.../* struct ctl_path describes where in the hierarchy a table is added */
struct ctl_path {const char *procname;
};.../* sysctl注册接口的实际核心总入口 */
struct ctl_table_header *__register_sysctl_table(struct ctl_table_set *set,const char *path, struct ctl_table *table);
struct ctl_table_header *__register_sysctl_paths(struct ctl_table_set *set,const struct ctl_path *path, struct ctl_table *table);/* 提供给用户注册sysctl接口是调用的接口 *、
struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table);
struct ctl_table_header *register_sysctl_table(struct ctl_table * table);
struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,struct ctl_table *table);
/* 注销释放接口 */
void unregister_sysctl_table(struct ctl_table_header * table);extern int sysctl_init_bases(void);
extern void __register_sysctl_init(const char *path, struct ctl_table *table,const char *table_name);
#define register_sysctl_init(path, table) __register_sysctl_init(path, table, #table)
extern struct ctl_table_header *register_sysctl_mount_point(const char *path);...#endif /* _LINUX_SYSCTL_H */

它提供了一个register_sysctl()方法,用于让用户注册自己要注册实现sysctl接口。用户怎么注册我们在后续详细说明。用户调用register_sysctl()就可将自己想要实现的systcl接口注册完成。

/*** register_sysctl - register a sysctl table* @path: The path to the directory the sysctl table is in.* sysctl接口的路径,具体存放在系统的那个位置* @table: the table structure* 当前要注册的接口列表,即你需要注册那些接口** Register a sysctl table. @table should be a filled in ctl_table* array. A completely 0 filled entry terminates the table.** See __register_sysctl_table for more details.*/
struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table)
{return __register_sysctl_table(&sysctl_table_root.default_set,path, table);
}
EXPORT_SYMBOL(register_sysctl);

注册sysctl的核心处理接口,这里包括计算ctl_table长度并申请内存空间,检查systcl接口目录是否合法和存在,插入sysctl接口到sysctl的总表中。

struct ctl_table_header *__register_sysctl_table(struct ctl_table_set *set,const char *path, struct ctl_table *table)
{struct ctl_table_root *root = set->dir.header.root;struct ctl_table_header *header;struct ctl_dir *dir;struct ctl_table *entry;struct ctl_node *node;int nr_entries = 0;//遍历ctl_table表,计算ctl_table长度(当前ctl_table已空结尾,即'{ }'结尾)list_for_each_table_entry(entry, table)nr_entries++;//申请内存空间header = kzalloc(sizeof(struct ctl_table_header) +sizeof(struct ctl_node)*nr_entries, GFP_KERNEL_ACCOUNT);if (!header)return NULL;node = (struct ctl_node *)(header + 1);init_header(header, root, set, node, table);if (sysctl_check_table(path, table))goto fail;spin_lock(&sysctl_lock);dir = &set->dir;/* Reference moved down the directory tree get_subdir */dir->header.nreg++;spin_unlock(&sysctl_lock);//创建proc sysctl所在目录,所在目录为/proc/sys/下的目录,具体路径由用户指定dir = sysctl_mkdir_p(dir, path);if (IS_ERR(dir))goto fail;spin_lock(&sysctl_lock);if (insert_header(dir, header))goto fail_put_dir_locked;drop_sysctl_table(&dir->header);spin_unlock(&sysctl_lock);return header;fail_put_dir_locked:drop_sysctl_table(&dir->header);spin_unlock(&sysctl_lock);
fail:kfree(header);dump_stack();return NULL;
}

1

static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
{struct ctl_table *entry;int err;/* Is this a permanently empty directory? */if (is_empty_dir(&dir->header))return -EROFS;/* Am I creating a permanently empty directory? */if (header->ctl_table == sysctl_mount_point) {if (!RB_EMPTY_ROOT(&dir->root))return -EINVAL;set_empty_dir(dir);}dir->header.nreg++;header->parent = dir;err = insert_links(header);if (err)goto fail_links;list_for_each_table_entry(entry, header->ctl_table) {err = insert_entry(header, entry);if (err)goto fail;}return 0;
fail:erase_header(header);put_links(header);
fail_links:if (header->ctl_table == sysctl_mount_point)clear_empty_dir(dir);header->parent = NULL;drop_sysctl_table(&dir->header);return err;
}

一个ctl_table的初始化赋值,必须包含5个变量初始化,不能为空。这5个变量分别为:1)proc_handler; 2)data; 3)maxlen; 4)mode; 5)procname。

// 从下文代码可以看出,一个ctl_table的初始化赋值,必须包含4个变量初始化,不能为空
// 这四个变量分别为:1)proc_handler; 2)data; 3)maxlen; 4)mode.
static int sysctl_check_table(const char *path, struct ctl_table *table)
{struct ctl_table *entry;int err = 0;list_for_each_table_entry(entry, table) {if (entry->child)err |= sysctl_err(path, entry, "Not a file");// ctl_table中的proc_handler变量不能为空,为空会返回"No proc_handler"错误if ((entry->proc_handler == proc_dostring) ||(entry->proc_handler == proc_dobool) ||(entry->proc_handler == proc_dointvec) ||(entry->proc_handler == proc_douintvec) ||(entry->proc_handler == proc_douintvec_minmax) ||(entry->proc_handler == proc_dointvec_minmax) ||(entry->proc_handler == proc_dou8vec_minmax) ||(entry->proc_handler == proc_dointvec_jiffies) ||(entry->proc_handler == proc_dointvec_userhz_jiffies) ||(entry->proc_handler == proc_dointvec_ms_jiffies) ||(entry->proc_handler == proc_doulongvec_minmax) ||(entry->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {// proc_handler不为空的情况下,继续检查data和maxlen是否合法if (!entry->data)err |= sysctl_err(path, entry, "No data");if (!entry->maxlen)err |= sysctl_err(path, entry, "No maxlen");elseerr |= sysctl_check_table_array(path, entry);}if (!entry->proc_handler)err |= sysctl_err(path, entry, "No proc_handler");// 检查sysctl接口文件的权限if ((entry->mode & (S_IRUGO|S_IWUGO)) != entry->mode)err |= sysctl_err(path, entry, "bogus .mode 0%o",entry->mode);}return err;
}
static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table,struct ctl_table_root *link_root)
{struct ctl_table *link_table, *entry, *link;struct ctl_table_header *links;struct ctl_node *node;char *link_name;int nr_entries, name_bytes;name_bytes = 0;nr_entries = 0;list_for_each_table_entry(entry, table) {nr_entries++;name_bytes += strlen(entry->procname) + 1;}links = kzalloc(sizeof(struct ctl_table_header) +sizeof(struct ctl_node)*nr_entries +sizeof(struct ctl_table)*(nr_entries + 1) +name_bytes,GFP_KERNEL);if (!links)return NULL;node = (struct ctl_node *)(links + 1);link_table = (struct ctl_table *)(node + nr_entries);link_name = (char *)&link_table[nr_entries + 1];link = link_table;list_for_each_table_entry(entry, table) {int len = strlen(entry->procname) + 1;memcpy(link_name, entry->procname, len);link->procname = link_name;link->mode = S_IFLNK|S_IRWXUGO;link->data = link_root;link_name += len;link++;}init_header(links, dir->header.root, dir->header.set, node, link_table);links->nreg = nr_entries;return links;
}

查找当前的sysctl接口目录是否已经创建,如果未创建则创建

/* Find or the directory for the ctl_table. If one is not found create it. */
// 查找当前的sysctl接口目录是否已经创建,如果未创建则创建
static ctl_dir *dir sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
{const char *name, *nextname;for (name = path; name; name = nextname) {int namelen;nextname = strchr(name, '/');if (nextname) {namelen = nextname - name;nextname++;} else {namelen = strlen(name);}if (namelen == 0)continue;/** namelen ensures if name is "foo/bar/yay" only foo is* registered first. We traverse as if using mkdir -p and* return a ctl_dir for the last directory entry.*/dir = get_subdir(dir, name, namelen);if (IS_ERR(dir))break;}return dir;
}

三、实现一个自己的sysctl接口

1、已实现的sysctl接口代码

我们以reboot和poweroff为例,先看看已经实现的sysctl接口都做了些什么事情。以下代码是从kernel/reboot.c文件中截取的sysctl注册的核心数据和代码实现。

...static int C_A_D = 1;...#define POWEROFF_CMD_PATH_LEN  256
static char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";...//sysctl宏控制,开启宏的情况下才启用ctl_table,否则将进入一个while空循环,什么也不处理
#ifdef CONFIG_SYSCTL
static struct ctl_table kern_reboot_table[] = {//在第二章节中的提到,必须注册初始化5个变量值,下方两个接口都是最少赋值情况下注册的{// proc sysctl名称,对应/proc/sys/xxx/poweroff_cmd.procname       = "poweroff_cmd",// data数据内容,可以是具体指令,可以是bool值或其他一些类型的数据值.data           = &poweroff_cmd,// 数据长度.maxlen         = POWEROFF_CMD_PATH_LEN,// 文件权限.mode           = 0644,// 接口的处理类型.proc_handler   = proc_dostring,},{// proc sysctl名称,对应/proc/sys/xxx/ctrl-alt-del.procname       = "ctrl-alt-del",// data数据内容,可以是具体指令,可以是bool值或其他一些类型的数据值.data           = &C_A_D,// 数据长度.maxlen         = sizeof(int),// 文件权限.mode           = 0644,// 接口的处理类型.proc_handler   = proc_dointvec,},// 已空结束table{ }
};// 需要注册的sysctl接口的初始化接口
static void __init kernel_reboot_sysctls_init(void)
{// 调用proc_sysctl.c中实现的init接口,需要传入/proc/sys/下的相对路径// 本次调用传入了"kernel"和kern_reboot_table两个变量// 实际完成注册启动系统后,我们将看到/proc/sys/kernel/有以下两个文件// $ cat /proc/sys/kernel/poweroff_cmd // /sbin/poweroff// $ cat /proc/sys/kernel/ctrl-alt-del // 0register_sysctl_init("kernel", kern_reboot_table);
}
#else
// CONFIG_SYSCTL未开启时,不做任何处理,空循环
#define kernel_reboot_sysctls_init() do { } while (0)
#endif /* CONFIG_SYSCTL */...static int __init reboot_ksysfs_init(void)
{struct kobject *reboot_kobj;int ret;reboot_kobj = kobject_create_and_add("reboot", kernel_kobj);if (!reboot_kobj)return -ENOMEM;ret = sysfs_create_group(reboot_kobj, &reboot_attr_group);if (ret) {kobject_put(reboot_kobj);return ret;}// 在reboot实现初始化等流程完成后,调用sysctl初始化注册函数kernel_reboot_sysctls_init();return 0;
}
// 调用initcall,让系统在启动时去调用接口,启动reboot的初始化过程
// 完成reboot的初始化后会注册systcl,并完成reboot_sysctl的初始化
late_initcall(reboot_ksysfs_init);

CONFIG_SYSCTL宏是作为sysctl实现的内核宏控,如果未开启则未提供sysctl机制,所有注册的sysctl接口都需要考虑未开启宏的处理方法(可以为空循环)。在注册ctl_table时,至少需要初始化5个结构体变量,如下:

static struct ctl_table xxx_table[] = {
        {
                .procname                = "my_sysctl",
                .data                         = xxx,
                .maxlen                    = xxx,
                .mode                       =xxx,
                .proc_handler           = xxx,
        },

{ }

}

所有的sysctl注册初始化,都通过register_sysctl_init("sysdir1", xxx_table)实现,在最终完成注册并启动系统后,我们将看到/proc/sys/sysdir1/my_sysctl文件,后续当这个文件发生变更时,将调用你所注册的handler处理函数进行处理。

因此,我们可以通过sysctl来控制/proc/sys/下的文件,进而和内核进行通信,实时的对内核运行过程中的某些配置进行配置和修改。

如果有哪里需要详细介绍或者想要了解讨论的地方,欢迎留言~

Linux内核proc sysctl相关推荐

  1. linux 内核/proc

    /proc是一个伪文件系统,只在内存中,不占用硬盘空间.用户可以通过proc得到系统信息,更改内核参数. 由于系统信息(如进程)是动态改变的,所以proc文件系统是动态从系统内核读出所需要的信息. 文 ...

  2. linux配置ARP内核参数,详细讲解linux内核参数arp_announce和arp_ignore

    linux内核/proc/sys/net/ipv4/conf中下面会有各网卡的配置参数其中arp_ignore和arp_annouce与arp相关 arp_ignore 取值为integer 0 (d ...

  3. 使用 /proc 文件系统来访问 Linux 内核的内容

    最初开发 /proc 文件系统是为了提供有关系统中进程的信息.但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置. /proc 文件系统包含了一些目录(用作 ...

  4. Linux基础命令---sysctl修改内核参数

    sysctl sysctl指令用来修改正在运行的内核参数,可以修改的参数都保存在/proc/sys/目录中,修改会立即生效.Linux中的sysctl支持需要Procfs.您可以使用sysctl来读取 ...

  5. (转)使用 /proc 文件系统来访问 Linux 内核的内容

    转载网址:http://www.ibm.com/developerworks/cn/linux/l-proc.html 这个虚拟文件系统在内核空间和用户空间之间打开了一个通信窗口/proc 文件系统是 ...

  6. 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》...

    优化Linux内核参数/etc/sysctl.conf  sysctl  <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...

  7. 使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径

    使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径 作者: feng 日期: 2010/12/22发表评论 (0)查看评论 程 任全 (crquan@gmail ...

  8. sysctl 默认值_设置Linux内核参数 /etc/sysctl.conf

    设置Linux内核参数 /etc/sysctl.conf 配置 Linux 内核参数修改后不用重启动更新: /sbin/sysctl -p 打开/etc/sysctl.conf 复制如下内容 kern ...

  9. linux内核参数详解 sysctl -a dns内核优化 dns优化 dnsmasq配置

    一 linux内核基础知识 原文:https://www.cnblogs.com/cnwcl/p/9811327.html sysctl 命令用于查看和修改内核参数 查看指定参数: sysctl ke ...

最新文章

  1. AI芯片加速图像识别
  2. CodeForces - 1249C2 Good Numbers (hard version)(进制转换)
  3. 【AngularJS】—— 2 初识AngularJs(续)
  4. 安装php时,make步骤报错make: *** [sapi/fpm/php-fpm] Error 1
  5. 计算机网络技术实验,计算机网络技术实验
  6. 创建python的虚拟环境(图文教程),并使用。
  7. Linux能适应不同的指令集,(转)linux常用指令集
  8. dbgrideh 为什么只一行_Mysql性能优化:为什么count(*)这么慢?
  9. Java 蓝桥杯 字母图形
  10. HDU2999 Stone Game, Why are you always there?【SG函数】
  11. Diameter协议学习笔记一(协议介绍)
  12. C++——STL库中各算法以及其主要运用简介
  13. oracle删除行 锁表,oracle锁表
  14. 工业相机与工业镜头相关参数详解
  15. WR720N改造记录1
  16. R语言的graphics画图功能
  17. 思维模型 第一性原理
  18. 水晶报表官方实例下载:报表和应用程序
  19. WWN,WWNN,WWPN区别
  20. 数字化转型六图法:战略地图

热门文章

  1. Fail2ban在ip网络电话调度中的作用和实现方法
  2. 儿子的幼儿园 毕业寄语 - 感谢各位老师三年对孩子的培育,
  3. IT30: IT经理与CIO的技能差别
  4. 网红的冬天四季如春,人间百味自有芬芳
  5. 对象的基本使用:属性和方法
  6. 【Inno Setup】安装包制作,项目打包方案
  7. 【观点】从平台经济到平台经济学
  8. selenium自动化工具(一)
  9. 使用盛派Senparc SDK微信开发
  10. 农村20年前割麦图曝光!那时的端午节后竟是这样过...