8086实时时钟实验(一)——《x86汇编语言:从实模式到保护模式》05
1.代码清单
;代码清单9-1
;文件名:c09_1.asm
;文件说明:用户程序
;创建日期:2011-4-16 22:03 ;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00] ;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code.start ;段地址[0x06] realloc_tbl_len dw (header_end-realloc_begin)/4
;段重定位表项个数[0x0a] realloc_begin:
;段重定位表
code_segment dd section.code.start ;[0x0c]
data_segment dd section.data.start ;[0x14]
stack_segment dd section.stack.start ;[0x1c] header_end: ;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
new_int_0x70:
push ax
push bx
push cx
push dx
push es .w0:
mov al,0x0a ;阻断NMI。当然,通常是不必要的
or al,0x80
out 0x70,al
in al,0x71 ;读寄存器A
test al,0x80 ;测试第7位UIP
jnz .w0 ;以上代码对于更新周期结束中断来说
;是不必要的
xor al,al
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(秒)
push ax mov al,2
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(分)
push ax mov al,4
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(时)
push ax mov al,0x0c ;寄存器C的索引。且开放NMI
out 0x70,al
in al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断
;此处不考虑闹钟和周期性中断的情况
mov ax,0xb800
mov es,ax pop ax
call bcd_to_ascii
mov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示 mov [es:bx],ah
mov [es:bx+2],al ;显示两位小时数字 mov al,':'
mov [es:bx+4],al ;显示分隔符':'
not byte [es:bx+5] ;反转显示属性 pop ax
call bcd_to_ascii
mov [es:bx+6],ah
mov [es:bx+8],al ;显示两位分钟数字 mov al,':'
mov [es:bx+10],al ;显示分隔符':'
not byte [es:bx+11] ;反转显示属性 pop ax
call bcd_to_ascii
mov [es:bx+12],ah
mov [es:bx+14],al ;显示两位小时数字 mov al,0x20 ;中断结束命令EOI
out 0xa0,al ;向从片发送
out 0x20,al ;向主片发送 pop es
pop dx
pop cx
pop bx
pop ax iret ;-------------------------------------------------------------------------------
bcd_to_ascii: ;BCD码转ASCII
;输入:AL=bcd码
;输出:AX=ascii
mov ah,al ;分拆成两个数字
and al,0x0f ;仅保留低4位
add al,0x30 ;转换成ASCII shr ah,4 ;逻辑右移4位
and ah,0x0f
add ah,0x30 ret ;-------------------------------------------------------------------------------
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax mov bx,init_msg ;显示初始信息
call put_string mov bx,inst_msg ;显示安装信息
call put_string mov al,0x70
mov bl,4
mul bl ;计算0x70号中断在IVT中的偏移
mov bx,ax cli ;防止改动期间发生新的0x70号中断 push es
mov ax,0x0000
mov es,ax
mov word [es:bx],new_int_0x70 ;偏移地址。 mov word [es:bx+2],cs ;段地址
pop es mov al,0x0b ;RTC寄存器B
or al,0x80 ;阻断NMI
out 0x70,al
mov al,0x12 ;设置寄存器B,禁止周期性中断,开放更
out 0x71,al ;新结束后中断,BCD码,24小时制 mov al,0x0c
out 0x70,al
in al,0x71 ;读RTC寄存器C,复位未决的中断状态 in al,0xa1 ;读8259从片的IMR寄存器
and al,0xfe ;清除bit 0(此位连接RTC)
out 0xa1,al ;写回此寄存器 sti ;重新开放中断 mov bx,done_msg ;显示安装完成信息
call put_string mov bx,tips_msg ;显示提示信息
call put_string mov cx,0xb800
mov ds,cx
mov byte [12*160 + 33*2],'@' ;屏幕第12行,35列 .idle:
hlt ;使CPU进入低功耗状态,直到用中断唤醒
not byte [12*160 + 33*2+1] ;反转显示属性
jmp .idle ;-------------------------------------------------------------------------------
put_string: ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl ;cl=0 ?
jz .exit ;是的,返回主程序
call put_char
inc bx ;下一个字符
jmp put_string .exit:
ret ;-------------------------------------------------------------------------------
put_char: ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es ;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx ;高8位
mov ah,al mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX=代表光标位置的16位数 cmp cl,0x0d ;回车符?
jnz .put_0a ;不是。看看是不是换行等字符
mov ax,bx ;
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor .put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是,那就正常显示字符
add bx,80
jmp .roll_screen .put_other: ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl ;以下将光标位置推进一个字符
shr bx,1
add bx,1 .roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840 ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720
add bx,2
loop .cls mov bx,1920 .set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al pop es
pop ds
pop dx
pop cx
pop bx
pop ax ret ;===============================================================================
SECTION data align=16 vstart=0 init_msg db 'Starting...',0x0d,0x0a,0 inst_msg db 'Installing a new interrupt 70H...',0 done_msg db 'Done.',0x0d,0x0a,0 tips_msg db 'Clock is now working.',0 ;===============================================================================
SECTION stack align=16 vstart=0 resb 256
ss_pointer: ;===============================================================================
SECTION program_trail
program_end:
以上就是全部的代码了(加载器采用第八章的)
也不知道我这个插件怎么了,显示出的源码歪歪扭扭,没有对齐好吧,咱们就凑合看吧。
2.用户程序结构图
3.中断处理程序
最开始的部分是头部,严格遵循第八章作者约定的格式,我们就不多说了。
;===============================================================================
SECTION code align=16 vstart=0 ;定义代码段(16字节对齐)
new_int_0x70:
push ax
push bx
push cx
push dx
push es
这里就开始中断处理程序了。首先是把用到的寄存器入栈,这是必须的。
.w0:
mov al,0x0a ;阻断NMI。当然,通常是不必要的
or al,0x80
out 0x70,al
in al,0x71 ;读寄存器A
test al,0x80 ;测试第7位UIP
jnz .w0 ;以上代码对于更新周期结束中断来说
;是不必要的
xor al,al
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(秒)
push ax mov al,2
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(分)
push ax mov al,4
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(时)
push ax
这段代码要细说,有很多新知识。
(1)CMOS RAM
在外围设备控制芯片(ICH)内部,集成了实时时钟电路(RTC)和两小块由互补金属氧化物(CMOS)材料组成的静态存储器(CMOS RAM)。实时时钟电路负责计时,而日期和时间的数值则存储在这块存储器中,它们由电脑主板上的一个小纽扣电池提供能量。
日期和时间信息存储在CMOS RAM中,通常CMOS RAM有128个存储单元,而日期和时间信息只占了一小部分容量,其余空间则保存整机的配置信息。
RTC芯片由一个频率为32.768kHz的晶振驱动,经过分频后,用于对CMOS RAM进行每秒一次的时间刷新。
表格9-1 CMOS RAM中的时间信息
偏移地址 |
内容 |
偏移地址 |
内容 |
0x00 |
秒 |
0x07 |
日 |
0x01 |
闹钟秒 |
0x08 |
月 |
0x02 |
分 |
0x09 |
年 |
0x03 |
闹钟分 |
0x0a |
寄存器A |
0x04 |
时 |
0x0b |
寄存器B |
0x05 |
闹钟时 |
0x0c |
寄存器C |
0x06 |
星期 |
0x0d |
寄存器D |
CMOS RAM的访问,需要两个端口:0x70是索引端口,用来指定内存单元;0x71是数据端口,用来读写相应单元里的内容。
举例:
mov al,2
out 0x70,al ;指定内存单元为2
in al,0x71 ;读RTC当前时间(分)
需要说明的是,从很早的时候开始,端口0x70的最高位是控制NMI中断的开关,当它为0时,允许NMI中断;为1时,阻断所有的NMI信号。其他7个bit,实际上用来指定CMOS RAM单元的索引号。
作者为了简化问题,所以在访问RTC时,直接关闭NMI,访问结束后,再打开NMI(不管它之前是不是打开的)。
查阅资料,有的朋友说“访问CMOS RAM可能导致产生NMI,所以需要关闭NMI。”
还有一点要注意:CMOS RAM中保存的日期和时间,默认是8421 BCD编码,也就是用0000~1001分别代表它所对应的十进制数。
.w0:
mov al,0x0a ;访问寄存器A
or al,0x80 ;阻断NMI
out 0x70,al
in al,0x71 ;读寄存器A
test al,0x80 ;测试第7位UIP
jnz .w0 ;以上代码对于更新周期结束中断来说是不必要的
test al,0x80 ,这句是测试寄存器A的bit7
正如书上155页所说:
CMOS RAM中的时间和日期会由RTC周期性地更新,在此期间,用户程序不应当访问它们。
寄存器A的bit7为0时,表示更新周期至少在488us内不会启动。换句话说,此时访问时间信息是安全的。
寄存器A的bit7为1时,表示正处于更新周期或者马上就要启动。
可以看到,上面的代码就是反复测试寄存器A的bit7,如果是0,可以向下执行。
xor al,al
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(秒)
push ax
这段代码很好理解,就是读出秒,并且把结果压栈(压栈时为了之后显示在屏幕上)
mov al,2
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(分)
push ax mov al,4
or al,0x80
out 0x70,al
in al,0x71 ;读RTC当前时间(时)
push ax
道理同上。
mov al,0x0c ;寄存器C的索引。且开放NMI
out 0x70,al
in al,0x71 ;读一下RTC的寄存器C,否则只发生一次中断
;此处不考虑闹钟和周期性中断的情况
这里要说一下寄存器C,这个寄存器是只读寄存器。可以通过读取这个寄存器,知道中断是否发生,如果发生,还可以知道中断原因。
寄存器C是8位寄存器。
[3:0]:保留;
[7]:中断请求标志,周期性中断/闹钟中断/更新结束中断,任何一种发生都会使这位置1;
[6]:周期性中断标志,置1则表示发生了周期性中断
[5]:闹钟中断标志,置1则表示发生了闹钟中断
[4]:更新结束中断标志,置1则表示发生了更新结束中断
注意,对寄存器的读操作将导致[7:4]清零。在中断发生后,我们应该读取这个寄存器,将其清零,否则同样的中断不再产生。
(2)把BCD码转换为ascii码
前面的代码中,把时分秒都读取出来并且压栈了。下一步的工作就是出栈,在屏幕上显示。前文已经说过,CMOS RAM中保存的日期和时间,默认是8421 BCD编码,所以我们可以利用一个过程,把BCD编码转换成与其对应的ascii码。
bcd_to_ascii: ;BCD码转ASCII
;输入:AL=bcd码
;输出:AX=ascii
mov ah,al ;分拆成两个数字
and al,0x0f ;仅保留低4位
add al,0x30 ;转换成ASCII shr ah,4 ;逻辑右移4位
and ah,0x0f
add ah,0x30 ret
举个例子来说吧,比如前面我们读取了小时到AL中,比如是12时,那么al=00010010b;前文我们压栈是把AX压进去,也就是说AX的低8位(AL)是有用的。现在我们需要调用这个过程,把00010010b转换成0x3132(因为字符‘1’对应的ASCII码是0x31,字符‘2’对应的ASCII码是0x32)。
mov ah,al ;分拆成两个数字
and al,0x0f ;仅保留低4位(就是个位)
add al,0x30 ;把个位转换成ASCII
shr ah,4 ;逻辑右移4位 ,ah中是十位数字
and ah,0x0f
add ah,0x30 ;把十位转换成ASCII
OK,这样之后,AX的高八位就是十位的ASCII,低八位就是个位的ASCII;
(3)把时间信息显示在屏幕上
mov ax,0xb800
mov es,ax pop ax
call bcd_to_ascii
mov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示 mov [es:bx],ah
mov [es:bx+2],al ;显示两位小时数字 mov al,':'
mov [es:bx+4],al ;显示分隔符':'
not byte [es:bx+5] ;反转显示属性
前两句让es指向了显示缓冲区;
pop ax ;小时出栈
call bcd_to_ascii ;转为ASCII码
mov bx,12*160 + 36*2 ;从屏幕上的12行36列开始显示
mov [es:bx],ah ;显示小时的十位
mov [es:bx+2],al ;显示小时的个位
mov al,':'
mov [es:bx+4],al ;显示分隔符':'
not byte [es:bx+5] ;反转显示属性
其实前两句可以写成
mov [es:bx+4],':’ ;显示分隔符':'
not是按位取反指令,假如之前属性是0x07(黑底白字),那么Not之后就是0xf8(闪烁白底灰色字)。
pop ax
call bcd_to_ascii
mov [es:bx+6],ah
mov [es:bx+8],al ;显示两位分钟数字 mov al,':'
mov [es:bx+10],al ;显示分隔符':'
not byte [es:bx+11] ;反转显示属性 pop ax
call bcd_to_ascii
mov [es:bx+12],ah
mov [es:bx+14],al ;显示两位小时数字
上面的代码同理。
mov al,0x20 ;中断结束命令EOI
out 0xa0,al ;向从片发送
out 0x20,al ;向主片发送
书上162页已经说明:在中断处理过程的结尾,我们要显式地向8259芯片写中断结束命令EOI(至于具体原因,可以参考361页,图17-17:8259A的初始化命令字)。如果外部中断是8259主片处理的,那么仅发送给主片即可,端口号是0x20;如果外部中断是由从片处理的,那么命令既要发给主片也要发给从片,端口号是0xa0. 中断结束命令的代码是0x20.
pop es
pop dx
pop cx
pop bx
pop ax iret
寄存器出栈,用iret命令返回。
4.主程序
(1)初始化
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer
mov ax,[data_segment]
mov ds,ax mov bx,init_msg ;显示初始信息
call put_string mov bx,inst_msg ;显示安装信息
call put_string
这就是程序的入口了。首先,设置栈段,栈段被安排在整个程序的末尾,保留了256字节。
SECTION stack align=16 vstart=0 resb 256
ss_pointer: ;===============================================================================
SECTION program_trail
program_end:
之后,设置好DS,令其指向数据段;然后显示一些信息。
(2)中断初始化和安装
书上158页说:在计算机启动期间,BIOS会初始化中断控制器,将主片的中断号设为从0x08开始,从片的从0x70开始。从上图可以看出来,实时时钟连到了从片的IR0,也就是说实时时钟的中断号是0x70.
mov al,0x70
mov bl,4
mul bl ;计算0x70号中断在IVT中的偏移
mov bx,ax cli ;防止改动期间发生新的0x70号中断
前文已经说过:
中断向量在中断向量表中的位置=中断类型号×4
N*4的字单元存放偏移地址;
N*4+2的字单元存放段基址。
我们已经知道中断类型号是0x70了,下面要计算它在中断向量表中的位置(也就是计算0x70*4):用乘法指令, AX=AL*r8; 前四句执行后,BX中就是0x70号中断向量在向量表中的偏移。
cli这个指令用来清除IF位标志,相当于屏蔽外部中断。因为在修改中断向量表时,如果表项信息只修改了一部分,这时候发生0x70号中断,将会产生不可预料的问题。
push es
mov ax,0x0000
mov es,ax
mov word [es:bx],new_int_0x70 ;偏移地址。 mov word [es:bx+2],cs ;段地址
pop es
将ES压栈(暂时保存),并使它指向中断向量表所在的段,把偏移地址设置为new_int_0x70 ,把段基地址设置为CS。最后恢复ES。
mov al,0x0b ;RTC寄存器B
or al,0x80 ;阻断NMI
out 0x70,al
mov al,0x12 ;设置寄存器B,禁止周期性中断,开放更
out 0x71,al ;新结束后中断,BCD码,24小时制
上面的代码用来设置寄存器B;寄存器B与本实验相关的位有:
[7]: 0表示更新周期每秒都会发生;1表示中止当前的更新周期,此后也不再产生更新周期;
[6]: 0表示禁止周期性中断,1表示允许周期性中断;
[5]: 0表示闹钟中断禁止,1表示闹钟中断允许;
[4]: 0表示禁止更新结束中断,1表示允许更新结束中断;
[3]:该位空着不用;
[2]:数据模式,0表示BCD,1表示2进制;
[1]: 小时格式,0表示12小时制(bit7为0时表示AM,为1表示PM,举例:在BCD模式下,10010001b表示上午11点),1表示24小时制;
[0]:该位空着不用;
从代码可以看出,我们写入寄存器B的值是0x12,也就是:
[7]:0,允许更新周期发生;
[6]:0,禁止周期性中断;
[5]:0,禁止闹钟中断;
[4]:1,允许更新结束中断;
[3]:0
[2]:0,BCD模式
[1]:1,24小时制
[0]:0
mov al,0x0c
out 0x70,al
in al,0x71 ;读RTC寄存器C,复位未决的中断状态
读寄存器C, 使之开始产生中断信号。注意,在向端口0x70写入al的同时,也打开了NMI,因为这是最后一次在主程序中访问RTC。到此,RTC芯片设置完毕。
in al,0xa1 ;读8259从片的IMR寄存器
and al,0xfe ;清除bit 0(此位连接RTC)
out 0xa1,al ;写回此寄存器 sti ;重新开放中断
8259A内部有一个中断屏蔽寄存器,如下图所示:
IMR是一个8位的寄存器,位0-7对应着引脚中断IR0-IR7;如果对应的位为0,则允许中断;为1,则屏蔽中断。
我们通过端口0xa1读取从片的IMR寄存器,用and指令清除bit0(其他位保持原样),然后再写回去。这样,关于中断的初始化就完成了。
最后,sti指令将IF置1,打开中断。从这时候开始,随时发生的中断就可以被处理了。
mov bx,done_msg ;显示安装完成信息
call put_string mov bx,tips_msg ;显示提示信息
call put_string
显示一些信息,表示中断设置和安装已完成。
mov cx,0xb800
mov ds,cx
mov byte [12*160 + 33*2],'@' ;屏幕第12行,33列
在屏幕12行33列显示一个“@”;
.idle:
hlt ;使CPU进入低功耗状态,直到用中断唤醒
not byte [12*160 + 33*2+1] ;反转显示属性
jmp .idle
hlt是停机指令,使程序停止运行。这时候处理器进入暂停状态,不执行任何操作。当复位线上有复位信号、CPU响应非屏蔽中断、CPU响应可屏蔽中断3种情况之一发生时,CPU就会脱离暂停状态,执行hlt的下一条指令。
代码分析就到这里吧,下次我们看一下运行结果。
8086实时时钟实验(一)——《x86汇编语言:从实模式到保护模式》05的更多相关文章
- 8086实时时钟实验(二)——《x86汇编语言:从实模式到保护模式》读书笔记06
上次我们说了代码,这次我们说说怎样看到实验结果. 首先编译源文件(我的源文件就在当前路径下,a盘和c盘在上一级目录下): nasm -f bin c08_mbr.asm -o c08_mbr.bin ...
- 8086键盘输入实验——《x86汇编语言:从实模式到保护模式》读书笔记07
1.BIOS中断 我们可以为所有中断类型自定义中断处理过程,包括内部中断.硬件中断和软中断. BIOS中断,又称BIOS功能调用,主要是为了方便地使用最基本的硬件访问功能.通常,为了区分针对同一硬件的 ...
- 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16
一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...
- 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17
(十)保护模式下的栈 ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 mov cx,00000000000_11_000B ;加载堆栈段选择子 mov ss,cx mov esp,0x7c00 ...
- 进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14
首先来段题外话:之前我发现我贴出的代码都没有行号,给讲解带来不便.所以从现在起,我要给代码加上行号.我写博客用的这个插入代码的插件,确实不支持自动插入行号.我真的没有找到什么好方法,无奈之下,只能按照 ...
- 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01
本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...
- ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构
★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...
- 存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20
存储器的保护(三) 修改本章代码清单,使之可以检测1MB以上的内存空间(从地址0x0010_0000开始,不考虑高速缓存的影响).要求:对内存的读写按双字的长度进行,并在检测的同时显示已检测的内存数量 ...
- 存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18
本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄 ...
随机推荐
- React进阶篇(1) -- react-router4模块化
本篇内容: 单一的路由无嵌套 多层嵌套路由 获取路径中的参数 按需加载 单一的路由无嵌套 routers.js import Home from 'components/Home'; import N ...
- Kubernetes -- Server 部署
1. Node 节点配置文件 1.1 下载相关的软件 wget https://dl.k8s.io/v1.13.1/kubernetes-server-linux-amd64.tar.gz wget ...
- 跟我一起读postgresql源码(一)——psql命令
进公司以来做的都是postgresql相关的东西,每次都是测试.修改边边角角的东西,这样感觉只能留在表面,不能深入了解这个开源数据库的精髓,遂想着看看postgresql的源码,以加深对数据库的理解, ...
- 犯得错误QAQ
1.十年OI一场空,不开longlong见祖宗(丢过150分) 2.计算完了再开数组,开的足足的.不要少开0:(丢过一共200分) 3.最大值,最小值一定开成7个f.(丢了20分). 4.freope ...
- Zookeeper客户端对比选择_4
Zookeeper客户端对比选择 本文思维导图 使用框架的好处是自带一套实用的API,但是Zookeeper虽然非常强大,但是社区却安静的可怕,版本更新较慢,下面会先从zookeeper原生API的不 ...
- shell-003:用for循环统计内存使用量
shell-100主要是用于练习! #!/bin/bash # 统计内存的使用量(这里用ps统计) # 第一步:不打印第一行,这里的sed ‘1d’ 去掉 for n in `ps aux |sed ...
- selenium定位不到元素
selenium定位不到元素时,网上大部分查到都是iFrame的切换问题,然后是多窗口.句柄的处理问题, 在初学是遇到定位不到元素,一直在找上面的问题,发现都不是上面的问题, 后来才发现是页面刷新的问 ...
- Flask之flask_script
flask端口占用 解决方案: lsof -i:5000 #查询是哪个进程占用的 kill PID 杀掉进程 flask_script之Manager类 from flask import Flask ...
- Android SharedPreferences应用实例(记录App的使用次数)
1.介绍 2.使用方法 3.java后台 package com.lucky.test46sharedpreferences_apply; import android.content.SharedP ...
- C++_标准模板库STL概念介绍4-算法
STL包含很多处理容器的非成员函数: sort() copy() find() random_shuffle() set_union() set_intersection() set_differen ...