【Linux5.4】【TUN】代码学习记录(6)--tun_chr_open

【Linux5.4】【TUN】代码学习记录(6)–tun_chr_open

当用户调用open函数打开"/dev/net/tun"设备时,tun_chr_open函数被调用。

tun_chr_open

tun_chr_open主要功能是创建并初始化一个tun_file结构体变量tfile。

static int tun_chr_open(struct inode *inode, struct file * file)
{
	struct net *net = current->nsproxy->net_ns;
	struct tun_file *tfile;

	DBG1(KERN_INFO, "tunX: tun_chr_open\n");
	/* 创建tfile,申请一个tfile内存空间 */
	tfile = (struct tun_file *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL,
					    &tun_proto, 0);
	if (!tfile)
		return -ENOMEM;
	if (ptr_ring_init(&tfile->tx_ring, 0, GFP_KERNEL)) {
		sk_free(&tfile->sk);
		return -ENOMEM;
	}

	mutex_init(&tfile->napi_mutex);
	/* #define RCU_INIT_POINTER(p, v)	do { (p) = (v); } while (0) */
	RCU_INIT_POINTER(tfile->tun, NULL);
	tfile->flags = 0;
	tfile->ifindex = 0;
	/* 初始化socket等待队列头 */
	init_waitqueue_head(&tfile->socket.wq.wait);
	/* socket回指file */
	tfile->socket.file = file;
	/* 一组socket操作集 */
	tfile->socket.ops = &tun_socket_ops;
	/* 初始化sk中字段 */
	sock_init_data(&tfile->socket, &tfile->sk);
	/* sk_write_space功能检查是否有缓存可写 */
	tfile->sk.sk_write_space = tun_sock_write_space;
	tfile->sk.sk_sndbuf = INT_MAX;
	/* tfile作为file的私有数据 */
	file->private_data = tfile;
	INIT_LIST_HEAD(&tfile->next);

	sock_set_flag(&tfile->sk, SOCK_ZEROCOPY);

	return 0;
}

tun_socket_ops结构体使用模拟原始sockets操作

/* Ops structure to mimic raw sockets with tun */
static const struct proto_ops tun_socket_ops = {
	.peek_len = tun_peek_len,
	.sendmsg = tun_sendmsg,
	.recvmsg = tun_recvmsg,
};

sock_init_data定义在 <net/core/sock.c>
参考这里:sock_init_data注释

/*\linux-5.10.x\net\core\sock.c
用于初始化套接字数据结构中的各个字段和锁,以及设置套接字的默认参数。具体功能如下:
	1、初始化套接字的读写锁和对等锁
	2、设置套接字的对等进程ID和对等进程凭证为NULL,并初始化对等锁
	3、设置套接字的待发送数据长度为0,接收缓冲区低水位标记为1,接收超时和发送超时为最大超时值
	4、设置套接字的时间戳为默认时间戳值
	5、如果BITS_PER_LONG的值是32,初始化套接字的时间戳序列锁
	6、设置套接字的零拷贝密钥为0
	7、如果配置了网络接收忙碌轮询功能,设置套接字的NAPI ID为0,并从系统变量中读取并设置套接字的忙碌轮询时间
	8、设置套接字的最大调度速率和调度速率为最大的无符号长整型值,调度速率的位移为10。将套接字的入站CPU设置为-1
	9、清除套接字的接收队列,删除所有已接收但尚未处理的数据包
*/
void sock_init_data(struct socket *sock, struct sock *sk)
{
	sk_init_common(sk);											//初始化通用的sock数据结构
	sk->sk_send_head	=	NULL;								//将发送队列头部指针置为空

	timer_setup(&sk->sk_timer, NULL, 0);						//设置定时器,使用空回调函数和初始超时值0

	sk->sk_allocation	=	GFP_KERNEL;							//设置当前socket的内存分配标志为内核级别的分配
	sk->sk_rcvbuf		=	READ_ONCE(sysctl_rmem_default);		//读取系统变量sysctl_rmem_default的值作为接收缓冲区大小
	sk->sk_sndbuf		=	READ_ONCE(sysctl_wmem_default);		//读取系统变量sysctl_wmem_default的值作为发送缓冲区大小
	sk->sk_state		=	TCP_CLOSE;							//设置socket状态为TCP_CLOSE,表示关闭状态
	sk_set_socket(sk, sock);									//将sock与sk关联起来

	sock_set_flag(sk, SOCK_ZAPPED);								//设置SOCK_ZAPPED标志位,表示socket被重置

	if (sock) {													//如果sock非空
		sk->sk_type	=	sock->type;								//将socket类型设置为sock的类型
		RCU_INIT_POINTER(sk->sk_wq, &sock->wq);					//将socket的等待队列指针赋给sk的等待队列指针
		sock->sk	=	sk;										//将sk赋给socket的sk字段
		sk->sk_uid	=	SOCK_INODE(sock)->i_uid;				//获取socket所属文件描述符的用户ID,并赋给sk的用户ID
	} else {													//如果sock为空
		RCU_INIT_POINTER(sk->sk_wq, NULL);						//初始化sk的等待队列指针为空
		sk->sk_uid	=	make_kuid(sock_net(sk)->user_ns, 0);	//将套接字的用户ID设置为make_kuid创建的内核用户ID(root用户ID)
	}

	rwlock_init(&sk->sk_callback_lock);							//对套接字的sk_callback_lock字段进行读写锁的初始化
	if (sk->sk_kern_sock)										//套接字是否为内核套接字(kern_sock)。kern_sock是一种特殊类型的套接字,它在内核空间中使用,与用户空间无关
		lockdep_set_class_and_name(								//lockdep_set_class_and_name用于设置锁的类和名称。是内核中的一个调试工具,用于跟踪锁的使用情况
			&sk->sk_callback_lock,								//要设置的锁对象
			af_kern_callback_keys + sk->sk_family,				
			af_family_kern_clock_key_strings[sk->sk_family]);	//将使用af_kern_callback_keys数组中与套接字家族(sk_family)相对应的元素作为锁类
	else														//如果套接字不是内核套接字
		lockdep_set_class_and_name(
			&sk->sk_callback_lock,
			af_callback_keys + sk->sk_family,
			af_family_clock_key_strings[sk->sk_family]);		//将使用af_callback_keys数组中与套接字家族(sk_family)相对应的元素作为锁类

	sk->sk_state_change	=	sock_def_wakeup;					//套接字状态改变的回调函数,当套接字状态发生改变时,将调用此函数
	sk->sk_data_ready	=	sock_def_readable;					//套接字数据就绪的回调函数,当套接字有数据可读时,将调用此函数
	sk->sk_write_space	=	sock_def_write_space;				//套接字写空间可用的回调函数,当套接字的写空间可用时,将调用此函数
	sk->sk_error_report	=	sock_def_error_report;				//套接字错误报告的回调函数,当套接字发生错误时,将调用此函数
	sk->sk_destruct		=	sock_def_destruct;					//套接字析构的回调函数,当套接字被销毁时,将调用此函数
	/* 以下字段用于在接收数据时进行分片处理 */
	sk->sk_frag.page	=	NULL;								//套接字的分段信息初始化为NULL
	sk->sk_frag.offset	=	0;									//偏移量为0
	sk->sk_peek_off		=	-1;									//查看偏移量设置为-1
	/* 以下字段和锁用于处理套接字的对等进程信息 */
	sk->sk_peer_pid 	=	NULL;								//对等进程ID字段
	sk->sk_peer_cred	=	NULL;								//对等进程凭证字段
	spin_lock_init(&sk->sk_peer_lock);							//对对等锁sk_peer_lock进行自旋锁的初始化
	/* 以下字段用于控制套接字的发送和接收行为 */
	sk->sk_write_pending	=	0;								//套接字的待发送数据长度字段
	sk->sk_rcvlowat		=	1;									//接收缓冲区低水位标记,表示最小可接收数据量
	sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT;				//接收超时字段设置为最大超时值(MAX_SCHEDULE_TIMEOUT)
	sk->sk_sndtimeo		=	MAX_SCHEDULE_TIMEOUT;				//发送超时字段设置为最大超时值(MAX_SCHEDULE_TIMEOUT)

	sk->sk_stamp = SK_DEFAULT_STAMP;							//套接字的时间戳设置为默认时间戳值。用于记录套接字的最后一次状态改变的时间
#if BITS_PER_LONG==32                                		 	// 如果每个长整型数占32位
		seqlock_init(&sk->sk_stamp_seq);				 		// 初始化套接字时间戳序列锁
#endif
	
	atomic_set(&sk->sk_zckey, 0);								// 设置套接字的零拷贝密钥为0
	
#ifdef CONFIG_NET_RX_BUSY_POLL
		sk->sk_napi_id = 0; 									// 网络适配器处理标识符初始化为0
		sk->sk_ll_usec = READ_ONCE(sysctl_net_busy_read);		// 读取网络繁忙忙等待时长
#endif
	/* 以下字段用于控制数据包的调度和处理 */
	sk->sk_max_pacing_rate = ~0UL;						 		// 设置套接字最大传输速率为最大值
	sk->sk_pacing_rate = ~0UL;							 		// 设置套接字传输速率为最大值
	WRITE_ONCE(sk->sk_pacing_shift, 10);				 		// 设置套接字传输速率控制参数为10
	sk->sk_incoming_cpu = -1;							 		// 设置套接字的所属CPU为-1,表示未分配CPU
	
	sk_rx_queue_clear(sk);								 		// 清空套接字的接收队列,这将删除所有已接收但尚未处理的数据包

	/*
	 * Before updating sk_refcnt, we must commit prior changes to memory
	 * (Documentation/RCU/rculist_nulls.rst for details)
	 */
	smp_wmb();													// 在多核系统中确保先前的写操作对于其他CPU可见
	refcount_set(&sk->sk_refcnt, 1);							// 设置套接字引用计数为1,表示有一个引用
	atomic_set(&sk->sk_drops, 0);								// 将套接字丢弃数据包计数器重置为0
}

tun_sock_write_space

static void tun_sock_write_space(struct sock *sk)
{
	struct tun_file *tfile;
	wait_queue_head_t *wqueue;

	if (!sock_writeable(sk))
		return;

	if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))
		return;

	wqueue = sk_sleep(sk);
	if (wqueue && waitqueue_active(wqueue))
		wake_up_interruptible_sync_poll(wqueue, EPOLLOUT |
						EPOLLWRNORM | EPOLLWRBAND);

	tfile = container_of(sk, struct tun_file, sk);
	/* 发送信号给tfile->fasync,即tun_chr_fasync */
	kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
}

tun_chr_fasync

static int tun_chr_fasync(int fd, struct file *file, int on)
{
	struct tun_file *tfile = file->private_data;
	int ret;

	if ((ret = fasync_helper(fd, file, on, &tfile->fasync)) < 0)
		goto out;

	if (on) {
		__f_setown(file, task_pid(current), PIDTYPE_TGID, 0);
		tfile->flags |= TUN_FASYNC;
	} else
		tfile->flags &= ~TUN_FASYNC;
	ret = 0;
out:
	return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这个错误通常表示在编译`aqua-sim-routing-dummy.cc`文件时出现了问题。根据日志,这是通过使用g++编译器尝试编译该文件的命令: ```shell /usr/bin/g++ -O0 -ggdb -g3 -Wall -Werror -std=c++11 -Wno-error=deprecated-declarations -fstrict-aliasing -Wstrict-aliasing -fPIC -pthread -I. -I.. -DNS3_BUILD_PROFILE_DEBUG -DNS3_ASSERT_ENABLE -DNS3_LOG_ENABLE -DHAVE_SYS_IOCTL_H=1 -DHAVE_IF_NETS_H=1 -DHAVE_NET_ETHERNET_H=1 -DHAVE_PACKET_H=1 -DHAVE_IF_TUN_H=1 -DHAVE_GSL=1 -DHAVE_SQLITE3=1 ../src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc -c -o /home/fjl/ns-allinone-3.26/ns-3.26/build/src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc.1.o ``` 根据命令和错误信息,可能有以下一些原因导致构建失败: 1. 缺少某些依赖项:请确保你的系统中安装了所有必需的依赖项。在这个特定的情况下,确保你的系统中安装了g++编译器、pthread库、以及其他相关的头文件和库。 2. 编译器错误:有时候编译器可能会有一些问题,尝试更新或更换你的编译器版本,可能会解决该问题。 3. 文件路径错误:检查`../src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc`文件是否存在,并确保路径正确。 4. 代码错误:在`aqua-sim-routing-dummy.cc`文件中可能存在语法错误或其他编译错误。请仔细检查该文件,并确保代码没有问题。 希望这些提示能够帮助你解决构建失败的问题!如果还有其他疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值