ping命令执行过程一

来源:互联网 发布:淘宝图片像素要求 编辑:程序博客网 时间:2024/05/17 09:07

一、简介

这里打算从U-Boot的ping命令说起。ping命令是用于测试网络是否和目标网络畅通简单工具,

在U-Boot中ping命令的使用方法是:

ping   比如我电脑的IP地址为192.168.1.10,ping命令使用如下:

ping 192.168.1.10,如果开发板和目标IP之间的通信畅通的话,将输出如下信息: 

host 192.168.1.100 is alive

 

否则显示: 

ping failed;

host 192.168.1.100 is not alive  

二、ping命令介绍

U-Boot中加入ping命令的代码如下:

U_BOOT_CMD(ping,// 命令名称

             2, //参数个数

             1,

             do_ping, //命令执行函数

             "send ICMP ECHO_REQUEST to network host",//长帮助说明

             "pingAddress" );   //短帮助说明

显然,我们需要分析do_ping函数

三、ping函数

   

int do_ping (cmd_tbl_t *cmdtp,

            int flag,

            int argc,//参数3和参数4是和main函数一样的参数

            char *argv[])//我们在使用ping命令的时候的格式如下:ping 192.168.1.10

                         //因此这里的argv[1]就是我们的IP地址的字符串格式了

    

       if (argc < 2) 该命令的参数至少应该为2 ,如果参数个数小于2则直接返回     

        return -1;      

       NetPingIP = string_to_ip(argv[1]);   //该函数将主机的字

                                            //符串ip地址转换成点分ip地址 

       if (NetPingIP == 0)//NetPingIP 是一个全局变量,定义在net/net.c中,

                          //如:IPaddr_t    NetPingIP; 

                          //而IPaddr_t 在 include/net.h文件中定义,

                          //定义如下:typedef ulong       IPaddr_t;也就是说他是一个长整形变量

       {        

                cmd_usage(cmdtp);       

                return -1;     

       }

       if (NetLoop(PING) 0)    //若NetLoop函数返回值小于0,则网络不通;

       { 

                printf("ping failed; host %s is not alive\n", argv[1]); 

                return 1; 

       } 

       printf("host %s is alive\n", argv[1]); 网络通畅。  

       return 0;

一个ip地址占用32位,具体怎么转换的需要看看相关书籍,推荐TCP/IP协议簇,很不错的书

我们在PC机上执行如下操作:

ping www.baidu.com

正在 PING www.a.shifen.com (61.135.169.105) 具有32字节的数据:

来自61.135.169.105 的回复:字节=32 时间=31ms TTL=55

...... 

后面的就不看了,

我们看到一个IP地址:61.135.169.105

由于IP地址是点分十进制格式的。比如百度的这个IP地址61.135.169.105,他的意思就是把

一个32位的数分成4个字节,每个字节八位,用三个点分开的四个数分别占据这四个字节

当中的一个。因此百度的这个IP地址实际上可以转换成下面的格式:

61 ----0x3D

135----0x87

169----0xA9

105----0x69 

那么这个整数就是0x3D87A969,转换为十进制就是1032300905。

在dos命令提示符下输入:ping 1032300905

正在 ping 61.135.169.105 具有32字节的数据:

......

我们在浏览器的地址栏里面输入:  http://1032300905 也能打开百度搜索网页。这其实就

说明了一个问题: NetPingIP = string_to_ip(argv[1]);  这行代码把我们输入的IP地址

解析成了后面代码需要使用的IP地址,现在记住这一条就行了。 

解析了IP地址,后面就是调用具体的工作函数NetLoop了。调用的时候

给了一个参数:PING。在include/net.h文件中有如下定义:

typedef enum

{ BOOTP, RARP, ARP, TFTP, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP } proto_t; 

proto_t是一个枚举型的变量,我们可以凭此猜测这个NetLoop函数还可以接受

这个枚举定义的其他参数。   准备工作就做到这里,下面接着看NetLoop函数。

这个函数比较庞大,在net/net.c文件中,我们分成若干部分来看:

 int  NetLoop(proto_t protocol)

    

        bd_t *bd = gd->bd;   

#ifdef CONFIG_NET_MULTI    

       NetRestarted = 0;    

       NetDevExists = 0;

#endif               

       NetArpWaitPacketMAC = NULL;  

       NetArpWaitTxPacket = NULL;

       NetArpWaitPacketIP = 0;    

       NetArpWaitReplyIP = 0;     

       NetArpWaitTxPacket = NULL;    

       NetTxPacket = NULL;    

       NetTryCount = 1; 

 

       if (!NetTxPacket)// 因为在前边这个变量定义为NULL,那么该部分肯定会执行,这里有点冗余

       {       

             int i;  //  Setup packet buffers,alignedcorrectly. 

     //NetTxPacket是一个unsigned char类型的指针,通过该语句,该指针指向PktBuf的第32个位置 

             NetTxPacket = &PktBuf[0] + (PKTALIGN - 1);        

             NetTxPacket -= (ulong)NetTxPacket % PKTALIGN;//32字节对齐,这两句的意思是,首先加

             //31,相当于指针指向数组的第31个位置,除以32 ,然后减去该地址除以32的余数,这这两

             //句执行完毕后NetTxPacket指向的PtkBuf地址肯定能被32整除,有可能导致数组一些位置不

             //在使用之所以要先加后除是因为不过不先加的话,可能导致操作时超出数组的边界.       

             for (i = 0; i < PKTBUFSRX; i++)

            {             

                NetRxPackets[i] = NetTxPacket + (i+1)*PKTSIZE_ALIGN;        

             }    

       } 

       NetTxPacket = &PktBuf[0] + (PKTALIGN - 1); //PktBuf是一个字符数组,

      // 定义如下:  volatile uchar  PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];

      //这上面的三个宏定义分别如下:

      //# define PKTBUFSRX  4 

      //#define PKTSIZE_ALIGN       1536

      //#define PKTALIGN    32 

      //所以这里定义的是一个大概5个1536字节大的数组。继续看下面:

       NetTxPacket -= (ulong)NetTxPacket % PKTALIGN; 

       //NetTxPacket先是获得了一个地址,然后把这个地址减去一个值,减去的这个值

       //是地址本身和32的余数,那么实际上就是把NetTxPacket进行32字节对齐了。

       for (i = 0; i < PKTBUFSRX; i++)

      {             

            NetRxPackets[i] = NetTxPacket + (i+1)*PKTSIZE_ALIGN;        

       } 

       //实际上就是把NetRxPackets数组进行赋值,这个数组定义如下:

       //volatile uchar *NetRxPackets[PKTBUFSRX]; 说明它也是指向字符串的数组,

       //可以知道这个数组都被赋予了PktBuf当中的某个地址,并且这些地址都是

       //32位对其的。

       if (!NetArpWaitTxPacket)  //传输包不为空执行以下操作

       {    //以下两句仍然是32字节对齐     

           NetArpWaitTxPacket=&NetArpWaitPacketBuf[0]+(PKTALIGN-1);                       

           NetArpWaitTxPacket-=(ulong)NetArpWaitTxPacket%PKTALIGN;

           NetArpWaitTxPacketSize = 0;    

       }

       // 同样,这里这个判断貌似也是多余,因为前面已经直接把

       //NetArpWaitTxPacket设置为了NULL,这个后面的代码肯定会执行的。

       //继续后面的代码: 

       NetArpWaitTxPacket = &NetArpWaitPacketBuf[0] + (PKTALIGN - 1);

       //先给它一个值,这个值就是数组当中的一个地址,数组定义如下:

       //uchar       NetArpWaitPacketBuf[PKTSIZE_ALIGN + PKTALIGN];

       //然后和上面的一样,进行一个地址对齐,32位: 

       //NetArpWaitTxPacket -= (ulong)NetArpWaitTxPacket % PKTALIGN;  

      // 到这里,一些house keeping的代码就完成了,然后是一些和硬件相关的:   

       eth_halt();

 #ifdef CONFIG_NET_MULTI    

       eth_set_current();

#endif     

       if (eth_init(bd) < 0)

        {        

            eth_halt();        

            return(-1);    

        } 

       首先是eth_halt()函数,定义在net/eth.c文件中:

       void eth_halt(void) 

       {     

           if (!eth_current)        

           return;      

           eth_current->halt(eth_current);      

           eth_current->state = ETH_STATE_PASSIVE;

      } 

      // 实际上它只是调用了具体网卡驱动的halt函数,然后把网卡状态设置为

      //passive。由于网卡驱动的halt函数只是进行了网卡的硬件操作,不是我们这

      //里关心的重点,因此这里就不分析了。   同样,后面的eth_init函数也调用

      //的是网卡驱动的init函数,对网卡硬件进行一个初始化,接着看下面的代码:

restart: 

#ifdef CONFIG_NET_MULTI     

      memcpy (NetOurEther, eth_get_dev()->enetaddr, 6);

#else     

      eth_getenv_enetaddr("ethaddr", NetOurEther);//该行执行完毕后,NetOurEther的值等于我在

                                                  //fs2410.h定义的MAC地址值

#endif 

      //实际上执行的是第一个函数,可以看出我们实际上是把本机的mac

      //地址拷贝到了NetOurEther中。本机的mac地址是在调用eth_init函

      //数的时候从网络控制器中读取到当前网络设备的enetaddr中的,和

      //具体硬件相关。   

      NetState = NETLOOP_CONTINUE

      //然后设置网络状态,初始化循环。

      switch (protocol) {//protocol =3 =ping
#if (CONFIG_COMMANDS & CFG_CMD_NFS)
 case NFS:
#endif
#if (CONFIG_COMMANDS & CFG_CMD_PING)
 case PING:
#endif
#if (CONFIG_COMMANDS & CFG_CMD_SNTP)
 case SNTP:
#endif
 case NETCONS:
 case TFTP:
  NetCopyIP(&NetOurIP, &bd->bi_ip_addr);//程序执行后,NetOurIP=192.168.1.5
  NetOurGatewayIP = getenv_IPaddr ("gatewayip");  //NetOurGatewayIP=NULL,因为gatewayip为空
  NetOurSubnetMask= getenv_IPaddr ("netmask"); //该命令执行后NetOurSubnetMask=255.255.255.0
  NetOurVLAN = getenv_VLAN("vlan"); //该命令执行后 NetOurVLAN的结果为NULL
  NetOurNativeVLAN = getenv_VLAN("nvlan");//同上

  switch (protocol) {//protocol =3 =ping
#if (CONFIG_COMMANDS & CFG_CMD_NFS)
  case NFS:
#endif
  case NETCONS:
  case TFTP:
   NetServerIP = getenv_IPaddr ("serverip"); //该命令执行后NetServerIP =192.168.1.10.该值在

                                             //fs2410.h中定义
   break;
#if (CONFIG_COMMANDS & CFG_CMD_PING)
  case PING:
   
   break;
#endif
#if (CONFIG_COMMANDS & CFG_CMD_SNTP)
  case SNTP:
   
   break;
#endif
  default:
   break;
  }

  break;
 case BOOTP:
 case RARP:
  
  NetOurIP = 0;
  NetServerIP = getenv_IPaddr ("serverip");
  NetOurVLAN = getenv_VLAN("vlan"); 
  NetOurNativeVLAN = getenv_VLAN("nvlan");
 case CDP:
  NetOurVLAN = getenv_VLAN("vlan"); 
  NetOurNativeVLAN = getenv_VLAN("nvlan");
  break;
 default:
  break;
 }

 switch (net_check_prereq (protocol)) {//这个函数返回0
 case 1:
  
  eth_halt();
  return (-1);

#ifdef CONFIG_NET_MULTI
 case 2:
  
  break;
#endif

 case 0:
#ifdef CONFIG_NET_MULTI
  NetDevExists = 1;//存在一个网络设备
#endif
  switch (protocol) {        //protocol =3 =ping执行ping命令
  case TFTP:
   
   TftpStart();
   break;

#if (CONFIG_COMMANDS & CFG_CMD_DHCP)
  case DHCP:
   
   BootpTry = 0;
   NetOurIP = 0;
   NetServerIP = getenv_IPaddr ("serverip");
   DhcpRequest();  
   break;
#endif

  case BOOTP:
   BootpTry = 0;
   BootpRequest ();
   break;

  case RARP:
   RarpTry = 0;
   RarpRequest ();
   break;
#if (CONFIG_COMMANDS & CFG_CMD_PING)
  case PING:                   //执行这一部分
   PingStart();
   break;
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NFS)
  case NFS:
   NfsStart();
   break;
#endif
#if (CONFIG_COMMANDS & CFG_CMD_CDP)
  case CDP:
   CDPStart();
   break;
#endif
#ifdef CONFIG_NETCONSOLE
  case NETCONS:
   NcStart();
   break;
#endif
#if (CONFIG_COMMANDS & CFG_CMD_SNTP)
  case SNTP:
   SntpStart();
   break;
#endif
  default:
   break;
  }

  NetBootFileXferSize = 0;
  break;
 }

//以下下划线之间的部分不执行,因为那些宏我们没定义

----------------------------------------------------------------------------------------------

#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
#if defined(CFG_FAULT_ECHO_LINK_DOWN) && defined(CONFIG_STATUS_LED) && defined(STATUS_LED_RED)
 
 if(miiphy_link(eth_get_dev()->name, CFG_FAULT_MII_ADDR)) {
  status_led_set (STATUS_LED_RED, STATUS_LED_OFF);
 } else {
  status_led_set (STATUS_LED_RED, STATUS_LED_ON);
 }
#endif
#endif

------------------------------------------------------------------------------------------------

 //  * Main packet reception loop.  Loop receiving packets until
  // someone sets `NetState' to a state that terminates.
 for (;;) {
  WATCHDOG_RESET();
#ifdef CONFIG_SHOW_ACTIVITY
  {
   extern void show_activity(int arg);
   show_activity(1);
  }
#endif
  
   eth_rx();

  
  if (ctrlc()) {
   eth_halt();
   puts ("\nAbort\n");
   return (-1);
  }

  ArpTimeoutCheck();

  
  if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) {
   thand_f *x;

#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
 if defined(CFG_FAULT_ECHO_LINK_DOWN) && \
      defined(CONFIG_STATUS_LED) &&    \
      defined(STATUS_LED_RED)
   
   if(miiphy_link(eth_get_dev()->name, CFG_FAULT_MII_ADDR)) {
    status_led_set (STATUS_LED_RED, STATUS_LED_OFF);
   } else {
    status_led_set (STATUS_LED_RED, STATUS_LED_ON);
   }
 endif
#endif
   x = timeHandler;
   timeHandler = (thand_f *)0;
   (*x)();
  }


  switch (NetState) {

  case NETLOOP_RESTART:
#ifdef CONFIG_NET_MULTI
   NetRestarted = 1;
#endif
   goto restart;

  case NETLOOP_SUCCESS:
   if (NetBootFileXferSize > 0) {
    char buf[10];
    printf("Bytes transferred = %ld (%lx hex)\n",
     NetBootFileXferSize,
     NetBootFileXferSize);
    sprintf(buf, "%lx", NetBootFileXferSize);
    setenv("filesize", buf);

    sprintf(buf, "%lX", (unsigned long)load_addr);
    setenv("fileaddr", buf);
   }
   eth_halt();
   return NetBootFileXferSize;

  case NETLOOP_FAIL:
   return (-1);
  }
 }
下一节,我们将分析PingStart这个函数

原创粉丝点击