u-boot-1.1.6环境变量
学习目标:
1、分析u-boot-1.1.6环境变量,了解环境变量初始化、设置以及过程
2、为后面能够掌握u-boot-1.1.6如何启动内核过程打下基础
1、环境变量的概念
在分析uboot环境变量的源码实现之前,先介绍一下环境变量的概念。u-boot通过环境变量为用户提供一定程度的可配置性,在不改变源码、不重新编译的情况下,可以通过设置环境变量的值来改变uboot的一些设置,如bootdelay时间,内核启动命令参数等。可配置性意味着环境变量是可以添加、删除和修改的,也就是说环境变量的内容可能会频繁变化,为了不让这种变化对u-boot的代码和数据造成破坏,通常的选择是在FLASH中预留一个专门用来存储环境变量的块。开发者在串口终端输入setenv命令可以设置环境量值,设置完成后使用saveenv命令将setenv命令设置好的环境变量保存在非易失存储器中。如下图所示,使用uboot时, 在串口终端输入printenv命令便能够打印uboot中的环境变量值。
2、环境变量的数据结构
#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) typedef struct environment_s {
unsigned long crc; /* CRC32 over data bytes */
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;
crc变量保存上一次环境变量数据写入flash时做crc运算的结果,当重新从flash中读取环境变量值时,代码会对读取数据进行crc校验,并将新的校验的值与上次保存的值进行比较,如果两次crc校验值相同,表示存放在flash中的环境变量正常,否则,表示存放环境变量数据损坏。
数组data[ENV_SIZE]保存环境变量的值,环境变量名已经环境变量值以字符形式存放在内存中
3、环境变量的初始化
int env_init(void)
{
/* 未定义CONFIG_OMAP2420H4,此处代码不被编译 */
#ifdef CONFIG_OMAP2420H4
int flash_probe(void); if(flash_probe() == )
goto bad_flash;
#endif
/* env_t *env_ptr = (env_t *)CFG_ENV_ADDR, 此处执行吗?,如果此处执行 */
/* 先进入nor flash读取环境变量,通过CRC检测读取环境变量是否正确 */
if (crc32(, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
gd->env_addr = (ulong)&(env_ptr->data);
gd->env_valid = ;
return();
}
/* 未定义CONFIG_OMAP2420H4,此处代码不被编译 */
#ifdef CONFIG_OMAP2420H4
bad_flash:
#endif
/* CRC校验后读取环境变量不正确,此处执行,使用默认环境变量 */
gd->env_addr = (ulong)&default_environment[];
gd->env_valid = ;
return ();
}
int env_inti(void)函数在uboot代码第二阶段入口函数start_armboot开始处被调用。首先,env_init函数从存放环境变量的flash内存地址中读取环境变量的值,并且对读出的环境变量数据进行crc校验,将新校验的crc值与写入环境变量时的crc校验值进行比较。如果两个值相等,将读取数据的地址赋值给gd指针执行的全局数据结构中的env_addr成员,并将env_valid标志位置位。如果读取环境变量校验值和写入时的校验值不同,存储在flash中的环境变量值损坏,将默认环境变量值赋给gd指针执行的全局数据结构中的env_addr成员。
env_ptr指针指向环境变量存放初始地址,env_t *env_ptr = (env_t *)CFG_ENV_ADDR。CFG_ENV_ADDR是一个宏,代表环境变量在flash中存放的地址,由于我配置uboot时目标板是smdk2410,所以该宏存放在include/configs/smdk2410.h头文件中。默认环境变量值如下所示:
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"
#endif
#ifdef CONFIG_LOADS_ECHO
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
#endif
#ifdef CFG_AUTOLOAD
"autoload=" CFG_AUTOLOAD "\0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef CONFIG_ROOTPATH
"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" MK_STR(CONFIG_NETMASK) "\0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" MK_STR(CONFIG_LOADADDR) "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};
4、环境变量的重定位
env_inti函数在初始化时对环境变量保存位置进行识别,并将环境变量存放的初始地址赋值给了全局数据结构gt_t成员env_addr。读取存放在flash中的环境变量数据,进行crc校验后有效,便使用flash中的环境变量,否则使用默认环境变量。我们知道flash操作速度内存慢(若环境变量存放在nand flash中,进行读取时还需要相应的指令),对flash中环境变量进行直接读写不仅会影响uboot性能,而且多次擦除还会影响flash的使用寿命,因此,需要将flash中的环境变量进行重定位操作。
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off); /* 未定义CONFIG_AMIGAONEG3SE宏,此处不被编译 */
#ifdef CONFIG_AMIGAONEG3SE
enable_nvram();
#endif /* 未定义ENV_IS_EMBEDDED宏,此处不被编译 */
#ifdef ENV_IS_EMBEDDED
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
/* ifdef语句为假,此处被编译 */
#else
/*
* We must allocate a buffer for the environment
*/
/* 在malloc区,为环境变量分配存储空间 */
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif /*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory; if (gd->env_valid == ) {
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
SHOW_BOOT_PROGRESS (-);
#endif if (sizeof(default_environment) > ENV_SIZE)
{
puts ("*** Error - default environment is too large\n\n");
return;
} memset (env_ptr, , sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = ;
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
/* 未定义CONFIG_AMIGAONEG3SE宏,此处代码不被编译 */
#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}
void env_relocate (void)函数功能是对flash中环境变量的数据进行重定位,首先在uboot自定义的堆区为环境变量分配相应的内存空间,env_ptr指针指向这块内存的初始地址,然后根据gd->env_valid值执行不同的操作(gd->env_valid在env_init函数中设置)。gd->env_valid=0代表flash中环境变量无效,使用默认环境变量,将env_ptr指针指向的内存地址清零,再将存放默认环境变量default_environment数组内容拷贝到env_ptr指针指向的内存空间中;gd->env_valid=1时flash中存放环境变量有效,调用env_relocate_spec ()函数,将flash中存放的环境变量数据复制到env_ptr指针指向的内存空间。最后,将环境变量中的数据在重定位内存中的地址赋给gd->env_addr。
5、读取环境变量
前面介绍过在串口终端输入printenv,执行回车后,打印环境变量值。执行printenv命令其实底层调用的是do_printenv函数,这个函数源码如下:
int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i, j, k, nxt;
int rcode = ; if (argc == ) { /* 打印所有的环境变量 */
for (i=; env_get_char(i) != '\0'; i=nxt+) {
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
;
for (k=i; k<nxt; ++k)
putc(env_get_char(k));
putc ('\n'); if (ctrlc()) {
puts ("\n ** Abort\n");
return ;
}
} printf("\nEnvironment size: %d/%d bytes\n", i, ENV_SIZE); return ;
}
/* 遍历输入的参数,分别打印其环境变量值 */
for (i=; i<argc; ++i) { /* print single env variables */
char *name = argv[i]; k = -;
for (j=; env_get_char(j) != '\0'; j=nxt+) { for (nxt=j; env_get_char(nxt) != '\0'; ++nxt) /* uchar (*env_get_char)(int) = env_get_char_init,env_get_char_init函数获取相对环境变量内存起始地址偏移nxt长度字符 */
;
k = envmatch((uchar *)name, j); /* 检测输入参数名和内存中环境变量参数名是否匹配 */
if (k < ) {
continue;
}
puts (name);
putc ('=');
while (k < nxt)
putc(env_get_char(k++));
putc ('\n');
break;
}
if (k < ) {
printf ("## Error: \"%s\" not defined\n", name);
rcode ++;
}
}
return rcode;
}
1、当argc==1时(例如执行printenv命令,argc=1),打印uboot所有的环境变量
2、当argc>1时(例如执行printenv bootcmd baudrate命令,argc=3),打印环境变量bootcmd、baudrate
6、设置环境变量
同读取环境变量一样,执行setenv命令其实底层调用的是_do_setenv函数,这个函数源码如下:
int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
if (argc < 2) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
} return _do_setenv (flag, argc, argv);
} int _do_setenv (int flag, int argc, char *argv[])
{
int i, len, oldval;
int console = -;
uchar *env, *nxt = NULL;
char *name;
bd_t *bd = gd->bd;
/* env_data获取环境变量的数据部分在内存的初始地址 */
uchar *env_data = env_get_addr(); if (!env_data) /* need copy in RAM */
return ; name = argv[];
/* 检测输入的第二个参数为'=',打印相关错误信息 */
if (strchr(name, '=')) {
printf ("## Error: illegal character '=' in variable name \"%s\"\n", name);
return ;
}
/* 遍历内存中的环境变量数据数组,检测配置环境变量名是否存在 */
oldval = -;
for (env=env_data; *env; env=nxt+) {
for (nxt=env; *nxt; ++nxt)
;
if ((oldval = envmatch((uchar *)name, env-env_data)) >= )
break;
} /*
* Delete any existing definition
*/
if (oldval >= ) {
#ifndef CONFIG_ENV_OVERWRITE /* 网卡地址和串口号是固定的,如果设置是网卡地址和串口号,打印"Can't overwrite \"%s\"\n", name,代码返回 */
if ( (strcmp (name, "serial#") == ) ||
((strcmp (name, "ethaddr") == )
#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
&& (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != )
#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
) ) {
printf ("Can't overwrite \"%s\"\n", name);
return ;
}
#endif /* Check for console redirection */
if (strcmp(name,"stdin") == ) {
console = stdin;
} else if (strcmp(name,"stdout") == ) {
console = stdout;
} else if (strcmp(name,"stderr") == ) {
console = stderr;
} if (console != -) {
if (argc < ) { /* Cannot delete it! */
printf("Can't delete \"%s\"\n", name);
return ;
} /* Try assigning specified device */
if (console_assign (console, argv[]) < )
return ; #ifdef CONFIG_SERIAL_MULTI
if (serial_assign (argv[]) < )
return ;
#endif
} /* 如果是参数=波特率,重新设置相应波特率值 */
if (strcmp(argv[],"baudrate") == ) {
int baudrate = simple_strtoul(argv[], NULL, );
int i;
/* 检测输入波特率值是否合法,如果不合法,打印错误,返回函数 */
for (i=; i<N_BAUDRATES; ++i) {
if (baudrate == baudrate_table[i])
break;
}
if (i == N_BAUDRATES) {
printf ("## Baudrate %d bps not supported\n",
baudrate);
return ;
}
printf ("## Switch baudrate to %d bps and press ENTER ...\n",
baudrate);
udelay();
gd->baudrate = baudrate;
#ifdef CONFIG_PPC
gd->bd->bi_baudrate = baudrate;
#endif serial_setbrg ();
udelay();
for (;;) {
if (getc() == '\r')
break;
}
} if (*++nxt == '\0') {
if (env > env_data) {
env--;
} else {
*env = '\0';
}
} else {
for (;;) {
*env = *nxt++;
if ((*env == '\0') && (*nxt == '\0'))
break;
++env;
}
}
*++env = '\0';
} #ifdef CONFIG_NET_MULTI
if (strncmp(name, "eth", ) == ) {
char *end;
int num = simple_strtoul(name+, &end, ); if (strcmp(end, "addr") == ) {
eth_set_enetaddr(num, argv[]);
}
}
#endif /* Delete only ? */
if ((argc < ) || argv[] == NULL) {
env_crc_update ();
return ;
} /*
* Append new definition at the end
*/
for (env=env_data; *env || *(env+); ++env)
;
if (env > env_data)
++env;
/*
* Overflow when:
* "name" + "=" + "val" +"\0\0" > ENV_SIZE - (env-env_data)
*/
len = strlen(name) + ;
/* add '=' for first arg, ' ' for all others */
for (i=; i<argc; ++i) {
len += strlen(argv[i]) + ;
}
if (len > (&env_data[ENV_SIZE]-env)) {
printf ("## Error: environment overflow, \"%s\" deleted\n", name);
return ;
}
while ((*env = *name++) != '\0')
env++;
for (i=; i<argc; ++i) {
char *val = argv[i]; *env = (i==) ? '=' : ' ';
while ((*++env = *val++) != '\0') /* 将设置的新的参数写入到重定位的环境变量数据中 */
;
} /* end is marked with double '\0' */
*++env = '\0'; /* Update CRC */
env_crc_update (); /* 如果设置环境变量是ethaddr、ipaddr、loadaddr、bootfile、vga_fg_color 根据新的环境值,立即更新相应全局变量、更新底层硬件 */
if (strcmp(argv[],"ethaddr") == ) {
char *s = argv[]; /* always use only one arg */
char *e;
for (i=; i<; ++i) {
bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, ) : ;
if (s) s = (*e) ? e+ : e;
}
#ifdef CONFIG_NET_MULTI
eth_set_enetaddr(, argv[]);
#endif
return ;
} if (strcmp(argv[],"ipaddr") == ) {
char *s = argv[]; /* always use only one arg */
char *e;
unsigned long addr;
bd->bi_ip_addr = ;
for (addr=, i=; i<; ++i) {
ulong val = s ? simple_strtoul(s, &e, ) : ;
addr <<= ;
addr |= (val & 0xFF);
if (s) s = (*e) ? e+ : e;
}
bd->bi_ip_addr = htonl(addr);
return ;
}
if (strcmp(argv[],"loadaddr") == ) {
load_addr = simple_strtoul(argv[], NULL, );
return ;
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if (strcmp(argv[],"bootfile") == ) {
copy_filename (BootFile, argv[], sizeof(BootFile));
return ;
}
#endif /* CFG_CMD_NET */ #ifdef CONFIG_AMIGAONEG3SE
if (strcmp(argv[], "vga_fg_color") == ||
strcmp(argv[], "vga_bg_color") == ) {
extern void video_set_color(unsigned char attr);
extern unsigned char video_get_attr(void); video_set_color(video_get_attr());
return ;
}
#endif /* CONFIG_AMIGAONEG3SE */ return ;
}
7、保存设置的环境变量
串口输入setenv命令,系统是将新的环境变量保存到重定位的内存中,我们知道内存在系统掉电后数据丢失,如果想要将设置的环境变量永久保存,还是要将新的环境变量写入到存储环境变量的flash地址中。执行saveenv命令就是将分配内存中的环境变量保存到flash中,执行saveenv命令其实底层调用的是do_printenv函数,这个函数源码如下:
int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
extern char * env_name_spec; printf ("Saving Environment to %s...\n", env_name_spec); return (saveenv() ? : );
}
/* 由于saveenv函数过长,下面删去不被包含的宏内的代码 */
int saveenv(void)
{
int len, rc;
ulong end_addr;
ulong flash_sect_addr; uchar *env_buffer = (uchar *)env_ptr; int rcode = ;
/* static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; */
flash_sect_addr = (ulong)flash_addr; /* flash_sect_addr 保持环境变量在flash中起始地址 */
len = CFG_ENV_SIZE; end_addr = flash_sect_addr + 0x20000 - ; debug ("Protect off %08lX ... %08lX\n",
(ulong)flash_sect_addr, end_addr); if (flash_sect_protect (, flash_sect_addr, end_addr))
return ; puts ("Erasing Flash...");
if (flash_sect_erase (flash_sect_addr, end_addr))
return ; puts ("Writing to Flash... ");
rc = flash_write((char *)env_buffer, flash_sect_addr, len);
if (rc != ) {
flash_perror (rc);
rcode = ;
} else {
puts ("done\n");
} /* try to re-protect */
(void) flash_sect_protect (, flash_sect_addr, end_addr);
return rcode;
}
1、env_buffer指针存放环境变量在内存中的初始位置
2、写入flash之前应该擦除想要写入的扇区,调用flash_sect_erase函数擦除环境变量所做flash的扇区
3、再调用flash_write,写入新的环境变量
u-boot-1.1.6环境变量的更多相关文章
- Spring Boot 环境变量读取 和 属性对象的绑定
网上看到的一些方法,结合我看到的 和我们现在使用的.整理成此文: 第一种方法 参见catoop的博客之 Spring Boot 环境变量读取 和 属性对象的绑定(尊重原创) 第二种方法 class不用 ...
- 十五、Spring Boot 环境变量读取 和 属性对象的绑定
凡是被spring管理的类,实现接口 EnvironmentAware 重写方法 setEnvironment 可以在工程启动时,获取到系统环境变量和application配置文件中的变量. 如: @ ...
- Spring boot基础:配置文件配置变量、多环境的配置
一.配置 resources下面application.properties 1.普通配置 resources下面application.properties,比如写上:server.port=909 ...
- uboot环境变量实现分析
u-boot的环境变量用来存储一些经常使用的参数变量,uboot希望将环境变量存储在静态存储器中(如nand nor eeprom mmc). 其中有一些也是大家经常使用,有一些是使用人员自己定义的, ...
- ubuntu 添加环境变量
转自:http://hi.baidu.com/mario05/item/02b6d60ff7371136a2332a48 Ubuntu Linux系统环境变量配置文件:/etc/profile : 在 ...
- ***LINUX添加PHP环境变量:CentOS下将php和mysql命令加入到环境变量中
CentOS系统下如何将PHP和mysql命令加入到环境变量中,在Linux CentOS系统上 安装完php和MySQL后,为了使用方便,需要将php和mysql命令加到系统命令中,如果在没有添加到 ...
- U-boot的环境变量值得注意的有两个: bootcmd 和bootargs
本文转载至:http://www.cnblogs.com/cornflower/archive/2010/03/27/1698279.html U-boot的环境变量值得注意的有两个: bootcmd ...
- linux网络配置、环境变量以及JDK安装(CentOS 6.5)
由于需要搭建hadoop平台,但是苦于没有现成可用的linux服务器,只好自己下载了CentOS 6.5从头装起,安装过程中遇到了很多问题,比如网络配置.时钟同步.环境变量配置.以及各种服务的启停,还 ...
- 在Linux里读取UBOOT环境变量
转载:http://falloutmx.blog.163.com/blog/static/39236020201211145010154/ 可以通过mtd方式读取,也可以用ioremap方式.不过这些 ...
- uboot环境变量与内核MTD分区关系
uboot 与系统内核中MTD分区的关系: 分区只是内核的概念,就是说A-B地址放内核,C-D地址放文件系统,(也就是规定哪个地址区间放内核或者文件系统)等等. 1:在内核MTD中可以定义分区A~B, ...
随机推荐
- intent调用代码总结
进入联系人界面 Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(People.CO ...
- idea 自定义视图
效果: 设置:!file:.iml&&!file:.idea//&&!file:.settings//*&&!file:.classpath&& ...
- 带你从零学ReactNative开发跨平台App开发(三)
ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...
- lsnrctl 与 tnsnames.ora 的联系
平台:Windoxs XP+Oracle 11G 当使用oralce的 Net Manager创建了一个名为“L3”的Listener后,要想使用lsnrctl启动和关闭 L3 还必须在tnsname ...
- armel和armhf区别
出于低功耗.封装限制等种种原因,之前的一些ARM架构处理器因为内部资源宝贵,加入浮点运算单元是十分奢侈的,因为需要额外的软件实现.之前的ARM处理器架构是什么样的?(http://www.cnblog ...
- MVC 在视图中获取当前的Controller、Action的方式
在视图中获取Controller和Action的方式: Controller: @ViewContext.RouteData.Route.GetRouteData(this.Context).Valu ...
- Oracle GoldenGate OGG管理员手册(较早资料)
第一章 系统实现简述 前言 编写本手册的目的是为系统管理员以及相关操作人员提供 Oracle Goldengat 软 件的日常维护和使用的技术参考: 3 ORACLE 第二章 OGG 日常维护操作 ...
- 转:ClickOnce部署Winform程序的方方面面
1. ClickOnce简介 微软官方对ClickOnce的解释是:ClickOnce 是一项部署技术,您可以利用这项技术来创建基于 Windows 的自行更新的应用程序,并且安装和运行这类应用程序所 ...
- [UI] 精美UI界面欣赏[12]
精美UI界面欣赏[12]
- json格式转换(json,csjon)(天气预报)
json格式数据默认为string,可以使用eval()函数或者json模块将其转换为dict.标准Json字符串必须使用双引号(")而不能使用单引号('),否则从字符串转换成dict类型会 ...