【linux C】一个函数看懂进程堵塞和非堵塞的原理

堵塞和非堵塞

阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务。函数只有在得到结果之后才会返回。
比如: 堵塞的accept 函数,在内核没有缓冲区数据的时候,会被堵塞;直到内核缓冲区有数据的时候,才会返回,线程才会继续往下执行

非阻塞:非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
比如:非堵塞的accept 函数,在内核没有缓冲区数据的时候,可以直接返回一个错误码给当前线程。

accept 函数

应用层的 accept 的调用逻辑如下,最终调到 inet_csk_accept

sock->ops->accept
	|->inet_steam_ops->accept(inet_accept)
		/* 由一开始的sock图可知sk_prot=tcp_prot */
		|->sk1->sk_prot->accept
			|->inet_csk_accept

看内核函数inet_csk_accept
如下:除去省略的代码,
1. 可以看到主要的堵塞代码:error = inet_csk_wait_for_connect(sk, timeo);,作用:睡眠等待,线程挂起,【堵塞】等待有新连接进入队列。
2. 但如果将文件描述符的属性flags设为 O_NONBLOCK,那么 timeo 将为 空,接着 if (!timeo) goto out_err; 直接返回。

/*
 * This will accept the next outstanding connection.
 */
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
{
	...
	/* Find already established connection */
	// 如果队列为空
	if (reqsk_queue_empty(queue)) {
		// 【非堵塞】如果文件描述符的 flags 的标记位是 O_NONBLOCK,那么直接返回。
		long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);

		/* If this is a non blocking socket don't sleep */
		error = -EAGAIN;
		if (!timeo)
			goto out_err;

		// 【堵塞】等待有新连接进入队列
		error = inet_csk_wait_for_connect(sk, timeo);
		if (error)
			goto out_err;
	}
	req = reqsk_queue_remove(queue);
	newsk = req->sk;

	...
out:
	...
out_err:
	...
}
EXPORT_SYMBOL(inet_csk_accept);

其中,inet_csk_wait_for_connect 的堵塞逻辑,是一个 for(;;),它里面比较高级一些,可以设置超时时间

static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{
	for (;;) {
		/* 通过增加EXCLUSIVE标志使得在BIO中调用accept中不会产生惊群效应 */
		prepare_to_wait_exclusive(sk_sleep(sk), &wait,
					  TASK_INTERRUPTIBLE);
		if (reqsk_queue_empty(&icsk->icsk_accept_queue))
			timeo = schedule_timeout(timeo);
		.......
		err = -EAGAIN;
		/* 这边accept超时,返回的是-EAGAIN */
		if (!timeo)
			break;
	}
	finish_wait(sk_sleep(sk), &wait);
	return err;						
}

通过上面就可以看出,默认的 socket 堵塞的,不带 O_NONBLOCK 属性。
可以调用 fcntl,将文件描述符的 flags 设为O_NONBLOCK,这样就可以在 accept 的时候非阻塞

flags &= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) < 0)
{
    perror("fcntl");
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值