VIM 相关资源

vim 下载地址:
Linux版:wget ftp://ftp.vim.org/pub/vim/unix/vim-7.1.tar.bz2

帮助文档地址:
http://vimcdoc.sourceforge.net/
http://vimcdoc.sourceforge.net/doc/help.html

VIM帮助手册pdf
VIM在线手册

手把手教你把Vim改装成一个IDE编程环境(图文)
http://blog.csdn.net/wooin/archive/2007/12/30/2004470.aspx

Vim颜色设置
http://zywangyan54.blog.163.com/blog/static/31810358200752993227703/

Vim程序调试
http://www.wangchao.net.cn/bbsdetail_69434.html

Vim使用经验
http://blog.csdn.net/camry_camry/archive/2004/09/23/114188.aspx

Vim自动给脚本加注释
http://blog.chinaunix.net/u/6542/showart.php?id=357716

VIM 插件大全 及 不错介绍
http://hi.baidu.com/00%C6%F3%B6%EC/blog/item/fd456c03a2d40f8bd53f7c29.html

Vim即学即用
http://blog.linuxpk.com/3973/viewspace-2644

小技巧:

全文档代码格式化,命令模式下: gg=G 即可搞定;
当前行代码格式化,命令模式下: == 即可搞定;
代码块格式化,命令模式下: =a{ ; 格式化{}里面的代码
更多的还要看上面的手册呢

删除所有空行: :g /^$/d #注意不要 :% s/^$//

 

linux内核网络栈代码的准备知识

一.linux内核网络栈代码的准备知识
 
1. linux内核ipv4网络部分分层结构
 
BSD socket层: 这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现。这一部分的文件
 
主要有:/net/socket.c /net/protocols.c etc

INET socket层:BSD socket是个可以用于各种网络协议的接口,而当用于tcp/ip,即建立了AF_INET形式的socket时,

 
还需要保留些额外的参数,于是就有了struct sock结构。文件主要
 
有:/net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc

TCP/UDP层:处理传输层的操作,传输层用struct inet_protocol和struct proto两个结构表示。文件主要

 
有:/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp.c /net/ipv4/tcp_input.c /net/ipv4//tcp_output.c /net/ipv4/tcp_minisocks.c /net/ipv4/tcp_output.c /net/ipv4/tcp_timer.c
 
etc  
     
IP层:处理网络层的操作,网络层用struct packet_type结构表示。文件主要有:/net/ipv4/ip_forward.c
ip_fragment.c ip_input.c ip_output.c etc.

数据链路层和驱动程序:每个网络设备以struct net_device表示,通用的处理在dev.c中,驱动程序都在/driver/net目

 
录下。
 
2. 两台主机建立udp通信所走过的函数列表
 
^
|       sys_read                fs/read_write.c
|       sock_read               net/socket.c
|       sock_recvmsg            net/socket.c
|       inet_recvmsg            net/ipv4/af_inet.c
|       udp_recvmsg             net/ipv4/udp.c
|       skb_recv_datagram       net/core/datagram.c
|       ——————————————-
|       sock_queue_rcv_skb      include/net/sock.h
|       udp_queue_rcv_skb       net/ipv4/udp.c
|       udp_rcv                 net/ipv4/udp.c
|       ip_local_deliver_finish net/ipv4/ip_input.c
|       ip_local_deliver        net/ipv4/ip_input.c
|       ip_recv                 net/ipv4/ip_input.c
|       net_rx_action           net/dev.c
|       ——————————————-
|       netif_rx                net/dev.c
|       el3_rx                  driver/net/3c309.c
|       el3_interrupt           driver/net/3c309.c

==========================

|       sys_write               fs/read_write.c
|       sock_writev             net/socket.c                    
|       sock_sendmsg            net/socket.c
|       inet_sendmsg            net/ipv4/af_inet.c
|       udp_sendmsg             net/ipv4/udp.c
|       ip_build_xmit           net/ipv4/ip_output.c
|       output_maybe_reroute    net/ipv4/ip_output.c
|       ip_output               net/ipv4/ip_output.c
|       ip_finish_output        net/ipv4/ip_output.c
|       dev_queue_xmit          net/dev.c
|       ——————————————–
|       el3_start_xmit          driver/net/3c309.c
V

 
 
3. 网络路径图、重要数据结构sk_buffer及路由介绍
 
    linux-net.pdf 第2.1章 第2.3章 第2.4章
    
4. 从连接、发送、到接收数据包的过程
 
    linux-net.pdf 第4、5、6章详细阐述
 
 
二.linux的tcp-ip栈代码的详细分析
 
1.数据结构(msghdr,sk_buff,socket,sock,proto_ops,proto)
 
bsd套接字层,操作的对象是socket,数据存放在msghdr这样的数据结构:
 
创建socket需要传递family,type,protocol三个参数,创建socket其实就是创建一个socket实例,然后创建一个文件描述符结构,并且互相建立一些关联,即建立互相连接的指针,并且初始化这些对文件的写读操作映射到socket的read,write函数上来。
 
同时初始化socket的操作函数(proto_ops结构),如果传入的type参数是STREAM类型,那么就初始化为SOCKET->ops为inet_stream_ops,如果是DGRAM类型,则SOCKET-ops为inet_dgram_ops。对于inet_stream_ops其实是一个结构体,包含了stream类型的socket操作的一些入口函数,在这些函数里主要做的是对socket进行相关的操作,同时通过调用下面提到的sock中的相关操作完成socket到sock层的传递。比如在inet_stream_ops里有个inet_release的操作,这个操作除了释放socket的类型空间操作外,还通过调用socket连接的sock的close操作,对于stream类型来说,即tcp_close来关闭sock
释放sock。
 
创建socket同时还创建sock数据空间,初始化sock,初始化过程主要做的事情是初始化三个队列,receive_queue(接收到的数据包sk_buff链表队列),send_queue(需要发送数据包的sk_buff链表队列),backlog_queue(主要用于tcp中三次握手成功的那些数据包,自己猜的),根据family、type参数,初始化sock的操作,比如对于family为inet类型的,type为stream类型的,sock->proto初始化为tcp_prot.其中包括stream类型的协议sock操作对应的入口函数。
 
在一端对socket进行write的过程中,首先会把要write的字符串缓冲区整理成msghdr的数据结构形式(参见linux内核2.4版源代码分析大全),然后调用sock_sendmsg把msghdr的数据传送至inet层,对于msghdr结构中数据区中的每个数据包,创建sk_buff结构,填充数据,挂至发送队列。一层层往下层协议传递。一下每层协议不再对数据进行拷贝。而是对sk_buff结构进行操作。
 
inet套接字及以下层 数据存放在sk_buff这样的数据结构里:
 
路由:
    
    在linux的路由系统主要保存了三种与路由相关的数据,第一种是在物理上和本机相连接的主机地址信息表,第二种是保存了在网络访问中判断一个网络地址应该走什么路由的数据表;第三种是最新使用过的查询路由地址的缓存地址数据表。
    1.neighbour结构  neighbour_table{ }是一个包含和本机所连接的所有邻元素的信息的数据结构。该结构中有个元素是neighbour结构的数组,数组的每一个元素都是一个对应于邻机的neighbour结构,系统中由于协议的不同,会有不同的判断邻居的方式,每种都有neighbour_table{}类型的实例,这些实例是通过neighbour_table{}中的指针next串联起来的。在neighbour结构中,包含有与该邻居相连的网络接口设备net_device的指针,网络接口的硬件地址,邻居的硬件地址,包含有neigh_ops{}指针,这些函数指针是直接用来连接传输数据的,包含有queue_xmit(struct * sk_buff)函数入口地址,这个函数可能会调用硬件驱动程序的发送函数。
 
    2.FIB结构 在FIB中保存的是最重要的路由规则,通过对FIB数据的查找和换算,一定能够获得路由一个地址的方法。系统中路由一般采取的手段是:先到路由缓存中查找表项,如果能够找到,直接对应的一项作为路由的规则;如果不能找到,那么就到FIB中根据规则换算传算出来,并且增加一项新的,在路由缓存中将项目添加进去。
    3.route结构(即路由缓存中的结构)
 
 
 
数据链路层:
  
   net_device{}结构,对应于每一个网络接口设备。这个结构中包含很多可以直接获取网卡信息的函数和变量,同时包含很多对于网卡操作的函数,这些直接指向该网卡驱动程序的许多函数入口,包括发送接收数据帧到缓冲区等。当这些完成后,比如数据接收到缓冲区后便由netif_rx(在net/core/dev.c各种设备驱动程序的上层框架程序)把它们组成sk_buff形式挂到系统接收的backlog队列然后交由上层网络协议处理。同样,对于上层协议处理下来的那些sk_buff。便由dev_queue_xmit函数放入网络缓冲区,交给网卡驱动程序的发送程序处理。
 
   在系统中存在一张链表dev_base将系统中所有的net_device{}结构连在一起。对应于内核初始化而言,系统启动时便为每个所有可能支持的网络接口设备申请了一个net_device{}空间并串连起来,然后对每个接点运行检测过程,如果检测成功,则在dev_base链表中保留这个接点,否则删除。对应于模块加载来说,则是调用register_netdev()注册net_device,在这个函数中运行检测过程,如果成功,则加到dev_base链表。否则就返回检测不到信息。删除同理,调用
unregister_netdev。
 
 
2.启动分析
 
    2.1 初始化进程 :start-kernel(main.c)—->do_basic_setup(main.c)—->sock_init(/net/socket.c)—->do_initcalls(main.c)
 
void __init sock_init(void)
{
 int i;
 
 printk(KERN_INFO "Linux NET4.0 for Linux 2.4\n");
 printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n");
 
 /*
  * Initialize all address (protocol) families. 每一项表示的是针对一个地址族的操作集合,例如对于ipv4来说,在net/ipv4/af_inet.c文件中的函数inet_proto_init()就调用sock_register()函数将inet_families_ops初始化到属于IPV4的net_families数组中的一项。
  */
 
 for (i = 0; i < NPROTO; i++)
  net_families = NULL;  
 
 /*
  * Initialize sock SLAB cache.初始化对于sock结构预留的内存的slab缓存。
  */
 
 sk_init();
 
#ifdef SLAB_SKB
 /*
  * Initialize skbuff SLAB cache 初始化对于skbuff结构的slab缓存。以后对于skbuff的申请可以通过函数kmem_cache_alloc()在这个缓存中申请空间。
  */
 skb_init();
#endif
 
 /*
  * Wan router layer.
  */
 
#ifdef CONFIG_WAN_ROUTER 
 wanrouter_init();
#endif
 
 /*
  * Initialize the protocols module. 向系统登记sock文件系统,并且将其安装到系统上来。
  */
 
 register_filesystem(&sock_fs_type);
 sock_mnt = kern_mount(&sock_fs_type);
 /* The real protocol initialization is performed when
  *  do_initcalls is run. 
  */
 /*
  * The netlink device handler may be needed early.
  */
 
#ifdef CONFIG_NET
 rtnetlink_init();
#endif
#ifdef CONFIG_NETLINK_DEV
 init_netlink();
#endif
#ifdef CONFIG_NETFILTER
 netfilter_init();
#endif
 
#ifdef CONFIG_BLUEZ
 bluez_init();
#endif
 
/*yfhuang ipsec*/
#ifdef CONFIG_IPSEC            
 pfkey_init();
#endif
/*yfhuang ipsec*/
}
 
 
    2.2 do_initcalls() 中做了其它的初始化,其中包括
 
                协议初始化,路由初始化,网络接口设备初始化
 
(例如inet_init函数以_init开头表示是系统初始化时做,函数结束后跟module_init(inet_init),这是一个宏,在include/linux/init.c中定义,展开为_initcall(inet_init),表示这个函数在do_initcalls被调用了)
 
    2.3 协议初始化
此处主要列举inet协议的初始化过程。
 
static int __init inet_init(void)
{
 struct sk_buff *dummy_skb;
 struct inet_protocol *p;
 struct inet_protosw *q;
 struct list_head *r;
 
 printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0\n");
 
 if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
  printk(KERN_CRIT "inet_proto_init: panic\n");
  return -EINVAL;
 }
 
 /*
  * Tell SOCKET that we are alive… 注册socket,告诉socket inet类型的地址族已经准备好了
  */
  
   (void) sock_register(&inet_family_ops);
 
 /*
  * Add all the protocols. 包括arp,ip、ICMP、UPD、tcp_v4、tcp、igmp的初始化,主要初始化各种协议对应的inode和socket变量。
 
其中arp_init完成系统中路由部分neighbour表的初始化
 
ip_init完成ip协议的初始化。在这两个函数中,都通过定义一个packet_type结构的变量将这种数据包对应的协议发送数据、允许发送设备都做初始化。
  */
 
 printk(KERN_INFO "IP Protocols: ");
 for (p = inet_protocol_base; p != NULL;) {
  struct inet_protocol *tmp = (struct inet_protocol *) p->next;
  inet_add_protocol(p);
  printk("%s%s",p->name,tmp?", ":"\n");
  p = tmp;
 }
 
 /* Register the socket-side information for inet_create. */
 for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
  INIT_LIST_HEAD(r);
 
 for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
  inet_register_protosw(q);
 
 /*
  * Set the ARP module up 
  */
 
 arp_init();
 
   /*
    * Set the IP module up
    */
 
 ip_init();
 
 tcp_v4_init(&inet_family_ops);
 
 /* Setup TCP slab cache for open requests. */
 tcp_init();
 /*
  * Set the ICMP layer up
  */
 
 icmp_init(&inet_family_ops);
 
 /* I wish inet_add_protocol had no constructor hook…
    I had to move IPIP from net/ipv4/protocol.c 🙁 –ANK
  */
#ifdef CONFIG_NET_IPIP
 ipip_init();
#endif
#ifdef CONFIG_NET_IPGRE
 ipgre_init();
#endif
 
 /*
  * Initialise the multicast router
  */
#if defined(CONFIG_IP_MROUTE)
 ip_mr_init();
#endif
 
 /*
  * Create all the /proc entries.
  */
#ifdef CONFIG_PROC_FS
 proc_net_create ("raw", 0, raw_get_info);
 proc_net_create ("netstat", 0, netstat_get_info);
 proc_net_create ("snmp", 0, snmp_get_info);
 proc_net_create ("sockstat", 0, afinet_get_info);
 proc_net_create ("tcp", 0, tcp_get_info);
 proc_net_create ("udp", 0, udp_get_info);
#endif  /* CONFIG_PROC_FS */
 
 ipfrag_init();
 
 return 0;
}   
module_init(inet_init);
                                                 
 
     2.4 路由初始化(包括neighbour表、FIB表、和路由缓存表的初始化工作)
 
            2.4.1 rtcache表 ip_rt_init()函数 在net/ipv4/ip_output中调用,net/ipv4/route.c中定义
 
            2.4.2 FIB初始化 在ip_rt_init()中调用 在net/ipv4/fib_front.c中定义
 
           2.4.3 neigbour表初始化  arp_init()函数中定义 
 
     2.5 网络接口设备初始化
             
             在系统中网络接口都是由一个dev_base链表进行管理的。通过内核的启动方式也是通过这个链表进行操作的。在系统启动之初,将所有内核能够支持的网络接口都初始化成这个链表中的一个节点,并且每个节点都需要初始化出init函数指针,用来检测网络接口设备。然后,系统遍历整个dev_base链表,对每个节点分别调用init函数指针,如果成功,证明网络接口设备可用,那么这个节点就可以进一步初始化,如果返回失败,那么证明该网络设备不存在或是不可用,只能将该节点删除。启动结束之后,在dev_base中剩下的都是可以用的网络接口设备。
 
            2.5.1 do_initcalls—->net_dev_init()(net/core/dev.c)——>ethif_probe()(drivers/net/Space.c,在netdevice{}结构的init中调用,这边ethif_probe是以太网卡针对的调用)
 
 
 
3.网络设备驱动程序(略)
        
 
4.网络连接
 
     4.1 连接的建立和关闭
 
            tcp连接建立的代码如下:
                    server=gethostbyname(SERVER_NAME);
                    sockfd=socket(AF_INET,SOCK_STREAM,0);
                    address.sin_family=AF_INET;
                    address.sin_port=htons(PORT_NUM);
                    memcpy(&address.sin_addr,server->h_addr,server->h_length);
                    connect(sockfd,&address,sizeof(address));
 
       连接的初始化与建立期间主要发生的事情如下:
                      
       1)sys_socket调用:调用socket_creat(),创建出一个满足传入参数family、type、和protocol的socket,调用sock_map_fd()获取一个未被使用的文件描述符,并且申请并初始化对应的file{}结构。
        
       2)sock_creat():创建socket结构,针对每种不同的family的socket结构的初始化,就需要调用不同的create函数来完成。对应于inet类型的地址来说,在网络协议初始化时调用sock_register()函数中完成注册的定义如下:
        struct net_proto_family inet_family_ops={
                PF_INET;
                inet_create
        };所以inet协议最后会调用inet_create函数。
        
       3)inet_create: 初始化sock的状态设置为SS_UNCONNECTED,申请一个新的sock结构,并且初始化socket的成员ops初始化为inet_stream_ops,而sock的成员prot初始化为tcp_prot。然后调用sock_init_data,将该socket结构的变量sock和sock类型的变量关联起来。
 
       4)在系统初始化完毕后便是进行connect的工作,系统调用connect将一个和socket结构关联的文件描述符和一个sockaddr{}结构的地址对应的远程机器相关联,并且调用各个协议自己对应的connect连接函数。对应于tcp类型,则sock->ops->connect便为inet_stream_connect。
 
 
       5)inet_stream_connect: 得到sk,sk=sock->sk,锁定sk,对自动获取sk的端口号存放在sk->num中,并且用htons()函数转换存放在sk->sport中。然后调用sk->prot->connect()函数指针,对tcp协议来说就是tcp_v4_connect()函数。然后将sock->state状态字设置为SS_CONNECTING,等待后面一系列的处理完成之后,就将状态改成SS_CONNECTTED。
 
       6) tcp_v4_connect():调用函数ip_route_connect(),寻找合适的路由存放在rt中。ip_route_connect找两次,第一次找到下一跳的ip地址,在路由缓存或fib中找到,然后第二次找到下一跳的具体邻居,到neigh_table中找到。然后申请出tcp头的空间存放在buff中。将sk中相关地址数据做一些针对路由的变动,并且初始化一个tcp连接的序列号,调用函数tcp_connect(),初始化tcp头,并设置tcp处理需要的定时器。一次connect()建立的过程就结束了。
 
       连接的关闭主要如下:
 
        1)close: 一个socket文件描述符对应的file{}结构中,有一个file_operations{}结构的成员f_ops,它的初始化关闭函数为sock_close函数。
 
        2)sock_close:调用函数sock_release(),参数为一个socket{}结构的指针。
 
        3)sock_release:调用inet_release,并释放socket的指针和文件空间
 
        4)inet_release: 调用和该socket对应协议的关闭函数inet_release,如果是tcp协议,那么调用的是tcp_close;最后释放sk。
 
        4.2 数据发送流程图
 
 
 
各层主要函数以及位置功能说明:
        1)sock_write:初始化msghdr{}结构 net/socket.c
        2)sock_sendmsg:net/socket.c
        3)inet_sendmsg:net/ipv4/af_net.c
        4)tcp_sendmsg:申请sk_buff{}结构的空间,把msghdr{}结构中的数据填入sk_buff空间。net/ipv4/tcp.c
        5)tcp_send_skb:net/ipv4/tcp_output.c
        6)tcp_transmit_skb:net/ipv4/tcp_output.c
        7)ip_queue_xmit:net/ipv4/ip_output.c
        8)ip_queue_xmit2:net/ipv4/ip_output.c
        9)ip_output:net/ipv4/ip_output.c
        10)ip_finish_output:net/ipv4/ip_output.c
        11)ip_finish_output2:net/ipv4/ip_output.c
        12)neigh_resolve_output:net/core/neighbour.c
        13)dev_queue_xmit:net/core/dev.c
 
 
        4.3 数据接收流程图
 
各层主要函数以及位置功能说明:
 
        1)sock_read:初始化msghdr{}的结构类型变量msg,并且将需要接收的数据存放的地址传给msg.msg_iov->iov_base.      net/socket.c
        2)sock_recvmsg: 调用函数指针sock->ops->recvmsg()完成在INET Socket层的数据接收过程.其中sock->ops被初始化为inet_stream_ops,其成员recvmsg对应的函数实现为inet_recvmsg()函数. net/socket.c
        3)sys_recv()/sys_recvfrom():分别对应着面向连接和面向无连接的协议两种情况. net/socket.c
        4)inet_recvmsg:调用sk->prot->recvmsg函数完成数据接收,这个函数对于tcp协议便是tcp_recvmsg net/ipv4/af_net.c
        5)tcp_recvmsg:从网络协议栈接收数据的动作,自上而下的触发动作一直到这个函数为止,出现了一次等待的过程.函数tcp_recvmsg可能会被动地等待在sk的接收数据队列上,也就是说,系统中肯定有其他地方会去修改这个队列使得tcp_recvmsg可以进行下去.入口参数sk是这个网络连接对应的sock{}指针,msg用于存放接收到的数据.接收数据的时候会去遍历接收队列中的数据,找到序列号合适的.
        但读取队列为空时tcp_recvmsg就会调用tcp_v4_do_rcv使用backlog队列填充接收队列.
        6)tcp_v4_rcv:tcp_v4_rcv被ip_local_deliver函数调用,是从IP层协议向INET Socket层提交的"数据到"请求,入口参数skb存放接收到的数据,len是接收的数据的长度,这个函数首先移动skb->data指针,让它指向tcp头,然后更新tcp层的一些数据统计,然后进行tcp的一些值的校验.再从INET Socket层中已经建立的sock{}结构变量中查找正在等待当前到达数据的哪一项.可能这个sock{}结构已经建立,或者还处于监听端口、等待数据连接的状态。返回的sock结构指针存放在sk中。然后根据其他进程对sk的操作情况,将skb发送到合适的位置.调用如下:
 
        TCP包接收器(tcp_v4_rcv)将TCP包投递到目的套接字进行接收处理. 当套接字正被用户锁定,TCP包将暂时排入该套接字的后备队列(sk_add_backlog).这时如果某一用户线程企图锁定该套接字(lock_sock),该线程被排入套接字的后备处理等待队列(sk->lock.wq).当用户释放上锁的套接字时(release_sock,在tcp_recvmsg中调用),后备队列中的TCP包被立即注入TCP包处理器(tcp_v4_do_rcv)进行处理,然后唤醒等待队列中最先的一个用户来获得其锁定权. 如果套接字未被上锁,当用户正在读取该套接字时, TCP包将被排入套接字的预备队列(tcp_prequeue),将其传递到该用户线程上下文中进行处理.如果添加到sk->prequeue不成功,便可以添加到 sk->receive_queue队列中(用户线程可以登记到预备队列,当预备队列中出现第一个包时就唤醒等待线程.)   /net/tcp_ipv4.c
 
        7)ip_rcv、ip_rcv_finish:从以太网接收数据,放到skb里,作ip层的一些数据及选项检查,调用ip_route_input()做路由处理,判断是进行ip转发还是将数据传递到高一层的协议.调用skb->dst->input函数指针,这个指针的实现可能有多种情况,如果路由得到的结果说明这个数据包应该转发到其他主机,这里的input便是ip_forward;如果数据包是给本机的,那么input指针初始化为ip_local_deliver函数./net/ipv4/ip_input.c
 
        8)ip_local_deliver、ip_local_deliver_finish:入口参数skb存放需要传送到上层协议的数据,从ip头中获取是否已经分拆的信息,如果已经分拆,则调用函数ip_defrag将数据包重组。然后通过调用ip_prot->handler指针调用tcp_v4_rcv(tcp)。ip_prot是inet_protocol结构指针,是用来ip层登记协议的,比如由udp,tcp,icmp等协议。 /net/ipv4/ip_input.c
 

linux下socket通信之通信模型

  导读:
  1.Socket简介
  Socket是TCP/IP网络的API,可以用它来开发网络应用程序,Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符
  2.Socket的建立
  int socket(int domain, int type, int protocol)
  函数返回:一个整型的Socket描述符,可以在后面调用它。
  参数说明:
  int domain:指明所使用的协议族, 通常是PF_INET, 表示网络(TCP/IP)协议族说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等).
  AF_UNIX:只能够用于单一的Unix系统进程间通信,
  AF_INET:是针对Internet的,因而可以允许在远程主机之间通信(当我们man socket时发现domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现所以用PF代替了AF,不过我们都可以使用的)
  int type:指定socket的类型, 通常是 SOCK_STREAM 流式Socket这样会提供按顺序的,可靠,双向,面向连接的比特流和SOCK_DGRAM数据报式Socket这样只会提供定长的,不可靠,无连接的通信
  int prottocol:通常为0 由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了
  应用示例:int sockfd = socket(PF_INET, SOCK_STREAM, 0);
  
  3.Socket配置
  Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。
  两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。
  通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket:
  1) 面向连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息。
  2) 无连接socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。
  
  4.Bind()
  Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。
  函数原型:int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
  函数返回:成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。
  参数说明:
  Sockfd:是调用socket函数返回的socket描述符,
  my_addr:是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;
  addrlen:常被设置为sizeof(struct sockaddr)。
  1> struct sockaddr结构类型是用来保存socket信息的:
  struct sockaddr
  {
  unsigned short sa_family; /* 地址族, AF_xxx */
  char sa_data[14]; /* 14 字节的协议地址 */
  };
  sa_family:一般为AF_INET,代表Internet(TCP/IP)地址族;
  sa_data:则包含该socket的IP地址和端口号。
  2>sockaddr_in结构类型:
  struct sockaddr_in
  {
  short int sin_family; /* 地址族 */
  unsigned short int sin_port; /* 端口号 */
  struct in_addr sin_addr; /* IP地址 */
  unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。 */
  };
  这个结构更方便使用。
  指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向ockaddr_in的指针转换为指向sockaddr的指针;或者相反。使用bind函数时,可以自动获得本机IP地址和随机获取一个没有被占用的端口号:系统随机选择一个未被使用的端口号,通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。填入本机IP地址:通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
  注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则不需要转换。计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先: Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。
  下面是几个字节顺序转换函数: (h: host n: network l: long s: short)
  htonl():把32位值从主机字节序转换成网络字节序, 转为高位字节优先
  htons():把16位值从主机字节序转换成网络字节序, 转为高位字节优先
  ntohl():把32位值从网络字节序转换成主机字节序, 从高位字节优先转换
  ntohs():把16位值从网络字节序转换成主机字节序, 从高位字节优先转换
  需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,你可以选择大于1024中的任何一个没有被占用的端口号。
  应用示例:
  A)服务端
  1)建立结构变量
  struct sockaddr_in my_addr;
  int SERVPORT;
  
  2)配置协议族、端口、地址、sin_zero填充位
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(SERVPORT);
  my_addr.sin_addr.s_addr = INADDR_ANY;
  bzero(&(my_addr.sin_zero), 8);
  
  3)把sockfd的本地端口、IP地址、连接协议进行绑定
  if( bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
  {
  perror("bind");
  return 1;
  }
  
  
  B)客户端
  1)建立结构体变量和端口号
  struct sockaddr_in serv_addr;
  struct hostent *host;
  int SERVPORT;
  
  
  //struct hostent *host; 把服务器端IP通过gethostbyname赋给host结构体,如果传入的是域名则转为IP地址再赋值
  if((host = gethostbyname(“www.800hr.com”)) == NULL)
  //或 if((host = gethostbyname(“192.168.0.1”)) == NULL)
  {
  herror("gethostbyname error");
  return 1;
  }
  else
  {//输出IP地址: xxx.xxx.xxx.xxx
  printf("host: %s\n", inet_ntoa(*((struct in_addr*)host->h_addr)));
  }
  
  
  2)建立Socket
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  {
  perror("create sock");
  return 1;
  }
  
  3)给服务端结构变量赋值
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(SERVPORT);
  serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
  bzero(&(serv_addr.sin_zero), 8);
  
  4)连接服务端
  //int connect(int socfd, struct sockaddr *serv_addr, int addrlen)
  进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到达端口
  
  Connect函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。
  面向连接的服务器从不启动一个连接,它只是被动的在协议端口监听客户的请求。
  无连接协议从不建立直接连接。
  
  if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
  {
  perror("create sock");
  return 1;
  }
  
  5.Listen()
  Listen函数使socket处于被动的监听模式,为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。
  函数原型:int listen(int socfd, int backlog)
  参数说明:
  sockfd 是socket()函数返回的socket描述符
  backlog 指定在请求队列中允许的最大请求数, 进入的连接请求将在队列中等待accept它们
  如果一个服务请求到来时,输入队列己满, 此socket将拒绝连接请求,客户收到一个出错信息。
  应用实例:
  int BACKLOG = 20;
  if(listen(sockfd, BACKLOG) == -1)
  {
  perror("listen");
  return 1;
  }
  
  6.accept()
  在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。
  函数原型:int accept(int sockfd, void *addr, int *addrlen);
  参数说明:
  sockfd 被监听的socket描述符
  addr sockaddr_in变量的指针,该变量用来存放请求服务的客户机的信息
  addrlen 通常是sizeof(struct sockaddr_in)
  返回值:-1出错,client_fd 成功
  [注]:当accept函数监视的socket收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的初始socket仍可以继续在以前的 socket上监听,同时可以在新的socket描述符上进行数据传输操作。
  应用实例:
  int client_fd;
  sin_size = sizeof(struct sockaddr_in);
  if((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr.sin_addr, &sin_size)) == -1)
  {
  perror("accept");
  continue;
  }
  
  7.send()
  函数原型:int send(int sockfd, const void *msg, int len, int flags);
  参数列表:
  Sockfd:接收数据的socket方的id
  msg: 要发送数据的指针
  len: 以字节为单位的数据的长度
  flags: 一般为0
  返回值:失败 -1,成功 发送成功的字节数
  应用实例:
  char *msg = "Hello!";
  int len, bytes_sent;
  。。。
  len = strlen(msg);
  bytes_sent = send(client_fd, msg,len,0);
  。。。
  
  8.recv()
  函数原型:int recv(int sockfd, void *buf, int len, unsigned int flags);
  参数列表:
  Sockfd:接收数据的socket的fd
  Buf: 存放数据的数据缓冲区
  Len: len是缓冲的长度
  flags: 通常为0
  返回值:成功 实际接收的字节数,错误 -1。
  应用实例:
  while(1)
  {
  recvbytes = recv(sockfd, buf, MAXDATASIZE, 0);
  if(recvbytes <= 0)
  break;
  fwrite(buf,1,recvbytes, fp);
  };
  
  recv和send
  recv和send函数提供了和read和write差不多的功能.不过它们提供 了第四个参数来控制读写操作.
  int recv(int sockfd,void *buf,int len,int flags)
  int send(int sockfd,void *buf,int len,int flags)
  前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合
  MSG_DONTROUTE:不查找路由表
  MSG_OOB:接受或者发送带外数据
  MSG_PEEK:查看数据,并不从系统缓冲区移走数据
  MSG_WAITALL:等待所有数据
  MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程序里面。
  MSG_OOB:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的。
  MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志。
  MSG_WAITALL:是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误。
  1)当读到了指定的字节时,函数正常返回。返回值等于len
  2)当读到了文件的结尾时,函数正常返回.返回值小于len
  3) 当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)。
  如果flags为0,则和read,write一样的操作.还有其它的几个选项,不过我们实际上用的很少,可以查看 Linux Programmer’s Manual得到详细解释。
  
  recvfrom和sendto
  这两个函数一般用在非套接字的网络程序当中(UDP),我们已经在前面学会了。
  recvmsg和sendmsg
  recvmsg和sendmsg可以实现前面所有的读写函数的功能.
  int recvmsg(int sockfd,struct msghdr *msg,int flags)
  int sendmsg(int sockfd,struct msghdr *msg,int flags)
  struct msghdr
  {
  void *msg_name;
  int msg_namelen;
  struct iovec *msg_iov;
  int msg_iovlen;
  void *msg_control;
  int msg_controllen;
  int msg_flags;
  }
  
  struct iovec
  {
  void *iov_base; /* 缓冲区开始的地址 */
  size_t iov_len; /* 缓冲区的长度 */
  }
  msg_name和 msg_namelen当套接字是非面向连接时(UDP),它们存储接收和发送方的地址信息.msg_name实际上是一个指向struct sockaddr的指针,msg_name是结构的长度.当套接字是面向连接时,这两个值应设为NULL. msg_iov和 msg_iovlen指出接受和发送的缓冲区内容.msg_iov是一个结构指针,msg_iovlen指出这个结构数组的大小. msg_control和msg_controllen这两个变量是用来接收和发送控制数据时的 msg_flags指定接受和发送的操作选项.和 recv,send的选项一样
  
  9.套接字的关闭
  关闭套接字有两个函数close和shutdown.用close时和我们关闭文件一样.
  shutdown
  int shutdown(int sockfd,int howto)
  TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown.针对不同的howto,系统回采取不同的关闭方式。Howto = 0这个时候系统会关闭读通道.但是可以继续往接字描述符写.,howto=1关闭写通道,和上面相反,着时候就只可以读了。howto=2关闭读写通道,和close一样 在多进程程序里面,如果有几个子进程共享一个套接字时,如果我们使用shutdown, 那么所有的子进程都不能够操作了,这个时候我们只能够使用close来关闭子进程的套接字描述符.
  [附]
  Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址。
  sendto()函数原型为:int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
  该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
  Recvfrom()函数原型为:int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
  from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。
  Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。
  如果你对数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。
  
  10.结束传输
  当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd)。
  你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。
  int shutdown(int sockfd,int how);
  Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式: 0 不允许继续接收数据,1 不允许继续发送数据,2 不允许继续发送和接收数据,均为允许则调用close ()
  shutdown在操作成功时返回0,在出现错误时返回-1并置相应errno。
  
  11.IP和域名的转换
  在网络上标志一台机器可以用IP或者是用域名.那么我们怎么去进行转换呢?
  struct hostent *gethostbyname(const char *hostname)
  struct hostent *gethostbyaddr(const char *addr,int len,int type)
  在中有struct hostent的定义
  struct hostent
  {
  char *h_name; /* 主机的正式名称 */
  char *h_aliases; /* 主机的别名 */
  int h_addrtype; /* 主机的地址类型 AF_INET*/
  int h_length; /* 主机的地址长度 对于IP4 是4字节32位*/
  char **h_addr_list; /* 主机的IP地址列表 */
  #define h_addr h_addr_list[0] /* 主机的第一个IP地址*/
  }
  gethostbyname:可以将机器名(如 linux.yessun.com)转换为一个结构指针,在这个结构里面储存了域名的信息。
  gethostbyaddr:可以将一个32位的IP地址(C0A80001)转换为结构指针。
  这两个函数失败时返回NULL 且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息。

mysql 的 unauthenticated user 现象

show processlist查看有大量如下信息,造成mysql假死.

| 364 | unauthenticated user | xxx.xxx.xxx.xxx:63249 | NULL | Connect |    | login | NULL          |
| 365 | unauthenticated user | xxx.xxx.xxx.xxx:56768 | NULL | Connect |    | login | NULL          |
| 366 | unauthenticated user | xxx.xxx.xxx.xxx:54127 | NULL | Connect |    | login | NULL          |
| 367 | unauthenticated user | xxx.xxx.xxx.xxx:51060 | NULL | Connect |    | login | NULL          |

看下手册中的解释是:unauthenticated user refers to a thread that has become associated with a client connection but for which authentication of the client user has not yet been done。意即:有一个线程在处理客户端的连接,但是该客户端还没通过用户验证。
原因可能有:
1、 服务器在做DNS反响解析,解决办法有2:
1、) 在 hosts 中添加客户端ip,如
192.168.0.1   yejr
2、) MySQL启动参数增加一个skip-name-resolve,即不启用DNS反响解析;或者在/etc/my.cnf里添加一行skip-name-resolve
2、服务器的线程还处于排队状态,因此可以加大 back_log

然后重启mysql就会发现unauthenticated user 全部消失

[手册]

7.5.10. How MySQL Uses DNS

When a new client connects to mysqld, mysqld spawns a new thread to handle the request. This thread first checks whether the hostname is in the hostname cache. If not, the thread attempts to resolve the hostname:

        

  •     

    If the operating system supports the thread-safe gethostbyaddr_r() and gethostbyname_r() calls, the thread uses them to perform hostname resolution.

        

  •     

  •     

    If the operating system does not support the thread-safe calls, the thread locks a mutex and calls gethostbyaddr() and gethostbyname() instead. In this case, no other thread can resolve hostnames that are not in the hostname cache until the first thread unlocks the mutex.

        

You can disable DNS hostname lookups by starting mysqld with the --skip-name-resolve option. However, in this case, you can use only IP numbers in the MySQL grant tables.

If you have a very slow DNS and many hosts, you can get more performance by either disabling DNS lookups with --skip-name-resolve or by increasing the HOST_CACHE_SIZE define (default value: 128) and recompiling mysqld.

You can disable the hostname cache by starting the server with the --skip-host-cache option. To clear the hostname cache, issue a FLUSH HOSTS statement or execute the mysqladmin flush-hosts command.

To disallow TCP/IP connections entirely, start mysqld with the --skip-networking option.

HTTP缓存协议原理解析及应用指南

http协议里控制浏览器缓存的头有三个Cache-Control,Expires,Last-Modified
对于静态页面还有Etag。
一、先来看第一种情况:apache 静态页面
apache发送给客户端的静态页面一般包含Last-Modified和Etag,这两个标签的值来自静态文件的修改时间和inode,
下面是截取得apache返回客户端的头
———
Last-Modified: Fri, 26 Jan 2007 01:53:34 GMT
ETag: "3f9f640-318-cb9f8380"
———
搜索引擎之所以喜欢静态文件是因为有这两个标识,可以判断文件是否更新过

二、PHP等动态页面
由于php是动态生成的,它的内容是不能根据php程序的时间来确定最后修改日期,所以默认php返回客户端的时候补包含任何缓存控制,要想利用好缓存就必须了解缓存机制,和理减少b,s的交互,缩减带宽流量,减轻服务器负担…好处多多

三、缓存控制的具体含义
先解释一下本人经过测试理解的这几个标签的含义
Cache-Control:指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如下:
Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
php用法:
在输出之前用header(),(如果使用ob_start()可以将header放在程序任意地方)
header(‘Cache-Control: max-age=8’);
max-age=8表示最大生存期8秒,超过8秒浏览器必须去服务器重新读取,这个时间是以用户的读取页面开始计时的,而Expires是绝对时间。
Expires:缓存过期的绝对时间,如果过了它指定的那个时间点,浏览器就不认缓存了,要去服务器重新请求一份最新的。

Last-Modified:文档的最后修改时间,它的妙用就是:1 如果是静态文件,客户端会发上来它缓存里的时间,apache会来比对,如果发现没有修改就直接返回一个头,状态码是304,字节数非常少,(高级版本还会增加比较Etag来确定文件是否变化)
2 php动态文件: 客户端发上比对时间,php会判断是否修改,如果修改时间相同,就只会返回1024字节,至于为什么返回1024不得而知,如果你的php生成的文件非常大,它也只返回1024,所以比较省带宽,客户端会根据服务器端发过来的修改时间自动从缓存文件里显示。

注:如果没有Last-Modified头,Cache-Control和Expires也是可以起作用的,但每次请求要返回真实的文件字节数,而不是1024

四、HOW ?
静态页面不用去管它了,如果想更好的控制静态页面的缓存,apache有几个模块可以很好的控制,这里不讨论
php页面:
这里分两种:1 不经常改动的页面,类似新闻发布,这类页面的特点:第一次发布之后会有几次改动,随着时间推移基本不会再修改。控制策略应该是:1第一次发布之发送Last-Modified,max-age设定1天,修改过之后更新Last-Modified,max-age时间随着修改次数正常。这样似乎比较繁琐,还要记录修改次数,也可以预计一下下次可能的修改时间用Expires指定到大概时间过期
php代码:
//header(‘Cache-Control: max-age=86400’);//缓存一天
header(‘Expires: Mon, 29 Jan 2007 08:56:01 GMT’);//指定过期时间
header(‘Last-Modified: ‘.gmdate(‘D, d M Y 01:01:01′,$time).’ GMT’);//格林尼治时间,$time是文件添加时候的时间戳
2 经常改动的页面  类似bbs,论坛程序,这种页面更新速度比较快,缓存的主要作用是防止用户频繁刷新列表,导致服务器数据库负担,既要保证更新的及时性,也要保证缓存能被利用
这里一般用Cache-Control来控制,根据论坛的发帖的频率灵活控制max-age。
header(‘Cache-Control: max-age=60’);//缓存一分钟
header(‘Last-Modified: ‘.gmdate(‘D, d M Y 01:01:01′,$time).’ GMT’);//格林尼治时间,$time是帖子的最后更新时间戳

五 额外
1 刷新,转到,强制刷新的区别
浏览器上有刷新和转到按键,有的浏览器支持用ctrl+F5强制刷新页面,它们的区别是什么?
转到:用户点击链接就是转到,它完全使用缓存机制,如果有Last-Modified那么不会和服务器通讯,用抓包工具可以查看到发送字节是0byte,如果缓存过期,那么它会执行F5刷新的动作。
刷新(F5):这种刷新也是根据缓存是否有Last-Modified来决定,如果有会转入304或1024(php),如果没有最后更新时间那么去服务器读取,返回真实文档大小
强制刷新:完全抛弃缓存机制,去服务器读取最新文档,向服务器发送的header如下
Cache-Control: no-cache

2 调试工具
查看浏览器和服务器交互比较好的工具是httpwatch pro,现在的版本4.1,支持ie7
还有别的代理抓包工具可以分析,http debugging。没用过,还有tcp抓包工具,2000自带的network monitor不过不是专门针对http的比较难用

六 声明
本文作者保留所有权力,允许被自由查看和转载,但必须指明作者(Ash)和源网址(www.cosrc.com);不允许商用

 

相关资料:
http://www.zgkw.cn/FORUMS/forums/59660/ShowThread.aspx
http://www.javaeye.com/topic/119336
http://www.happyshow.org/article/209.html

开心果

爹跟儿子说:我要给你找个媳妇。
儿子说,可我愿意自己找! 
爹说,但这个女孩子是比尔盖茨女儿!
儿子说,要是这样,可以。
然后他爹找到比尔盖茨,说,我给你女儿找了一个老公。
比尔盖茨说,不行,我女儿还小!
爹说,可是这个小伙子是世界银行副总裁! 
比尔盖茨说,啊,这样,行!
最后,爹找到了世界银行总裁,说,我给你推荐一个副总裁!
总裁说,可是我有太多副总裁了,多余了!
爹说,可是这个小伙子是比尔盖茨的女婿! 
总裁说,这样,行!
               —-生意是这样做成的

Apache 2.0中prefork.c模块和worker.c模块的比较

Apache 2.XX中prefork.c模块和worker.c模块的比较

空闲子进程:是指没有正在处理请求的子进程。

1、prefork.c模块(一个非线程型的、预派生的MPM)
prefork MPM 使用多个子进程,每个子进程只有一个线程。每个进程在某个确定的时间只能维持一个连接。在大多数平台上,Prefork MPM在效率上要比Worker MPM要高,但是内存使用大得多。prefork的无线程设计在某些情况下将比worker更有优势:它可以使用那些没有处理好线程安全的第三方模块,并且对于那些线程调试困难的平台而言,它也更容易调试一些。

<IfModule prefork.c>
ServerLimit 20000
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 1000
MaxRequestsPerChild 0
</IfModule>

ServerLimit 2000
//默认的MaxClient最大是256个线程,如果想设置更大的值,就的加上ServerLimit这个参数。20000是ServerLimit这个参数的最大值。如果需要更大,则必须编译apache,此前都是不需要重新编译Apache。
生效前提:必须放在其他指令的前面

StartServers 5
//指定服务器启动时建立的子进程数量,prefork默认为5。

MinSpareServers 5
//指定空闲子进程的最小数量,默认为5。如果当前空闲子进程数少于MinSpareServers ,那么Apache将以最大每秒一个的速度产生新的子进程。此参数不要设的太大。

MaxSpareServers 10
//设置空闲子进程的最大数量,默认为10。如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程将杀死多余的子进程。此参数不要设的太大。如果你将该指令的值设置为比MinSpareServers小,Apache将会自动将其修改成"MinSpareServers+1"。

MaxClients 256
//限定同一时间客户端最大接入请求的数量(单个进程并发线程数),默认为256。任何超过MaxClients限制的请求都将进入等候队列,一旦一个链接被释放,队列中的请求将得到服务。要增大这个值,你必须同时增大ServerLimit 。

MaxRequestsPerChild 10000
//每个子进程在其生存期内允许伺服的最大请求数量,默认为10000.到达MaxRequestsPerChild的限制后,子进程将会结束。如果MaxRequestsPerChild为"0",子进程将永远不会结束。

将MaxRequestsPerChild设置成非零值有两个好处:
1.可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。
2.给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。

工作方式:
一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。Apache总是试图保持一些备用的(spare)或者是空闲的子进程用于迎接即将到来的请求。这样客户端就不需要在得到服务前等候子进程的产生。在Unix系统中,父进程通常以root身份运行以便邦定80端口,而Apache产生的子进程通常以一个低特权的用户运行。User和Group指令用于设置子进程的低特权用户。运行子进程的用户必须要对它所服务的内容有读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。

2、worker.c模块(支持混合的多线程多进程的多路处理模块)
worker MPM 使用多个子进程,每个子进程有多个线程。每个线程在某个确定的时间只能维持一个连接。通常来说,在一个高流量的HTTP服务器上,Worker MPM是个比较好的选择,因为Worker MPM的内存使用比Prefork MPM要低得多。但worker MPM也由不完善的地方,如果一个线程崩溃,整个进程就会连同其所有线程一起"死掉".由于线程共享内存空间,所以一个程序在运行时必须被系统识别为"每个线程都是安全的"。

<IfModule worker.c>
ServerLimit 50
ThreadLimit 200
StartServers 5
MaxClients 5000
MinSpareThreads 25
MaxSpareThreads 500
ThreadsPerChild 100
MaxRequestsPerChild 0
</IfModule>

ServerLimit 16
//服务器允许配置的进程数上限。这个指令和ThreadLimit结合使用设置了MaxClients最大允许配置的数值。任何在重启期间对这个指令的改变都将被忽略,但对MaxClients的修改却会生效。

ThreadLimit 64
//每个子进程可配置的线程数上限。这个指令设置了每个子进程可配置的线程数ThreadsPerChild上限。任何在重启期间对这个指令的改变都将被忽略,但对ThreadsPerChild的修改却会生效。默认值是"64".

StartServers 3
//服务器启动时建立的子进程数,默认值是"3"。

MinSpareThreads 75
//最小空闲线程数,默认值是"75"。这个MPM将基于整个服务器监视空闲线程数。如果服务器中总的空闲线程数太少,子进程将产生新的空闲线程。

MaxSpareThreads 250
//设置最大空闲线程数。默认值是"250"。这个MPM将基于整个服务器监视空闲线程数。如果服务器中总的空闲线程数太多,子进程将杀死多余的空闲线程。MaxSpareThreads的取值范围是有限制的。Apache将按照如下限制自动修正你设置的值:worker要求其大于等于MinSpareThreads加上ThreadsPerChild的和

MaxClients 400
//允许同时伺服的最大接入请求数量(最大线程数量)。任何超过MaxClients限制的请求都将进入等候队列。默认值是"400",16(ServerLimit)乘以25(ThreadsPerChild)的结果。因此要增加MaxClients的时候,你必须同时增加ServerLimit的值。

ThreadsPerChild 25
//每个子进程建立的常驻的执行线程数。默认值是25。子进程在启动时建立这些线程后就不再建立新的线程了。

MaxRequestsPerChild 0
//设置每个子进程在其生存期内允许伺服的最大请求数量。到达MaxRequestsPerChild的限制后,子进程将会结束。如果MaxRequestsPerChild为"0",子进程将永远不会结束。

将MaxRequestsPerChild设置成非零值有两个好处:
1.可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。
2.给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。
注意
对于KeepAlive链接,只有第一个请求会被计数。事实上,它改变了每个子进程限制最大链接数量的行为。

工作方式:
每个进程可以拥有的线程数量是固定的。服务器会根据负载情况增加或减少进程数量。一个单独的控制进程(父进程)负责子进程的建立。每个子进程可以建立ThreadsPerChild数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。Apache总是试图维持一个备用(spare)或是空闲的服务线程池。这样,客户端无须等待新线程或新进程的建立即可得到处理。在Unix中,为了能够绑定80端口,父进程一般都是以root身份启动,随后,Apache以较低权限的用户建立子进程和线程。User和Group指令用于设置Apache子进程的权限。虽然子进程必须对其提供的内容拥有读权限,但应该尽可能给予它较少的特权。另外,除非使用了suexec ,否则,这些指令设置的权限将被CGI脚本所继承。

公式:
ThreadLimit >= ThreadsPerChild
MaxClients <= ServerLimit * ThreadsPerChild 必须是ThreadsPerChild的倍数
MaxSpareThreads >= MinSpareThreads+ThreadsPerChild

硬限制:

ServerLimi和ThreadLimit这两个指令决定了活动子进程数量和每个子进程中线程数量的硬限制。要想改变这个硬限制必须完全停止服务器然后再启动服务器(直接重启是不行的)。

Apache在编译ServerLimit时内部有一个硬性的限制,你不能超越这个限制。
prefork MPM最大为"ServerLimit 200000"
其它MPM(包括work MPM)最大为"ServerLimit 20000

Apache在编译ThreadLimit时内部有一个硬性的限制,你不能超越这个限制。
mpm_winnt是"ThreadLimit 15000"
其它MPM(包括work prefork)为"ThreadLimit 20000

注意
使用ServerLimit和ThreadLimit时要特别当心。如果将ServerLimit和ThreadLimit设置成一个高出实际需要许多的值,将会有过多的共享内存被分配。当设置成超过系统的处理能力,Apache可能无法启动,或者系统将变得不稳定。

上帝没给它鱼鳔

   上帝造了一群鱼,种类多样,大小各异。它们的身体被做成流线型,而且十分光滑,还拥有短而有力的鳍,这样就能在大海中自由自在地游动。

这些鱼被放入大海后,上帝忽然想起一个问题,鱼的身体比重大于水,它们一旦停止游动,就会下沉,很可能被水压死。

上帝赶紧找到这些鱼,又给了它们一个法宝——鱼鳔。有了鱼鳔,鱼就轻松多了,它们不但随意沉浮,还可以停在某地休息。鱼鳔对于鱼来讲,实在太有用了。

但是,上帝费了好大劲也没有找到鲨鱼。鲨鱼是个调皮的家伙,它一入海,便撒着欢儿地跑远了。上帝想,既然找不到鲨鱼,那就算了,它会由于缺少鳔而沦为海洋中的弱者,最后被淘汰。

亿万年后,上帝忽然想看看当年放到海中的鱼现在到底如何了。上帝把海里的鱼都找了来,经过亿万年的演变,所有的鱼都变了模样。

面对千姿百态、大大小小的鱼,上帝问:“谁是当初的鲨鱼?”一群威猛强壮的鱼游上前来,它们就是当初的鲨鱼,现在的“海霸王”。上帝十分惊讶,心想当初只有鲨鱼没有鱼鳔,它要比别的鱼多承受多少压力和风险啊,可现在看来,鲨鱼无疑是鱼类中的佼佼者。

鲨鱼说:“我们没有鱼鳔,无时无刻不承受着压力,所以我们就一刻不停地游动,否则就会被水压死。亿万年来,我们从未停止过游动和抗争,这成了我们的生存方式。”

上帝恍然大悟。

   

  我们如这些鱼儿.所以只有生于忧患,不能沉缅安逸,才会变的更加有本事.天大的本事都是被逼而出,这种可怕优势,而这一切源于天生缺少的一件重要的法宝。

佛曰

佛曰:人生有八苦:生,老,病,死,爱别离,怨长久,求不得,放不下。

佛曰:命由己造,相由心生,世间万物皆是化相,心不动,万物皆不动,心不变,万物皆不变。

佛曰:笑着面对,不去埋怨。悠然,随心,随性,随缘。注定让一生改变的,只在百年后,那一朵花开的时间。

佛曰:刹那便是永恒。

佛曰:爱别离,怨憎会,撒手西归,全无是类。不过是满眼空花,一片虚幻。

佛曰:由爱故生忧,由爱故生怖,若离于爱者,无忧亦无怖

佛曰:以物物物,则物可物;以物物非物,则物非物。物不得名之功,名不得物之实,名物不实,是以物无物也。

佛曰:一念愚即般若绝,一念智即般若生。

佛曰。坐亦禅,行亦禅,一花一世界,一叶一如来,春来花自青,秋至叶飘零,无穷般若心自在,语默动静体自然。

佛曰:吾法念无念念。行无行行。言无言言。修无修修。会者近尔。迷者远乎。言语道断。非物所拘。差之毫厘。失之须臾。

佛曰:如人锻铁。去滓成器。器即精好。学道之人。去心垢染。行即清净矣。

佛曰。净心守志。可会至道。譬如磨镜。垢去明存。断欲无求。当得宿命。

佛曰:缘来则去,缘聚则散,缘起则生,缘落则灭。

佛曰:笑着面对,不去埋怨。悠然,随心,随性,随缘。注定让一生改变的,只在百年后,那一朵花开的时间。

佛曰:种如是因,收如是果,一切唯心造。

问曰:“为何人有善恶之分?”
佛曰:“人无善恶,善恶存乎尔心”
问曰:“如何能静?如何能常?”
佛曰:“寻找自我。”
问曰:“世间为何多苦恼?”
佛曰:“只因不识自我。”
问曰:“人为何而活?”
佛曰:“寻根。”
问曰:“何谓之根?”
佛曰:“不可说。”
佛曰:不可说,不可说,一说即是错

“人之一辈子,有生必有死。为生而筹计者,是为生计。若按年龄区分,则一岁至十岁,为生计;二十至三十,为家计;三十至四十,为子孙计,五十至六十,为老计;六十至七十以上,则为死计。从二十至六十这四十年间,营营扰扰,或为功名,或为事业。外则苦其身以事劳攘,内则苦其心以密思虑,既要想目下的周身之防,又要想将来的善后之策,总而言之是劳碌一生。”