ソケットバックログ(connect)
Rev.1を表示中。最新版はこちら。
connectは接続時の接続済数がlistシステムコールによるバックログ(sk->sk_max_ack_backlog)を超えてなければ接続可能で、sk->sk_max_ack_backlog=0ならconnectは1つだけとなります。バックログを超える接続は、sk->sk_sndtimeo間sk->peer_waitでウエイトし再度接続を試み、接続できなければエラーとなります。ただし、sk->sk_sndtimeo=MAX_SCHEDULE_TIMEOUTなら、MAX_SCHEDULE_TIMEOUT間ウエイトとし再度接続の試みを永久に繰り返します。ソケットのデフォルトはMAX_SCHEDULE_TIMEOUTです。なおsetsockoptシステムコールの0での設定は、MAX_SCHEDULE_TIMEOUTが設定されます。検証サンプル
[root@localhost c]# cat unixsk_sdtime.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#define unixsk_file "unixsk.txt"
void sd_time(int sock, int time)
{
struct timeval send_tv;
send_tv.tv_sec = time;
send_tv.tv_usec = 0;
setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, &send_tv, sizeof(send_tv) );
}
void sever()
{
struct sockaddr_un sock_addr;
int status, sock;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
sock_addr.sun_family = AF_UNIX;
strcpy(sock_addr.sun_path, unixsk_file);
remove(sock_addr.sun_path);
bind(sock, (struct sockaddr*) &sock_addr, sizeof(struct sockaddr_un));
listen(sock, 0);
wait(&status);
}
void client(int wait)
{
int sock, wtime, ret;
struct sockaddr_un sa;
time_t time1, time2;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
sd_time(sock, wait);
sa.sun_family = AF_UNIX;
strcpy(sa.sun_path, unixsk_file);
ret = connect(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un));
printf("returned connect1:%d\n", ret);
time(&time1);
ret = connect(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un));
time(&time2);
wtime = (int)difftime(time2,time1);
printf("returned connect2:%d wate time:%d\n", ret, wtime);
close(sock);
}
void main(int argc, char * argv[])
{
if (fork()) {
sever();
}
else {
sleep(1);
client(atoi(argv[1]));
}
}
結果 [root@localhost c]# ./unixsk_sdtime.o -1 <-------sk->sk_sndtimeo=0でウエイトせずエラー。 returned connect1:0 returned connect2:-1 wate time:0 [root@localhost c]# ./unixsk_sdtime.o 0 <-------sk->sk_sndtimeo=MAX_SCHEDULE_TIMEOUTで永久にウエイト。 returned connect1:0 ^C [root@localhost c]# ./unixsk_sdtime.o 1 <-------sk->sk_sndtimeo=1で1秒ウエイトしエラー。 returned connect1:0 returned connect2:-1 wate time:1
実装
connectの待機はschedule_timeout(sk->sk_sndtimeo)がコールされ、MAX_SCHEDULE_TIMEOUTならサーバプロセスからaccess下から起床されるまで待機し、マイナスなら待機せず、そうでないなら其の時間待機し、待機した分を差し引いた時間(通常は0となりエラー)を次期待機時間とし再度接続を試みます。
#define MAX_SCHEDULE_TIMEOUT LONG_MAX
signed long __sched schedule_timeout(signed long timeout)
{
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
schedule();
goto out;
default:
if (timeout < 0) {
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx\n", timeout);
dump_stack();
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
schedule();
del_singleshot_timer_sync(&timer);
destroy_timer_on_stack(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
setsockoptシステムコールの引数level=SOL_SOCKET/optname=SO_SNDTIMEOで、long *timeo_p引数を&sk->sk_sndtimeoでコールされます。tv.tv_sec < 0なら、sk->sk_sndtimeo=0で、tv.tv_sec == 0 && tv.tv_usec == 0なら、sk->sk_sndtimeo=MAX_SCHEDULE_TIMEOUT、そうでないなら掛かる時間が設定されます。
static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
{
struct timeval tv;
if (optlen < sizeof(tv))
return -EINVAL;
if (copy_from_user(&tv, optval, sizeof(tv)))
return -EFAULT;
if (tv.tv_usec < 0 || tv.tv_usec >= USEC_PER_SEC)
return -EDOM;
if (tv.tv_sec < 0) {
static int warned __read_mostly;
*timeo_p = 0;
if (warned < 10 && net_ratelimit()) {
warned++;
printk(KERN_INFO "sock_set_timeout: `%s' (pid %d) "
"tries to set negative timeout\n",
current->comm, task_pid_nr(current));
}
return 0;
}
*timeo_p = MAX_SCHEDULE_TIMEOUT;
if (tv.tv_sec == 0 && tv.tv_usec == 0)
return 0;
if (tv.tv_sec < (MAX_SCHEDULE_TIMEOUT/HZ - 1))
*timeo_p = tv.tv_sec*HZ + (tv.tv_usec+(1000000/HZ-1))/(1000000/HZ);
return 0;
}
ソケット初期化のソケット作成時にコールされ、sk->sk_rcvtimeo=MAX_SCHEDULE_TIMEOUT/sk->sk_sndtimeo=MAX_SCHEDULE_TIMEOUTとなります。
void sock_init_data(struct socket *sock, struct sock *sk)
{
skb_queue_head_init(&sk->sk_receive_queue);
skb_queue_head_init(&sk->sk_write_queue);
skb_queue_head_init(&sk->sk_error_queue);
#ifdef CONFIG_NET_DMA
skb_queue_head_init(&sk->sk_async_wait_queue);
#endif
sk->sk_send_head = NULL;
init_timer(&sk->sk_timer);
sk->sk_allocation = GFP_KERNEL;
sk->sk_rcvbuf = sysctl_rmem_default;
sk->sk_sndbuf = sysctl_wmem_default;
sk->sk_state = TCP_CLOSE;
sk_set_socket(sk, sock);
sock_set_flag(sk, SOCK_ZAPPED);
if (sock) {
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk;
} else
sk->sk_wq = NULL;
spin_lock_init(&sk->sk_dst_lock);
rwlock_init(&sk->sk_callback_lock);
lockdep_set_class_and_name(&sk->sk_callback_lock,
af_callback_keys + sk->sk_family,
af_family_clock_key_strings[sk->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_sndmsg_page = NULL;
sk->sk_sndmsg_off = 0;
sk->sk_peer_pid = NULL;
sk->sk_peer_cred = NULL;
sk->sk_write_pending = 0;
sk->sk_rcvlowat = 1;
sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
sk->sk_stamp = ktime_set(-1L, 0);
smp_wmb();
atomic_set(&sk->sk_refcnt, 1);
atomic_set(&sk->sk_drops, 0);
}





