ソケットバックログ(connect)


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となり、要は指定された時間待機した2回の試みの実装という事です。)を次期待機時間とし再度接続を試みます。
#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);
}

最終更新 2016/10/05 14:09:34 - north
(2016/10/05 14:01:15 作成)


検索

アクセス数
3584673
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。