arm之mmu原理

实验目的:启用MMU,映射SDRAM的地址空间,操作虚拟地址实现“点灯大法”,借此掌握MMU的使用。

实验环境及说明:恒颐S3C2410开发板H2410。H2410核心板扩展有64MB的K4S561632 SDRAM(4M*16bit*4BANK),地址范围是0x30000000~0x33FFFFFF。GPIO端口的地址范围是0x56000000~0X560000B0。

实验思路:开发板上电启动后,自动将NandFlash开始的4K数据复制到SRAM中,然后跳转到0地址开始执行,然后初始化存储控制器SDRAM,把2K后的代码从SRAM中复制到SDRAM中(存放在0x30004000,前16KB用来存放页表)、设置页表、启动MMU实现虚拟地址映射GPIO寄存器和SDRAM,最后跳转到SDRAM中(地址0xB0004000)运行。重新设置栈指针,跳到点灯代码的入口点实现点灯操作。

知识掌握:MMU地址转换、内存访问权限检查、TLB及Cache的使用
一、MMU地址转换:
1.首先弄清除为什么要使用MMU纳?MMU即内存管理单元,直白一点的讲,就像食堂的餐具,所有的学生一起吃饭时不够用,但食堂又不想再出资购买新的餐具(原因很明显:一方面要成本,另一方面又占地方。这就像增加内存一样),那么有没有解决办法?根据以往经验得知不可能全学校的学习一起都到食堂吃饭,于是食堂就找几个人负责餐具的管理(相当于MMU),他们一方面发放餐具,保证来的同学有餐具可用,另一方面又回收用完的餐具(这就相当于虚拟地址到物理地址之间建立了一个映射一样,内存还是那么多,但从任意单个程序角度都好像用不完一样)。当然如果有同学一个人拿好几套餐具肯定不允许的(这就相当于内存的权限检查)。MMU在地址转换过程中涉及到三种地址:(VA---Virtual Address,虚拟地址)---这个就相当于餐具存放的地方(大家都可以领到餐具)。CPU核心看到和用到的只是虚拟地址VA,至于VA如果去对应物理地址PA,CPU核心不理会,大家也不会去关心总共有多少餐具吧;(MVA---Modified Virtual Address,变换后的虚拟地址)---这个相当于放假的时候,人很少,只发餐具好了,用过的就不先回收了,节省人员了。Caches和MMU看不到VA,他们利用MVA转换得到PA,放假了回收餐具的人也不需要一直寻找用完的餐具;(PA---Physical Address,物理地址)---实际的餐具量,就那些。实际设备看不到VA、MVA,读写它们使用的是物理地址PA,同学们就餐一般会领到餐具。
2.虚拟地址到物理地址的转换过程。ARM使用页表来进行转换,S3C2410最多会用到两级页表,以段(Section,1M)的方式进行转换时只用到一级页表,以页(Page)的方式进行转换时用到两级页表。页的大小有3种:大页(64KB)、小页(4KB)和极小页(1KB)。本文只是以段地址转换过程为例来讲解一下,页的转换大同小异。
★首先有个页表基址寄存器(位置为协处理器CP15的寄存器C2),它里面写入的就是一级页表的地址,通过读取它就可以找到一级页表存放的起始位置。一级页表的地址是16K对齐(所以[13:0]为0,使用[31:14]存储页表基址)。一级页表使用4096个描述符来表示4GB空间,所以每个描述符对应1MB的虚拟地址,存储它对应的1MB物理空间的起始地址,或者存储下一级页表的地址。使用MVA[31:20]来索引一级页表(31-20一共12位,2^12=4096,所以是4096个描述符),得到一个描述符,每个描述符占4个字节。
★描述符最后两位为0B10时,即是段的方式映射。[31:20]为段基址,此描述符低20位填充0后就是一块1MB物理地址空间的起始地址。MVA[19:0]用来在这1MB空间中寻址。描述符的位[31:20]和MVA[19:0]构成了这个虚拟地址MVA对应的物理地址。以段的方式进行映射时,虚拟地址MVA到物理地址PA的转换过程如下:①页表基址寄存器位[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利用这个地址找到段描述符;②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这就是MVA对应的PA)。


id="iframe_0.04625395219773054" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_c19a93f3ccea9b3.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.04625395219773054',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 20px; height: 20px;">

id="iframe_0.06444169906899333" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_ba0dd29d824d17a.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.06444169906899333',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 0px; height: 0px;">   
二、内存的访问权限检查
内存的访问权限检查决定一块内存是否允许读/写。这由CP15寄存器C3(域访问控制)、描述符的域(Domain)、CP15寄存器C1的R/S/A位和描述符的AP位共同决定。“域”决定是否对某块内存进行权限检查,"AP"决定如何对某块内容进行权限检查。S3C2440有16个域,CP15寄存器C3中每两位对应一个域(一共32位),用来表示这个域是否进行权限检查。
每两位数据的含义:00---无访问权限(任何访问都将导致"Domain fault"异常);01---客户模式(使用段描述符、页描述符进行权限检查);10---保留(保留,目前相当于“无访问权限”);11---管理模式(不进行权限检查,允许任何访问)。"Domain"占用4位,用来表示内存属于0-15哪一个域。

三、TLB和Cache
首先说两者都是利用程序访问的局部性原理,通过设置高速、小容量的存储器来提高性能。
1.(TLB---Translation Lookaside Buffers,转译查找缓存):由于从MVA到PA的转换需要访问多次内存,大大降低了CPU的性能,故提出TLB办法改进。当CPU发出一个虚拟地址时,MMU首先访问TLB。如果TLB中含有能转换这个虚拟地址的描述符,则直接利用此描述符进行地址转换和权限检查,否则MMU访问页表找到描述符后再进行地址转换和权限检查,并将这个描述符填入TLB中,下次再使用这个虚拟地址时就直接使用TLB用的描述符。使用TLB需要保证TLB中的内容与页表一致,在启动MMU之前,页表中的内容发生变化后,尤其要注意。一般的做法是在启动MMU之前使整个TLB无效,改变页表时,使所涉及的虚拟地址对应的TLB中条目无效。 
2.(Cache,高速缓存):为提高程序的运行速度,在主存和CPU通用寄存器之间设置一个高速的、容量相对较小的存储器,把正在执行的指令地址附近的一部分指令或数据从主存调入这个存储器,供CPU在一段时间内使用。
★写数据的两种方式:①(Write Through,写穿式)---任一CPU发出写信号送到Cache的同时,也写入主存,保证主存的数据同步更新。优点是操作简单,但由于主存速度慢,降低了系统的写速度并占用了总线的时间。②(Write Back,回写式)---数据一般只写到Cache,这样可能出现Cache中的数据得到更新而主存中的数据不变(数据陈旧)的情况。此时可在Cache中设一个标志地址及数据陈旧的信息,只有当Cache中的数据被换出或强制进行”清空“操作时,才将原更新的数据写入主存响应的单元中,保证了Cache和主存中数据一致。
★Cache有以下两个操作:①(Clean,清空)---把Cache或Write buffer中已经脏的(修改过,但未写入主存)数据写入主存。②(Invalidate,使无效)---使之不能再使用,并不将脏的数据写入主存。 
★S2C2440内置了(ICaches,指令Cache)、(DCaches,数据Cache)和(Write buffer,写缓存),操作时需要用到描述符中的C位(Ctt)和B位(Btt)。①(ICaches,指令Cache)---系统刚上电或复位时,ICaches中的内容是无效的,并且ICaches功能关闭。往Icr位(CP15协处理器中寄存器1的第12位)写1可以启动ICaches,写0停止ICaches。ICaches一般在MMU开启后使用,此时描述符的C位用来表示一段内存是否可以被Cache。若Ctt=1,允许Cache,否则不允许。如果MMU没有开启,ICaches也可以被使用,此时CPU读取指令时所涉及的内存都被当做允许Cache。ICaches关闭时,CPU每次取指都要读取主存,性能低,所以通常尽早启动ICaches。ICaches开启后,CPU每次取指时都会先在ICaches中查看是否能找到所用指令,而不管Ctt是0还是1。如果找到成为Cache命中,找不到称为Cache丢失,ICaches被开启后,CPU的取指有如下三种情况:Cache命中且Ctt为1时,从ICaches中取指,返回CPU;Cache丢失且Ctt为1时,CPU从主存中取指,并且把指令缓存到Cache中;Ctt为0时,CPU从主存中取指。②(DCaches,数据Cache)---与ICaches相似,系统刚上电或复位时,DCaches中的内容无效,并且DCaches功能关闭,Write buffer中的内容也是被废弃不用的。往Ccr位(CP15协处理器 中寄存器1的第二位)写1启动DCaches,写0停止DCaches。Write buffer和DCaches紧密结合,额米有专门的控制来开启和停止它。与ICaches不同,DCaches功能必须在MMU开启之后才能被使用。DCaches被关闭时,CPU每次都去内存取数据。DCaches被开启后,CPU每次读写数据时都会先在DCaches中查看是否能找到所要的数据,不管Ctt是0还是1,找到了称为Cache命中,找不到称为Cache丢失。
★使用Cache时需要保证Cache、Write buffer的内容和主存内容一致,保证下面两个原则:①清空DCaches,使主存数据得到更新。②使无效ICaches,使CPU取指时重新读取主存。
在实际编写程序时,要注意如下几点:①开启MMU前,使无效ICaches,DCaches和Write buffer。②关闭MMU前,清空ICaches、DCaches,即将“脏”数据写到主存上。③如果代码有变,使无效ICaches,这样CPU取指时会从新读取主存。④使用DMA操作可以被Cache的内存时:将内存的数据发送出去时,要清空Cache;将内存的数据读入时,要使无效Cache。⑤改变页表中地址映射关系时也要慎重考虑。⑥开启ICaches或DCaches时,要考虑ICaches或DCaches中的内容是否与主存保持一致。⑦对于I/O地址空间,不使用Cache和Write buffer。

四、MMU、TLB及Cache的控制指令
S3C2410除了ARM920T的CPU核心外,还有若干个协处理器,用来帮助主CPU完成一些特殊功能,对MMU、TLB及Cache等的操作就涉及到协处理器。格式如下:
<MCR|MRC>{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2
<MCR|MRC> {cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
MRC  //从协处理器获得数据,传给ARM920T CPU核心寄存器
MCR  //数据从ARM920T CPU核心寄存器传给协处理器
{cond}  //执行条件,省略时表示无条件执行
p#  //协处理器序号
<expression1>  //一个常数
Rd  //ARM920T CPU核心的寄存器
cn和cm  //协处理器中的寄存器
<expression2>  //一个常数
其中,<expression1>、cn、cm、<expression2>仅供协处理器使用,它们的作用如何取决于具体的协处理器。


示例代码解析:
开启MMU,并将虚拟地址0xA0000000~0xA0100000映射到物理地址0x56000000~0x56100000(GPFCON物理地址为0x56000050,GPFDAT物理地址为0x56000054);将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF。本示例以段的方式进行地址映射,只使用一级页表,通过上面内容可知一级页表使用4096个描述符来表示4G空间(每个描述符对应1MB),每个描述符占4字节,所以一级页表占16KB。使用SDRAM的开始16KB存放一级页表,所以剩下的内存开始地址就为0x30004000,这个地址最终会对应虚拟地址0xB0004000(所以代码运行地址为0xB0004000)。

★程序执行主要流程的示例代码。
.text
.global _start
_start:
    bl  disable_watch_dog                   @ 关闭WATCHDOG,否则CPU会不断重启
    bl  mem_control_setup                  @ 设置存储控制器以使用SDRAM
    ldr sp, =4096                                    @ 设置栈指针,以下是C函数调用前需要设好栈
    bl  copy_2th_to_sdram                   @ 将第二部分代码复制到SDRAM
    bl  create_page_table                     @ 设置页表
    bl  mmu_init                                      @ 启动MMU,启动以后下面代码都用虚拟地址
    ldr sp, =0xB4000000                       @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)
    ldr pc, =0xB0004000                        @ 跳到SDRAM中继续执行第二部分代码
halt_loop:
    b   halt_loop

★设置页表。
void create_page_table(void)
{

/* 
* 用于段描述符的一些宏定义:[31:20]段基址,[11:10]AP,[8:5]Domain,[3]C,[2]B,[1:0]0b10为段描述符
*/ 
#define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限AP */
#define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 Domain*/
#define MMU_SPECIAL         (1 << 4)    /* 必须是1 */
#define MMU_CACHEABLE       (1 << 3)    /* cacheable C位*/
#define MMU_BUFFERABLE      (1 << 2)    /* bufferable B位*/
#define MMU_SECTION         (2)         /* 表示这是段描述符 */
#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE    0x00100000        /*每个段描述符对应1MB大小空间*/


    unsigned long virtuladdr, physicaladdr;
    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;        /*SDRAM开始地址存放页表*/
    
    /*
     * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0, 为了在开启MMU后仍能运行第一部分的程序, 将0~1M的虚拟地址映射到同样的物理地址
     */
    virtuladdr = 0;
    physicaladdr = 0;
    /*虚拟地址[31:20]用于索引一级页表,找到它对应的描述符,对应于(virtualaddr>>20)。段描述符中[31:20]保存段的物理地址,对应(physicaladdr & 0xFFF00000)*/
    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;

    /*
     * 0x56000000是GPIO寄存器的起始物理地址,GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014, 为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
     * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
     */
    virtuladdr = 0xA0000000;
    physicaladdr = 0x56000000;
    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC;


    /*
     * SDRAM的物理地址范围是0x30000000~0x33FFFFFF, 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上, 总共64M,涉及64个段描述符
     */
    virtuladdr = 0xB0000000;
    physicaladdr = 0x30000000;
    while (virtuladdr < 0xB4000000)
    {
        *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
        virtuladdr += MMU_SECTION_SIZE; 
        physicaladdr += MMU_SECTION_SIZE; 
    }
}

★ 启动MMU。
void mmu_init(void)
{
    unsigned long ttb = 0x30000000;


__asm__(
    "mov    r0, #0\n"
    "mcr    p15, 0, r0, c7, c7, 0\n"    /* 使无效ICaches和DCaches */
    
    "mcr    p15, 0, r0, c7, c10, 4\n"   /* drain write buffer on v4 */
    "mcr    p15, 0, r0, c8, c7, 0\n"    /* 使无效指令、数据TLB */
    
    "mov    r4, %0\n"                   /* r4 = 页表基址 */
    "mcr    p15, 0, r4, c2, c0, 0\n"    /* 设置页表基址寄存器 */
    
    "mvn    r0, #0\n"                   
    "mcr    p15, 0, r0, c3, c0, 0\n"    /* 域访问控制寄存器设为0xFFFFFFFF, 不进行权限检查*/    
    /* 
     * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,然后再写入
     */
    "mrc    p15, 0, r0, c1, c0, 0\n"    /* 读出控制寄存器的值 */
    
    /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
     * R : 表示换出Cache中的条目时使用的算法,0 = Random replacement;1 = Round robin replacement
     * V : 表示异常向量表所在的位置,0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
     * I : 0 = 关闭ICaches;1 = 开启ICaches
     * R、S : 用来与页表中的描述符一起确定内存的访问权限
     * B : 0 = CPU为小字节序;1 = CPU为大字节序
     * C : 0 = 关闭DCaches;1 = 开启DCaches
     * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
     * M : 0 = 关闭MMU;1 = 开启MMU
     */
    
    /*  
     * 先清除不需要的位,往下若需要则重新设置它们    
     */
                                        /* .RVI ..RS B... .CAM */ 
    "bic    r0, r0, #0x3000\n"          /* ..11 .... .... .... 清除V、I位 */
    "bic    r0, r0, #0x0300\n"          /* .... ..11 .... .... 清除R、S位 */
    "bic    r0, r0, #0x0087\n"          /* .... .... 1... .111 清除B/C/A/M */


    /*
     * 设置需要的位
     */
    "orr    r0, r0, #0x0002\n"          /* .... .... .... ..1. 开启对齐检查 */
    "orr    r0, r0, #0x0004\n"          /* .... .... .... .1.. 开启DCaches */
    "orr    r0, r0, #0x1000\n"          /* ...1 .... .... .... 开启ICaches */
    "orr    r0, r0, #0x0001\n"          /* .... .... .... ...1 使能MMU */
    
    "mcr    p15, 0, r0, c1, c0, 0\n"    /* 将修改的值写入控制寄存器 */
    : /* 无输出 */
    : "r" (ttb) );
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457970.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

osal_start_timerEx(Lock_TaskID,SBP_START_DEVICE_EVT,SBP_PERIODIC_EVT_PERIOD)的理解

osal_start_timerEx(Lock_TaskID,SBP_START_DEVICE_EVT,SBP_PERIODIC_EVT_PERIOD)与osal_set_event(Music_TaskID,event)的区别是osal_start_timerEx周期性的一直推送事件周期时间由SBP_PERIODIC_EVT_PERIOD决定&#xff0c;而osal_set_event是只推送一次。osal_start_timerEx开…

ideaspringboot项目上传服务器_PHP中使用 TUS 协议来实现可恢复文件上传

曾经尝试过用PHP上传大文件吗&#xff1f;想知道您是否可以从上次中断的地方继续上传&#xff0c;而不会在遇到任何中断的情况下再次重新上传整个数据&#xff1f;如果您觉得这个场景很熟悉&#xff0c;请接着往下阅读。文件上传是我们几乎所有现代Web项目中的一项很常见的任务…

无密码登陆

server A /B以root账户在A上无密码ssh到B方式一ON A:ssh-keygen -t dsa -P -f ~/.ssh/id_dsa cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys chmod 644 ~/.ssh/authorized_keys service sshd restart ON B:ssh-keygen -t dsa -P -f ~/.ssh/id_dsa cat ~/.ssh/id_d…

嵌入式常见笔试题总结

预处理器&#xff08;Preprocessor&#xff09;1. 用预处理指令#define 声明一个常数&#xff0c;用以表明1年中有多少秒&#xff08;忽略闰年问题&#xff09; #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情&#xff1a; 1). #define 语法的基本知识…

【转】php利用mkdir创建多级目录

先介绍一下 mkdir() 这个函数&#xff1a; mkdir($path,0777,true); 第一个参数&#xff1a;必须&#xff0c;代表要创建的多级目录的路径&#xff1b; 第二个参数&#xff1a;设定目录的权限&#xff0c;默认是 0777&#xff0c;意味着最大可能的访问权&#xff1b; 第三个参数…

java使用xml存储数据_用存储过程和 JAVA 写报表数据源有什么弊端?

用存储过程和 JAVA 写报表数据源有什么弊端&#xff1f;跟着小编一起来一看一下吧&#xff01;我们在报表开发中经常会使用存储过程准备数据&#xff0c;存储过程支持分步计算&#xff0c;可以实现非常复杂的计算逻辑&#xff0c;为报表开发带来便利。所以&#xff0c;报表开发…

GIT文件的三种状态

对于任何一个文件&#xff0c;在 Git 内都只有三种状态&#xff1a;已提交&#xff08;committed&#xff09;&#xff0c;已修改&#xff08;modified&#xff09;和已暂存&#xff08;staged&#xff09;。已提交表示该文件已经被安全地保存在本地数据库 中了&#xff1b;已修…

嵌入式常见笔试题总结(2)

预处理器&#xff08;Preprocessor&#xff09;   1. 用预处理指令#define 声明一个常数&#xff0c;用以表明1年中有多少秒&#xff08;忽略闰年问题&#xff09;   #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL   我在这想看到几件事情&#xff1a;   1). #…

MAC OS上JAVA1.6 升级1.7,以及 maven3.2.1配置

一、我的MAC系统 预装的Jdk是1.6&#xff0c;由于需要使用eclipse MARs 2版本&#xff0c;故需要升级到1.7 二、下载JAVA jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 不知道为什么直接下非常慢&#xff0c;后来用的迅雷就超级快…

sql server 创建唯一性非聚集索引语句_数据库专题—索引原理

深入浅出数据库索引原理参见:https://www.cnblogs.com/aspwebchh/p/6652855.html1.为什么给表加上主键&#xff1f;1.平时创建表的时候&#xff0c;都会给表加上主键。如果没有主键的表&#xff0c;数据会一行行的排列在磁盘上&#xff0c;查找一个数据需要一条条的进行对比。而…

String,StringBuffer,StringBuilder区别

String 字符串常量StringBuffer 字符串变量&#xff08;线程安全&#xff09;StringBuilder 字符串变量&#xff08;非线程安全&#xff09; 简要的说&#xff0c; String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行…

oracle数据库更新语句_20_手把手教你学Python之操作数据库

数据库是数据的仓库&#xff0c;将大量数据按照一定的方式组织并存储起来&#xff0c;方便进行管理和维护&#xff0c;例如快速检索和统计等。数据库的主要特点&#xff1a;以一定的方式组织、存储数据&#xff1b;能为多个用户共享&#xff1b;与程序彼此独立。……数据库管理…

第十周学习进度

第十周 所花时间&#xff08;包括上课&#xff09; 10小时 代码量&#xff08;行&#xff09; 0行 博客量&#xff08;篇&#xff09; 4篇 了解到的知识点 对各组进行评价&#xff1b;思考并回复各组 转载于:https://www.cnblogs.com/qwer111/p/5470819.html

嵌入式常见笔试题总结(3)

1:设float a2, b4, c3&#xff1b;&#xff0c;以下C语言表达式与代数式 (ab)c计算结果不一致的是 A.(ab)*c/2 B.(1/2)*(ab)*c C.(ab)*c*1/2 D.c/2*(ab) 参考答案&#xff1a;B&#xff0c;因为a,b,c三个变量都是浮点数&#xff0c;所以在B答案中其结果是0&#xff0c;因为…

查询Oracle正在执行的sql语句

--查询Oracle正在执行的sql语句及执行该语句的用户 [sql] view plaincopy SELECT b.sid oracleID, b.username 登录Oracle用户名, b.serial#, spid 操作系统ID, paddr, sql_text 正在执行的SQL, b.machine 计算机名 FROM v$p…

WinForm 清空界面控件值的小技巧

WinForm 清空界面控件值的小技巧 原文:WinForm 清空界面控件值的小技巧在WinForm里面有时候需要清空自己输入内容或是选择的选项&#xff0c;以便重新操作流程&#xff0c;那么一般你是怎么清空界面各个控件值的呢&#xff1f;如果窗体里面控件&#xff0c;尤其是TextBox控件比…

int 取值范围_一定范围内的随机数

老司机的新问题&#xff0c;取得[min, max]范围的随机数。C版本的rand函数很不容易用对&#xff0c;直接用rand() % (max - min 1) min&#xff0c;这个公式不对。这个公式与取最低位的算法相同&#xff0c;而随机数的最低几位不一定等概率。Donald Knuth博士教导我们正确的用…

virsh的使用

2019独角兽企业重金招聘Python工程师标准>>> ###这里最先要完成的是对网卡的配置(os ubuntu14.04) 目的是实现vm上是以桥接的方式联网 修改/etc/network/interfaces # interfaces(5) file used by ifup(8) and ifdown(8) auto lo iface lo inet loopback #auto eth0…

嵌入式常见笔试题总结(4)

1&#xff1a;用C语言实现大小端的测试 Int CheckCpu() { union { int a; char b; }c; c.a1; if(c.b1) printf(“小端”)&#xff1b; else printf&#xff08;“大端”&#xff09;&#xff1b; } 2&#xff1a;volatile的作用有哪些&#xff1f; Volatile第一变量相当于告诉编…

《java入门第一季》之类String类小案例

String类有许多获取方法&#xff0c;API文档里面可查看。针对获取方法&#xff0c;给出小案例。 /** 需求&#xff1a;遍历获取字符串中的每一个字符* 分析&#xff1a; 用到两个方法&#xff1a;char charAt(int index) 表示获取字符串指定索引的字符int length() …