• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Linux內核2.4.x的網絡接口源碼的結構(2)

    發表于:2007-05-26來源:作者:點擊數: 標簽:
    四. 網絡 接口核心部分 剛才談論了驅動程序怎么和網絡接口核心層銜接的。網絡接口核心層知道驅動程序以及驅動程序的函數的入口是通過*dev_base指向的設備鏈的,而下層是通過調用這一層的函 .net if_rx()(net/core/dev.c1214行) 把數據傳遞個這一層的。 網絡
    四.網絡接口核心部分
      剛才談論了驅動程序怎么和網絡接口核心層銜接的。網絡接口核心層知道驅動程序以及驅動程序的函數的入口是通過*dev_base指向的設備鏈的,而下層是通過調用這一層的函.netif_rx()(net/core/dev.c1214行) 把數據傳遞個這一層的。
      網絡接口核心層的上層是具體的網絡協議,下層是驅動程序,我們已經解決了下層的關系,但和上層的關系沒有解決。先來討論一下網絡接口核心層和網絡協議族部份的關系,這種關系不外乎也是接收和發送的關系。
      網絡協議,例如IP,ARP等的協議要發送數據包的時候會把數據包傳遞給這層(網絡接口核心層),那么這種傳遞是通過什么函數來發生的呢?網絡接口核心層通過dev_queue_xmit()(net/core/dev.c,line975)這個函數,向上層提供統一的發送接口,也就是說無論是IP,還是ARP協議,通過這個函數把要發送的數據傳遞給這一層,想發送數據的時候調用這個函數就可以了。dev_queue_xmit()做的工作最后會落實到dev->hard_start_xmit(),而dev->hard_start_xmit()會調用實際的驅動程序來完成發送的任務。例如上面的例子中,調用dev->hard_start_xmit()實際就是調用了el_start_xmit()。
      現在討論接收的情況。網絡接口核心層通過的函數netif_rx()(net/core/dev.c 1214行)接收了下層發送來的數據,這時候當然要把數據包往上層派送。所有的協議族的下層協議都需要接收數據,TCP/IP的IP協議和ARP協議,SPX/IPX的IPX協議,AppleTalk的DDP和AARP協議等都需要直接從網絡接口核心層接收數據,網絡接口核心層接收數據是如何把包發給這些協議的呢?這時的情形,與該層和其下層的關系很相似,網絡接口核心層的下面可能有許多的網卡驅動程序,為了知道怎么向這些驅動程序發數據,前面已經講過,是通過*dev_base這個指針指向的鏈解決的?,F在解決和上層的關系,是通過static struct packet_ptype_base[16]( net/core/dev.c line 164)這個數組解決的。這個數組包含了需要接收數據包的協議,以及它們的接收函數的入口。
      從上面可以看到,IP協議接收數據是通過ip_rcv()函數的,而ARP協議是通過arp_rcv()的,網絡接口核心層只要通過這個數組就可以把數據交給上層函數了。
      如果,有協議想把自己添加到這個數組,是通過dev_add_pack()(net/core/dev.c, line233)函數添加;從數組刪除,則是通過dev_remove_pack()函數的。Ip層的注冊是在初始化函數進行的void __initip_init(void) (net/ipv4/ip_output.c, line 1003)
      {
      ………
      dev_add_pack(&ip_packet_type);
      ………
      }
      重新倒回我們關于接收的討論,網絡接include口核心層通過的函數netif_rx()(net/core/dev.c 1214行)接收了上層發送來的數據,看看這個函數做了些什么。
      由于現在還是在中斷的服務里面,所以并不能夠處理太多的東西,剩下的東西就通過cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ)交給軟中斷處理, 從open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL)可以知道NET_RX_SOFTIRQ軟中斷的處理函數是net_rx_action()(net/core/dev.c, line 1419)。net_rx_action()根據數據包的協議類型在數組ptype_base[16]里找到相應的協議,并從中知道了接收的處理函數,然后把數據包交給處理函數,這樣就交給了上層處理,實際調用處理函數是通過net_rx_action()里的pt_prev->func()這一句。例如如果數據包是IP協議的話,ptype_base[ETH_P_IP]->func()(ip_rcv()),這樣就把數據包交給了IP協議。

    五.網絡協議部分
      協議層是真正實現是在這一層。在linux/include/linux/socket.h里面,Linux的BSD Socket定義了多至32種支持的協議族,其中PF_INET就是我們最熟悉的TCP/IP協議族(IPv4, 以下沒有特別聲明都指IPv4)。以這個協議族為例,看看這層是怎么工作的。實現TCP/IP協議族的主要文件在Linux/net/ipv4/目錄下面,Linux/net/ipv4/af_inet.c為主要的管理文件。
      在Linux2.4.16里面,實現了TCP/IP協議族里面的的IGMP,TCP,UDP,ICMP,ARP,IP。我們先討論一下這些協議之間的關系。IP和ARP協議是需要直接和網絡設備接口打交道的協議,也就是需要從網絡核心模塊(core)接收數據和發送數據的。而其它協議TCP,UDP,IGMP,ICMP是需要直接利用IP協議的,需要從IP協議接收數據,以及利用IP協議發送數據,同時還要向上層Socket層提供直接的調用接口??梢钥吹絀P層是一個核心的協議,向下需要和下層打交道,又要向上層提供所有的傳輸和接收的服務。
      先來看看IP協議層。網絡核心模塊(core) 如果接收到IP層的數據,通過ptype_base[ETH_P_IP] 數組的IP層的項指向的IP協議的ip_packet_type->ip_rcv()函數把數據包傳遞給IP層,也就是說IP層通過這個函數ip_rcv()(linux/net/ipv4/ip_input.c)接收數據的。ip_rcv()這個函數只對IP數據包做了一些checksum的檢查工作,如果包是正確的,就把包交給了下一個處理函數ip_rcv_finish()(注意調用是通過NF_HOOK這個宏實現的)?,F在,ip_rcv_finish()這個函數真正要完成一些IP層的工作了。IP層要做的主要工作就是路由,要決定把數據包往那里送。路由的工作是通過函數ip_route_input()(/linux/net/ipv4/route.c,line 1622)實現的。對于進來的包可能的路由有這些:
      屬于本地的數據(即是需要傳遞給TCP,UDP,IGMP這些上層協議的) ;
      需要轉發的數據包(網關或者NAT服務器之類的);
      不可能路由的數據包(地址信息有誤);
      我們現在關心的是如果數據是本地數據的時候怎么處理。ip_route_input()調用ip_route_input_slow()(net/ipv4/route.c, line 1312),在ip_route_input_slow()里面
    的1559行rth->u.dst.input=ip_local_deliver,這就是判斷到IP包是本地的數據包,并把本地數據包處理函數的地址返回。好了,路由工作完成了,返回到ip_rcv_finish()。ip_rcv_finish()最后調用了skb->dst->input(skb),從上面可以看到,這其實就是調用了ip_local_deliver()函數,而ip_local_deliver()接著就調用了ip_local_deliver_finish()?,F在真正到了往上層傳遞數據包的時候了。
      現在的情形和網絡核心模塊層(core) 往上層傳遞數據包的情形非常相似,怎么從多個協議選擇合適的協議,并且往這個協議傳遞數據呢?網絡網絡核心模塊層(core) 通過一個數組ptype_base[16]保存了注冊了的所有可以接收數據的協議,同樣網絡協議層也定義了這樣一個數組struct net_protocol*inet_protos[MAX_INET_PROTOS](/linux/net/ipv4/protocol.c#L102),它保存了所有需要從IP協議層接收數據的上層協議(IGMP,TCP,UDP,ICMP)的接收處理函數的地址。我們來看看TCP協議的數據結構是怎么樣的:
    /*   linux/net/ipv4/protocol.c line67 */

      static struct inet_protocol tcp_protocol = {
      handler: tcp_v4_rcv,// 接收數據的函數
      err_handler: tcp_v4_err,// 出錯處理的函數
      next: IPPROTO_PREVIOUS,
      protocol: IPPROTO_TCP,
      name: "TCP"
      };
      第一項就是我們最關心的了,IP層可以通過這個函數把數據包往TCP層傳的。在linux/net/ipv4/protocol.c的上部,我們可以看到其它協議層的處理函數是igmp_rcv(),udp_rcv(), icmp_rcv()。同樣在linux/net/ipv4/protocol.c,往數組inet_protos[MAX_INET_PROTOS] 里面添加協議是通過函數inet_add_protocol()實現的,刪除協議是通過 inet_del_protocol()實現的。inet_protos[MAX_INET_PROTOS]初始化的過程在linux/net/ipv4/af_inet.c inet_init()初始化函數里面。
      inet_init(){
      ……
      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;
      ………
      }
      如果你在Linux啟動的時候有留意啟動的信息, 或者在linux下打命令dmesg就可以看到這一段程序輸出的信息:
      IP Protocols: ICMP,UDP,TCP,IGMP也就是說現在數組inet_protos[]里面有了ICMP,UDP,TCP,IGMP四個協議的inet_protocol數據結構,數據結構包含了它們接收數據的處理函數。
      Linux 2.4.16在linux/include/linux/socket.h里定義了32種支持的BSD socket協議
    ,常見的有TCP/IP,IPX/SPX,X.25等,而每種協議還提供不同的服務,例如TCP/IP協議通過TCP協議支持連接服務,而通過UDP協議支持無連接服務,面對這么多的協議,向用戶提供統一的接口是必要的,這種統一是通過socket來進行的。
      在BSD socket網絡編程的模式下,利用一系列統一的函數來利用通信的服務。例如一個典型的利用TCP協議通信程序是這樣:
      sock_descriptor = socket(AF_INET,SOCK_STREAM,0);
      connect(sock_descriptor, 地址,) ;
      send(sock_descriptor,”hello world”);
      recv(sock_descriptor,buffer,1024,0);
      第一個函數指定了協議Inet協議,即TCP/IP協議,同時是利用面向連接的服務,這樣就對應到TCP協議,以后的操作就是利用socket的標準函數進行的。
      從上面我們可以看到兩個問題,首先socket層需要根據用戶指定的協議族(上面是AF_INET),從下面32種協議中選擇一種協議來完成用戶的要求,當協議族確定以后,還要把特定的服務映射到協議族下的具體協議,例如當用戶指定的是面向連接的服務時,Inet協議族會映射到TCP協議。
      從多個協議中選擇用戶指定的協議,并把具體的出理交給選中的協議,這和網絡核心層向上和向下銜接的問題本質上是一樣的,所以解決的方法也是一樣的,同樣還是通過數組。在Linux/net/socket.c定義了這個數組staticstruct net_proto_family *net_families[NPROTO] 。數組的元素已經確定了,net_families[2] 是TCP/IP協議,net_families[3]是X.25協議,具體那一項對應什么協議,在include/linux/socket.h有定義。但是每一項的數據結構net_proto_family的ops是空的,也就是具體協議處理函數的地址是不知道的。協議的處理函數和ops建立聯系是通過sock_register()(Linux/net/socket.c)這個函數建立的,例如TCP/IP協議的是這樣建立關系的:
      int __init inet_init(void) /* (net/ipv4/af_inet.c) */
      {
      (void) sock_register(&inet_family_ops);
      }
      只要給出AF_INET(在宏里定義是2),就可以找到net_failies[2] 里面的處理函數了 。
      協議的映射完成了,現在要進行服務的映射了。上層當然不可能知道下層的什么協議能對應特定的服務,所以這種映射自然由協議族自己完成。在TCP/IP協議族里,這種映射是通過struct list_head inetsw[SOCK_MAX]( net/ipv4/af_inet.c)這個數組進行映射的,在談論這個數組之前我們來看另外一個數組inetsw_array[] (net/ipv4/af_inet.c)
      static struct inet_protosw inetsw_array[] =
      {
       {
       type: SOCK_STREAM,
       protocol: IPPROTO_TCP,
       prot: &tcp_prot,
       ops: &inet_stream_ops,
       capability: -1,
       no_check: 0,
       flags: INET_PROTOSW_PERMANENT,
       },
       {
       type: SOCK_DGRAM,
       protocol: IPPROTO_UDP,
       prot: &udp_prot,
       ops: &inet_dgram_ops,
       capability: -1,
       no_check: UDP_CSUM_DEFAULT,
       flags: INET_PROTOSW_PERMANENT,
       },
       {
       type: SOCK_RAW,
       protocol: IPPROTO_IP, /* wild card */
       prot: &raw_prot,
       ops: &inet_dgram_ops,
       capability: CAP_NET_RAW,
       no_check: UDP_CSUM_DEFAULT,
       flags: INET_PROTOSW_REUSE,
       }
      };
      我們看到,SOCK_STREAM映射到了TCP協議,SOCK_DGRAM映射到了UDP協議,SOCK_RA
    W映射到了IP協議?,F在只要把inetsw_array里的三項添加到數組inetsw[SOCK_MAX]就可
    以了,添加是通過函數inet_register_protosw()實現的。在inet_init()(net/ipv4/af_inet.c) 里完成了這些工作。
      還有一個需要映射的就是socket其它諸如aclearcase/" target="_blank" >ccept,send(),connect(),release(),bind()等的操作函數是怎么映射的呢?我們來看一下上面的數組的TCP的項:
      {
      type: SOCK_STREAM,
      protocol: IPPROTO_TCP,
      prot: &tcp_prot,
      ops: &inet_stream_ops,
      capability: -1,
      no_check: 0,
      flags: INET_PROTOSW_PERMANENT,
      },
      我們看到這種映射是通過ops,和prot來映射的,我們再來看看 tcp_prot這一項:

      struct proto tcp_prot = {
      name: "TCP",
      close: tcp_close,
      connect: tcp_v4_connect,
      disconnect: tcp_disconnect,
      accept: tcp_accept,
      ioctl: tcp_ioctl,
      init: tcp_v4_init_sock,
      destroy: tcp_v4_destroy_sock,
      shutdown: tcp_shutdown,
      setsockopt: tcp_setsockopt,
      getsockopt: tcp_getsockopt,
      sendmsg: tcp_sendmsg,
      recvmsg: tcp_recvmsg,
      backlog_rcv: tcp_v4_do_rcv,
      hash: tcp_v4_hash,
      unhash: tcp_unhash,
      get_port: tcp_v4_get_port,
      };
      所以的映射都已經完成了,用戶調用connect()函數,其實就是調用了tcp_v4_connect()函數,按照這幅圖,讀起源碼來就簡單了很多了。

    六 Socket層
      上一節把socket層大多數要討論的東西都談論了,現在只講講socket 層和用戶的銜接。
      系統調用socket(),bind(),connect(),accept,send(),release()等是在Linux/net/socket.c里面實現的,系統調用實現的函數是相應的函數名加上sys_的前綴。
      現在看看當用戶調用socket()這個函數,到底下面發生了什么。
      Socket(AF_INET,SOCK_STREAM,0)調用了sys_socket(),sys_socket()接著調用socket_creat(),socket_creat()就要根據用戶提供的協議族參數在net_families[]里尋找合適的協議族,如果協議族沒有被安裝就要請求安裝該協議族的模塊,然后就調用該協議族的create()函數的處理句柄。根據參數AF_INET,inet_creat()就被調用了,在inet_creat()根據服務類型在inetsw[SOCK_MAX]選擇合適的協議,并把協議的操作集賦給socket就是了,根據SOCK_STREAM,TCP協
    議被選中,
      inet_creat(){
      answer=inetsw [用戶要求服務] ;
      sock->ops = answer->ops;
      sk->prot = answer->prot
      }
      到此為止,上下都打通了,該是大家讀源碼的時候了。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>