从select函数谈及系统调用原理

目录

系统调用流程

select函数

在glibc-2.28中查找select函数

在linux内核linux-4.20.1中查找select

参考:


linux的select机制在socket编程中非常有用,其中存放文件描述符的fd_set已经给出代码:

https://blog.csdn.net/Rong_Toa/article/details/86359099

系统调用流程

图片来源:https://www.cnblogs.com/fasionchan/p/9431784.html

select函数

在glibc-2.28中查找select函数

在glibc中只能找到类似select.h这样的外部函数声明,

/* Check the first NFDS descriptors each in READFDS (if not NULL) for read
   readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
   (if not NULL) for exceptional conditions.  If TIMEOUT is not NULL, time out
   after waiting the interval specified therein.  Returns the number of ready
   descriptors, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int select (int __nfds, fd_set *__restrict __readfds,
		   fd_set *__restrict __writefds,
		   fd_set *__restrict __exceptfds,
		   struct timeval *__restrict __timeout);

在linux内核linux-4.20.1中查找select

发现经过source insight4.0创建的项目找不到select函数,然后查找select的文件名,找到了一个名字为select.c的文件,打开之,找到如下这行

COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp,
	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
	struct old_timeval32 __user *, tvp)
{
	return do_compat_select(n, inp, outp, exp, tvp);
}

//还有一个
COMPAT_SYSCALL_DEFINE1(old_select, struct compat_sel_arg_struct __user *, arg)
{
	struct compat_sel_arg_struct a;

	if (copy_from_user(&a, arg, sizeof(a)))
		return -EFAULT;
	return do_compat_select(a.n, compat_ptr(a.inp), compat_ptr(a.outp),
				compat_ptr(a.exp), compat_ptr(a.tvp));
}

再继续找到do_compat_select:

static int do_compat_select(int n, compat_ulong_t __user *inp,
	compat_ulong_t __user *outp, compat_ulong_t __user *exp,
	struct old_timeval32 __user *tvp)
{
	struct timespec64 end_time, *to = NULL;
	struct old_timeval32 tv;
	int ret;

	if (tvp) {
		if (copy_from_user(&tv, tvp, sizeof(tv)))
			return -EFAULT;

		to = &end_time;
		if (poll_select_set_timeout(to,
				tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
				(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
			return -EINVAL;
	}

	ret = compat_core_sys_select(n, inp, outp, exp, to);
	ret = compat_poll_select_copy_remaining(&end_time, tvp, 1, ret);

	return ret;
}

很显然调用了这个接口:

copy_from_user(&tv, tvp, sizeof(tv))

well,显然这实在内核中发生的事情,不过不要着急,这个只是select中的timeval参数,继续搜索COMPAT_SYSCALL_DEFINE5。结果没有直接的这个名字的宏,那么断定,这是一个宏定义的宏,至于宏定义了几“层”,尚且未知。那么想办法,等一下,方法这个select.c里面有一个do_select的函数,目前觉得可能最终是调用了这个接口。据说linux内核中的接口都是用这个“SYSCALL”调用的,找一下SYSCALL,在名为systbl_chk.c中找到了一个

#define SYSCALL(func)		__NR_##func

此外还找到一个

#define COMPAT_SYS(func)	__NR_##func

回顾一下上面的select接口的宏定义声明或者定义方式:

COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp,
	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
	struct old_timeval32 __user *, tvp)

这样看起来就对应上了,难不成有个叫做CALL_DEFINE5的宏?没找到。

那再找找__NR_select?这个还真有,在unistd.h文件里的宏定义:

#define __NR_select			358

此外还有一个:

#define __NR_select 				 14
__SYSCALL( 14, sys_select, 5)

还记得COMPAT_SYSCALL_DEFINE5吧,?难不成还有个COMPAT这个宏?不管了,在搜一下sys_select:

在syscall.h中找到:

asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,
			fd_set __user *exp, struct timeval __user *tvp);

其中:

#define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))

在syscall.c中:

#ifdef CONFIG_PPC32
/*
 * Due to some executables calling the wrong select we sometimes
 * get wrong args.  This determines how the args are being passed
 * (a single ptr to them all args passed) then calls
 * sys_select() with the appropriate args. -- Cort
 */
int
ppc_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp)
{
	if ( (unsigned long)n >= 4096 )
	{
		unsigned long __user *buffer = (unsigned long __user *)n;
		if (!access_ok(VERIFY_READ, buffer, 5*sizeof(unsigned long))
		    || __get_user(n, buffer)
		    || __get_user(inp, ((fd_set __user * __user *)(buffer+1)))
		    || __get_user(outp, ((fd_set  __user * __user *)(buffer+2)))
		    || __get_user(exp, ((fd_set  __user * __user *)(buffer+3)))
		    || __get_user(tvp, ((struct timeval  __user * __user *)(buffer+4))))
			return -EFAULT;
	}
	return sys_select(n, inp, outp, exp, tvp);
}
#endif

最终找到了select.c里:


/*
 * This is a virtual copy of sys_select from fs/select.c and probably
 * should be compared to it from time to time
 */

/*
 * We can actually return ERESTARTSYS instead of EINTR, but I'd
 * like to be certain this leads to no problems. So I return
 * EINTR just for safety.
 *
 * Update: ERESTARTSYS breaks at least the xview clock binary, so
 * I'm trying ERESTARTNOHAND which restart only when you want to.
 */
static int compat_core_sys_select(int n, compat_ulong_t __user *inp,
	compat_ulong_t __user *outp, compat_ulong_t __user *exp,
	struct timespec64 *end_time)
{
    /*此处省略一万个字*/
    ret = do_select(n, &fds, end_time);
    /*此处省略一万个字*/
}

其实我还是没有找到具体如何调用的,反正最终就是这么调用的。

参考:

https://my.oschina.net/fileoptions/blog/908682

https://www.cnblogs.com/fasionchan/p/9431784.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值