1)procfs(/proc )
procfs和sysctl都可以导出内核内部信息,但procfs用于导出只读数据,而sysctl导出的数据是可读写的。
大多数网络功能初始化时都会在/proc中注册一个或多个文件。当用户读取这个文件时,内核会调用一组内核函数来输出相应的数据。这组内核函数是在创建文件时初始化的文件操作函数句柄定义的;
static struct file_operation xx_fops={ //函数句柄xx_fops
.open = xx_open; //也是初始化工作,注册一组函数指针,用与遍邻和定位返回给用户的数据
.read = xx_read,
....
};
static int__init xx_proc_init(void){
if(!proc_net_fops_create("xx",s_irugo,&xx_fops)){..} //创建文件,初始化的文件函数句柄
..
};
static struct seq_operations xx_op={ //xx_open
.start = clip_seq_start,
.next = neigh_seq_next,
....
};
static in xx_open(struct inode *inode,struct file *file){ //定义xx_open()
rc=seq_open(file,&xx_op)
...
};
static ... xx_read (..){
....
};
/proc/sys中看到的每个文件实际上都是一个内核变量,读取和设置内核变量都是通过ctl_table结构(/proc/sys中的每个目录和文件都是一个ctl_table结构,对于文件
的ctl_table存在proc_handler指定操作,而对于目录存在child指定子目录或该目录中的文件)中的proc_handler域指定的内核函数作具体操作的;
static ctl_table scsi_table[]={
.ctl_name=dev_scsi_logging_level,
.procname="logging_level", // /proc/sys/下的文件名
.data=&scsi_logging_level, //相关联的内核变量
.maxlen=sizeof(scsi_logging_level), //内核变量大小
.mode=0644, //文件目录权限
.proc_handler=&proc_dointvec }, //内核调用的函数 proc_dointvec--读写一个整数数组,类似的函数很多在kernel/sysctl.c中
{}
};
static ctl_table scsi_dir_table[]={
.ctl_name=dev_scsi,
.procname="scsi",
.mode=0555,
.child=scsi_table ], //目录关系
{}
};
static ctl_table scsi_root_table[]= {
.ctl_name=ctl_dev,
.procname="dev",
.mode=0555,
.child=scsi_dir_table },
{}
};
int __init scsi_init_sysctl(void){
scsi_table_header=register_sysctl_table(scsi_root_table,1); //注册/proc/sys/dev/scsi/logging_level文件
}
比如我们cat /proc/sys/dev/scsi/logging_level 实际上是对scsi_logging_level变量执行proc_dointvec函数,具体操作要看proc_dointvec的实现
比procfs和sysctl更新的文件系统
ioctl接口实际上是通过打开一个socket,并根据这个socket初始化一个数据结构,然后把这个数据结构传递给ioctl,再经由sock_ioctl分派到正确的处理函数
以ifconfig eth0 mtu xxx为例
struct ifreq data;
fd=socket(PF_INET,SOCK_DGRAM,0); //创建套接字描述符, pf_inet--tcp/ip, sock_dgram--数据报套接字类型
err=ioctl(fd,siocsifmtu,&data);
....
(未涉及sock_ioctl代码)
5)netlink socket
netlink socket直接使用标准的socket api打开、关闭、发送和接收信息
socket(pf_netlink,sock_dgram,...)
netlink socket支持多播信息