转载地址: http://blog.csdn.net/cocoalary/article/details/46583697
这几天看uboot,感觉getenv函数里嵌套了很多其他函数,一直没搞清是怎么实现get的。今天终于领会了,把它记下来,供大家一起学习。
我研究的是IPaddr的环境变量如何get的,其实就是 gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");该如何分析。先找到getenv_IPaddr函数:
- typedef ulong IPaddr_t;
- IPaddr_t getenv_IPaddr (char *var)
- {
- return (string_to_ip(getenv(var)));
getenv_IPaddr函数使用字符串"ipaddr"做为参数,返回一个ulong型整数。再看getenv函数:
- char *getenv (char *name)
- {
- int i, nxt;
- WATCHDOG_RESET();
- for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
- int val;
- for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
- if (nxt >= CFG_ENV_SIZE) {
- return (NULL);
- }
- }
- if ((val=envmatch((uchar *)name, i)) < 0)
- continue;
- return ((char *)env_get_addr(val));
- }
- return (NULL);
- }
getenv 是一个指针函数,返回字符型指针。为什么会返回字符型指针?后面再说。getenv 函数的参数是字符指针变量name,实参就是“ipaddr"。从这里开始出现了一大堆莫名其妙的函数env_get_char,envmatch,env_get_addr。在分析getenv 之前,还得先把这些其他的函数搞清楚。首先看env_get_char:
- uchar (*env_get_char)(int) = env_get_char_init;
- static uchar env_get_char_init (int index)
- {
- uchar c;
- /* if crc was bad, use the default environment */
- if (gd->env_valid)
- {
- c = env_get_char_spec(index);
- } else {
- c = default_environment[index]; //返回default_environment指定位置的字符
- }
- return (c);
- }
env_get_char和env_get_char_init等效,参数为int,返回default_environment指定位置的字符。default_environment是定义了一系列缺省的环境变量值。以IP地址为例
- #ifdef CONFIG_IPADDR
- "ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
- #endif
- #define CONFIG_IPADDR 192.168.1.230
结果是 "ipaddr=192.168.1.230”+"\0"
再看envmatch:
- static int
- envmatch (uchar *s1, int i2)
- {
- while (*s1 == env_get_char(i2++))
- if (*s1++ == '=')
- return(i2);
- if (*s1 == '\0' && env_get_char(i2-1) == '=')
- return(i2);
- return(-1);
- }
我开始一直搞不清envmatch 是如何比较的。现在搞清楚了,它是在比较“ipaddr"和"ipaddr=192.168.1.230”。while (*s1 == env_get_char(i2++)) 就是从default_environment字符串的指定位置开始比较,指定位置由env_get_char返回值所定,比较不一致时函数返回-1。当比较到default_environment字符串的IPaddr时,开始有相同的字符出现了,这时while值为1,依次比较i,p,a,d,d,r字符,因为每比较一次,S1(“ipaddr")和i2的值都加1,即指向下一个字符。当比较完"r"后,指针指向了下一个字符即"=", "="之后的字符串正是我们需要的,所以return(i2),i2是赋给val的,下一步就是env_get_addr(val)。
- uchar *env_get_addr (int index)
- {
- if (gd->env_valid) {
- return ( ((uchar *)(gd->env_addr + index)) );
- } else {
- return (&default_environment[index]);
- }
- }
找到了val的值,也就是ipaddr在default_environment中所处的位置,即可以返回ipaddr字符串的地址。前面说为什么会返回字符型指针?这里给出了答案。注意:这里返回的是指针,是192.168.1.230的地址,确切地说是"1"的指针地址。
我们再返回char *getenv (char *name) 函数,其返回值也就是192.168.1.230的地址, 这个地址是string_to_ip函数的地址。
- IPaddr_t string_to_ip(char *s)
- {
- IPaddr_t addr;
- char *e;
- int i;
- if (s == NULL)
- return(0);
- for (addr=0, i=0; i<4; ++i) {
- ulong val = s ? simple_strtoul(s, &e, 10) : 0;
- addr <<= 8;
- addr |= (val & 0xFF);
- if (s) {
- s = (*e) ? e+1 : e;
- }
- }
- return (htonl(addr));
- }
string_to_ip函数将字符串192.168.1.230转化成整数,关键的地方是for (addr=0, i=0; i<4; ++i),这里可以看出进行了四次转化,分别是四个段。但是函数simple_strtoul是如何区分"."而分成四个段? 这是靠simple_strtoul中的while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) ? toupper(*cp) : *cp)-'A'+10) < base)实现的,因为当while遇到"."时,判定条件即为0,就不再判定而往下走了。四个段都操作完成,就出现了addr = (((((192 << 8) | 168) << 8) | 1) << 8 ) | 230,