搜索墙首页  编程技术  java  asp  ajax  php  c/c#/c++  数据库  oracle  mysql  db2  操作系统  windows  linux  股吧
首页 > 操作系统 > linux
 1         
1楼  bomail 2008-01-12

ktcpvs是LVS的一个子项目,主要是针对layer-7的负载均衡,具体介绍参考www.linuxvirtualserver.org。本文是针 对ktcpvs2.6.9版本的,对应linux2.6内核的,下面进入正题,对我理解的ktcpvs源码进行一个描述。

1.整个module的入口文件是tcp_vs.c:
module_init(ktcpvs_init);
这是module的初始化接口,由此转到ktcpvs_init。

2.ktcpvs_init函数
static int __init ktcpvs_init(void)
{
    tcp_vs_control_start();
    tcp_vs_slowtimer_init();
    tcp_vs_srvconn_init();
    (void) kernel_thread(master_daemon, NULL, 0);
}
做一些初始化工作之后,进入master_daemon线程。
3.master_daemon线程
static int master_daemon(void *unused)
{
    /* main loop */
    while (sysctl_ktcpvs_unload == 0) {
        read_lock(&__tcp_vs_svc_lock);
        list_for_each(l, &tcp_vs_svc_list) {
            svc = list_entry(l, struct tcp_vs_service, list);
            if (!atomic_read(&svc->running) && svc->start)
                kernel_thread(tcp_vs_daemon, svc, 0);
        }
        read_unlock(&__tcp_vs_svc_lock);       
    }
    return 0;
}
这是主工作线程,只有一个,遍历tcp_vs_svc_list(相当于config文件),启动每个service(ktcpvs的一个service 就对应一个虚拟服务,相当于config文件中的其中一个vitual web)就是svc,每个svc开启tcp_vs_daemon线程。

4.tcp_vs_daemon线程
static int tcp_vs_daemon(void *__svc)
{
   /* Then start listening and spawn the daemons */
    if (StartListening(svc) < 0)
        goto out;
    for (i = 0; i < svc->conf.startservers; i++)
        make_child(child_table, i, svc);
        /* dynamically keep enough thread to handle load */
        child_pool_maintenance(child_table, svc);
    }
    /* stop listening */
    StopListening(svc);
    return 0;
}
每个虚拟服务svc都会启动这样一个线程,对于一个svc,主要调用StartListening函数创建socket,启动监听,然后根据config 文件startserver参数通过make_child函数初始启动相应的多个children与svc相关的连接准备,放到child_table 中,child_table主要由struct tcp_vs_child数组children构成,每个数组元素主要有个指向svc的指针。最后是关闭监听,退出,不再跟下去了,主要看 StartListening和make_child。

5.StartListening函数,在Misc.c文件
int StartListening(struct tcp_vs_service *svc)
{
    /* First create a socket */
    error = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
    /* Now bind the socket */
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = svc->conf.addr;
    sin.sin_port = svc->conf.port;
    error = sock->ops->bind(sock, (struct sockaddr *) &sin, sizeof(sin));
    /* Now, start listening on the socket */
    error = sock->ops->listen(sock, sysctl_ktcpvs_max_backlog);
    svc->mainsock = sock;
    return 0;
}
就是创建socket,绑定制定地址和端口,开始监听。

6.make_child函数
static inline void make_child(struct tcp_vs_child_table *tbl, int slot, struct tcp_vs_service *svc)
{
    tbl->children[slot].svc = svc;
    if (kernel_thread(tcp_vs_child, &tbl->children[slot], CLONE_VM | CLONE_FS | CLONE_FILES) < 0)
        TCP_VS_ERR("spawn child failed\n");
}
把tcp_vs_child_table的children[i]跟svc关联上,然后启动tcp_vs_child线程。

7.tcp_vs_child线程
static int tcp_vs_child(void *__child)
{
    struct tcp_vs_child *chd = (struct tcp_vs_child *) __child;
    struct tcp_vs_service *svc = chd->svc;
    chd->pid = current->pid;
    sock = svc->mainsock;
    while (svc->stop == 0 && sysctl_ktcpvs_unload == 0) {       
        /* create tcp_vs_conn object */
        conn = tcp_vs_conn_create(sock, Buffer, BufLen);
        /* Do the actual accept */
        ret = sock->ops->accept(sock, conn->csock, O_NONBLOCK);
        /* Do the work */
        ret = tcp_vs_conn_handle(conn, svc);
    }
    return 0;
}
一个虚拟服务会根据配置启动tcp_vs_child的线程池,把sock指向之前在监听的mainsock,初始化一个与client之间的 tcp_vs_conn对象conn,从监听socket accpet一个socket指定由conn->csock处理(就是与client之间的socket),然后就处理进入 tcp_vs_conn_handle函数进行具体数据报处理。

8.tcp_vs_conn_handle函数
int tcp_vs_conn_handle(struct tcp_vs_conn *conn, struct tcp_vs_service *svc)
{
    csock = conn->csock;
    if (csock->sk->sk_state != TCP_ESTABLISHED) {
        if (csock->sk->sk_state == TCP_CLOSE_WAIT)
            return 0;
        return -1;
    }
    switch (svc->scheduler->schedule(conn, svc)) {
    case 1:        /* scheduler has done all the work */
        return 0;
    case 0:        /* further process needed */
        break;
    case -1:        /* try to redirect the connection to other sockets */
        /* fault tolerance function*/
        if (!fault_redirect(conn,svc))
            break;
        if (svc->conf.redirect_port) {
            redirect_to_local(conn, svc->conf.redirect_addr, svc->conf.redirect_port);
            return 0;
        }
        return -1;
    default:
        return 0;
    }
    dsock = conn->dsock;    
    lastupdated = jiffies;
    while ((jiffies - lastupdated) < sysctl_ktcpvs_read_timeout * HZ) {
        /* if the connection is closed, go out of this loop */
        if (dsock->sk->sk_state != TCP_ESTABLISHED
            && dsock->sk->sk_state != TCP_CLOSE_WAIT)
            break;
        if (csock->sk->sk_state != TCP_ESTABLISHED
            && csock->sk->sk_state != TCP_CLOSE_WAIT)
            break;
        /* Do we have data from server? */
        if (!skb_queue_empty(&(dsock->sk->sk_receive_queue))) {
            if (tcp_vs_relay_socket(dsock, csock) == 0)
                break;
            lastupdated = jiffies;
        }
        /* Do we have data from client? */
        if (!skb_queue_empty(&(csock->sk->sk_receive_queue))) {
            if (tcp_vs_relay_socket(csock, dsock) == 0)
                break;
            lastupdated = jiffies;
        }
        if (skb_queue_empty(&(dsock->sk->sk_receive_queue))
                && skb_queue_empty(&(csock->sk->sk_receive_queue))) {
            if (dsock->sk->sk_state == TCP_CLOSE_WAIT
                || csock->sk->sk_state == TCP_CLOSE_WAIT)
                break;
            /*
            * Put the current task on the sleep wait queue
            * of both the sockets, wake up the task if one
            * socket has some data ready.
            */
            add_wait_queue(csock->sk->sk_sleep, &wait1);
            add_wait_queue(dsock->sk->sk_sleep, &wait2);
            __set_current_state(TASK_INTERRUPTIBLE);
            __set_current_state(TASK_RUNNING);
            remove_wait_queue(csock->sk->sk_sleep, &wait1);
            remove_wait_queue(dsock->sk->sk_sleep, &wait2);
        }
    }
    return 0;
}
根据config中配置的scheduler来处理client的请求,如果scheduler配置为http,则在tcp_vs_http.c这定义了static struct tcp_vs_scheduler tcp_vs_http_scheduler = {
    {0},            /* n_list */
    "http",            /* name */
    THIS_MODULE,        /* this module */
    tcp_vs_http_init_svc,    /* initializer */
    tcp_vs_http_done_svc,    /* done */
    tcp_vs_http_update_svc,    /* update */
    tcp_vs_http_schedule,    /* select a server by http request */
};调度函数是tcp_vs_http_schedule,负责选择一个realserver并与之建立连接,把client过来的数据包转发过去,调度完之后,再由tcp_vs_relay_socket把realserver得到响应再响应给client。
 1         
您的发言将按有关规定都会存档,您须为所发表后果负责,请您遵纪守法并注意语言文明。
标题:ktcpvs源代码结构分析
热门关注
标题回复点击
d7se77dy/77dy电影. 77dy是77dy宽频和77dy133819
zeh 4.p patch on RHEL 30566
Apache 超全0518
将 Puppy Linux 4.00 安装在U盘里 打造随身系统2507
gcc glibc kernel 版本兼容性 reference0498
[保留]7498
linux 有啥低级格式化硬盘的软件1466
黄网最新地址0396
求助inter(r) pro/100 ve network connection 网卡驱动(急~~急~~急~)1393
man page3371
搜索墙@2009 www.pkwall.com all rights reserved QQ:276471788 [京ICP备09111534号]
声明:本站部分数据来源于网络,仅供参考,如有版权问题,请联系我们,我们将及时删除!转载本站请注明来源