说实话越计卷作者用了16页(我还是删过的),来讲怎么控制声卡,其实真正归纳起来就那么几点。

★PART1:直接存储访问

1. 总线控制设备(bus master)

  在硬件技术不发达的早期,处理器是最重要的总线主控制设备,它有权决定谁参与总线数据传输。考虑代码片断:mov [0x2000],dx,在执行这条指令时,处理器不但发出地址信号,也发出控制信号,控制信号用来表明该地址是发给内存的,还是发给外部设备的。所有设备都有译码电路,这些译码电路的输入就是地址和控制信号。以上指令执行的时候,内存的译码结果是打开通向总线的数据通路,而外部设备则保持同总线的脱离状态。相反地,in al,0x70指令是发给端口的,内存当然不会工作。而且,只有那个端口号相符的外部设备才会和数据总线连通,其它所有设备都保持同总线的脱离状态。

  在过去的岁月中先后出现过多种不同的总线类型,它们的典型代表就是工业标准结构总线(Industrial Standard Architecture:ISA)。总线不单单是数据线路,还包括地址线路的控制信号线路,规定和数据和地址的宽度,以及各种控制信号的规程和电气特性。控制信号规定了设备之间互相交流的协同的方式,而不同的总线有不同的控制信号规程。ISA是面向单用户和简单应用环境的总线,结构并不复杂。所以,符合ISA信号规程的外部设备都很简单。

  为了对总线有更好的控制能力,人们采用DMA(Direct Memory Access:DMA,也是一个总线设备)来进行协调控制。直接存储器访问的核心器件是DMA控制器(DMA Controller:DMAC)。一台计算机只有一个DMA控制器,由它负责所有外部设备的直接传输协调工作。而且赋予其总线主控能力高于处理器。当然在现在,DMA已经是一种很古老的技术了,即将被淘汰了,现在流行的都是PCI(E)技术。

  在DMA主导的控制环境下,当外部设备需要发起一次针对内存某个区域的数据传输时,应该向DMAC发出请求。DMAC回应此请求,同时告诉处理器不要再使用总线。注意,这是干预处理器的工作,命令它让出总线。接着,由它主导,开始在该外部设备和内存之间直接传输数据。在早期的计算机系统中,DMAC是独立于处理器和外部设备之外的第三方(Thrid party)总线主控器。设备向DMAC发送DMA请求(DMA REQuest:DREQ);如果总线空闲,可以占用,DMAC用DMA确认信号(DMA ACKnology)回应。此后,就可以正常开始DMA传送。

2. Sound Blaster 16声卡

  声卡是数字和声音的转换器件,录音的时候,声波可以推动磁场中的线圈,也可以使处于静电场中的两个电极间距改变,或者使碳精砂的疏密程度发生变化,又或者使压电陶瓷振动,从而产生音频电流。其中涉及模数转换和,这已经在数字电子技术会讲到的,里面涉及到比较复杂的电路构造(如果要深入学习的话),还要一点傅里叶变换啥的。采样率是反应模数转换的一个非常重要的参数,采样率越高,模拟得越精准。声卡有自己的微处理器,即I/O处理器,通常称为数字信号处理器(Digital Singal Processor:DSP)。

Sound Blaster 16声卡支持双声道(立体声)、每声道16位样本、最高44.1KHz的采样和回放。传统上,要访问该声卡,需要通过4个端口,

注:W缓冲区用来接受外部的命令和数据;R缓冲区用来存放供外部读取的数据。

初始化SB-16需要三个步骤:                                                   

1.向0x226号端口写数字0x01,并等待3毫秒;

2.向0x226号端口写数字0x00;

3.典型的SB-16声卡需要100毫秒来初始化自己。在此期间,可以不停地读取0x22e号端口,并判断数据的最高位(第7位)是否为“1”。如果是比特“1”,表明可以从0x22a端口读取声卡的状态。如果从0x22a号端口返回的内容是0xAA,表明声卡已经准备就绪。否则,意味着声卡没有安装,或者端口号不正确。

典型的程序可以写成这样:

  播放声音是采样的逆过程,是连续地将数字转换成模拟信号。同时,将得到模拟信号放大,用来推动扬声器,我们就听到还原后的声音了。为了回放声音,最容易想到的办法是连续不断地向声卡传送数字,直到把所有的数字都传完,这称为直接模式(Direct Mode)。回放的难点不在于数-模转换,这个电路很简单。真正的难点在于如何把握回放的速度,也就是要精确地控制采样率。SB-16只支持8位单声道的直接模式,回放速率由用户决定,声卡不为此负责。需要对计算机系统中的定时器芯片编程,使它定时发出中断信号。定时器芯片是8254,也可以是用来代替8254的高精度事件定时器(High Precision Event Timer:HPET)。

  定时器应当按采样率所要求的间隔定期发出中断信号,每次中断发生时,就往声卡发送一个数字。在直接模式下,采样率没有上限和下限,仅仅取决于定时器中断的频率和中断处理程序的效率。总的来说,直接模式是一个很别扭的回放模式。而声卡已经自定义了很多种回放模式,比如SB-16允许多种能自动控制数据传输的回放模式,但都需要DMA机制,而且同样需要中断。当声卡开始回放时,它会向DMAC发出直接存储器传输请求,在得到允许后,占用总线,从内存中获取数据并自动按设置的采样率进行回放。通常要由声音回放程序提供一小块内存,称为缓冲区。声音数据通常以文件的形式存放在硬盘和光盘上,在回放的时候,才一点一点地读到缓冲区,再从缓冲区传输到声卡。

  传统上,SB-16声卡是连在8259主片的第5个中断输入引脚IR5上的。在计算机启动期间,BIOS程序会初始化8259,将主片的中断号定义成从0x08开始,所以IR5对应的中断号是0x0d。

  设置中断程序和第九章的类似,具体看代码就知道了。

3. 初始化DMA

  每一片82C37A芯片都有个各通道公用的屏蔽寄存器,用来允许或者关断外部DMA请求信号,每个通道都可以单独设置。屏蔽寄存器在主片上的端口号是0x0a;在从片上的端口号是0xd4。

  在82C37A内部,每个通道都有自己的基准地址寄存器和当前地址寄存器。在DMA传送开始前,这两个寄存器的内容应当由软件设置成相同的值,都指向数据在内存中的起始物理地址或者末端物理地址。具体是起始物理地址还是末端物理地址,取决于是正向传送还是反向传送。当前地址寄存器的作用是在DMA传送的过程中提供物理地址。在传送的过程中,每传送一个字节,当前地址寄存器自动加一或者减一,以指向下一个数据所在的位置,而基准地址寄存器的内容始终不变。

  数据在内存中所占用的区域通常称为缓冲区(buffer),根据需要,可大可小,但应当始终保持不变,它的起始物理地址或者末端物理地址就是要设置到基准地址寄存器和当前地址寄存器的数值。内存空间从来都是有限的,缓冲区可以定义得小一点,数据量大的时候,可以分批进行。比如说,声音文件位于硬盘上,大小是64KB,可以在内存中定义2KB的缓冲区,每次从硬盘上读取512个字节到缓冲区,再从缓冲区通过DMA传送到声卡,共分32个批次。82C37A支持自动预置模式,如果允许这种模式,那么,每当一个批次的数据传送完毕之后,它会自动用基准地址寄存器的内容来初始化当前地址寄存器,这就是基准地址寄存器的作用。每个DMA通道都有自己的基准字数寄存器和当前字数寄存器。在DMA传送开始前,软件应当把它们设置成相同的内容。当DMA传送开始时,每传送一个字节,当前字数寄存器的内容减一,而基准字数寄存器的内容始终不变。如果当前字数寄存器的内容为零,就说明一个批次传送完毕,82C37A芯片会产生一个传送过程结束的信号。同时,如果允许自动预置功能,82C37A就会重新把基准字数寄存器的内容写入当前字数寄存器。

  首先要设置DMA缓冲区的起始地址和数据长度。DMA传送是自动进行的,它要求的是一个物理地址,也就是真实的地址(而不是逻辑地址)。82C37A主片的先后触发器口地址为0x0c,通过向该端口写入一个任意值,可以将它初始化到一个确定的状态。此后,第一次向地址寄存器和字数寄存器写入时,对应的是低字节;第二次写入时,对应的是高字节要设置82C37A主片通道1的基准地址寄存器和当前地址寄存器,可以通过端口0x02。

  不过,当前地址寄存器确实只有16位,不足以形成20位地址。所以,每个82C37A的DMA通道还各自有一个8位的页面寄存器,使用它,可以提供20位物理地址的高4位,主片通道1的页面寄存器使用端口号是0x83接下来设置82C37A主片通道1的基准字数寄存器和当前字数寄存器,它们对应的端口号是0x03。DMA传送有好几种方式,分为单字节操作、数据块操作、请求操作和级联方式。单字节操作方式是,先由外部设备进行DMA请求,获得响应后,82C371占用总线,操作一个字节,然后交还总线控制权给处理器。即使是有很多数据,每操作一个字节,都要按以上步骤进行。

  表面上看起来,这种操作方式不会很快。但事实上,它依然是很快的,毕竟用它来传送数据时,不需要处理器中转。而且处理器每次接到总线请求时,会立即在当前总线周期结束时让出总线。当处理器接到DMA请求时,它可能正在执行指令,可能正在按指令的要求访问总线(访问内存和I/O设备)。DMA会先把当前任务完成再把总线开放给处理器,如果当时并未访问总线,它可以立即让出总线控制权。数据块操作方式是,一旦DMA操作开始了,DMA控制器就一直占用总线,直到操作完成。在此期间,即使外部设备的DMA请求变得无效,82C37A也一直占用总线,暂停操作,直至DMA请求变为有效。

  请求操作的方式是,是以否有DMA请求来决定,如果有DMA请求,则占用总线并进行DMA操作;当DMA请求无效或者操作完成时,释放总线。

  设定82C37A具体的做法是,向主片或者从片的端口发送命令代码。主片上的端口号是0x0b;从片的端口号是0xd6。

  校验操作在DMA操作期间进行的不是数据传送,而是对数据的正确性进行校验。

4. 启动音频播放

  接着初始化和设置声卡。SB-16有多种播放模式,每种模式有不同的设置要求。因为教材采用的波形文件是8位单声道,采样率8000Hz,所以仅采用和介绍8位单声道自动初始化传输模式(8 bit Mono Auto-initialize Transfer)。

  在这种模式下,DMA控制器也应当设置为自动预置状态,每当指定数量的字节传送之后,当前字数寄存器恢复为和基准字数寄存器一样。而对于声卡来说,需要在播放前设置数据块的大小为DMA缓冲区的一半。回放开始后,每次播放的数据量达到这个数值时, 声卡将会发出一个中断信号,使程序可以继续用新的数据填充已回放的区域。

首先是设置声卡回放速率时间常量。SB-16给出的计算方法是

时间常量 = 65536 - (256 000 000/(声道数 × 采样率))

  计算出来的数值是16位的,在寄存器AX中,但SB-16只使用其高8位。

  最后向0x22c端口写入命令字0xd1以打开扬声器,并且向0x22c端口写入命令字0x1c,以正式启动音频回放。

5. 音频回放中断处理

  一旦下达了命令字0x1c,声卡就开始启动音频回放,这个过程是独立于处理器的。当它播放的内容长度达到DMA缓冲区的一半时,就会产生中断信号,一般情况下,中断号为5。退出自动初始化模式的方法是向0x22c端口写命令字0xda。中断发生时,如果声卡正在播放缓冲区的后半部分。当声卡接到此命令时,它不会立即停止工作,只有在当前数据块播放结束之后,它才会真正退出自动初始化模式。关闭扬声器的命令是向0x22c端口发送0xd3。

  在正常的声卡中断处理过程中,应当读一下0x22f端口,作为对声卡中断的应答。

6. 中断号的总结

  不过说实话,这一章的中断号实在是太多了,有必要来总结一下:

★PART2:本章习题

  本章最后的练习要我们采用读取读取windows的wav文件的文件头来获得采样率和声道数以计算时间常量,这个太简单了,我们先来看下wav文件头是个什么

  编译器提供了incbin指令来编译外部文件到二进制(INCluding external BINary files)它的功能是将指定的文件逐字地包含到编译后的结果中,从它在源程序中出现的位置开始。       这直接把文件头读取进来然后把0x16和0x18两个偏移量找到就可以了,但是由于是16位模式,长度上可能会有点限制。

incbin的后面需要三个参数,第一个参数是文件名。举个例子:

incbin “baby.wav”

这将会把baby.wav文件的内容原样包含进最终的编译结果中去。

incbin “baby.wav”,44

这句的意思是,在编译的结果中包含baby.wav文件的内容,但跳过该文件开头的40个字节。

如果该伪指令的后面出现有三个参数,比如

incbin “baby.wav”,44,50000

那么,编译器将会在编译结果中包含baby.wav文件的内容,但跳过该文件开头的44个字节,而且实际上仅仅包含50000个字节。

教材中的声音文件只有57.3KB,所以可以直接在用户程序定义一个数据段来存放声音文件(正常来讲应该是给声音文件专门安排一个段来存放的)。并且也是因为他只有一段,所以我们不必分段传输声音信息给声卡。

还有,上次第八章我的mbr程序都写错了,但是不知道为什么可以驱动第九章的程序,奇怪。(下面程序用第八章的mbr驱动就好了)。

 ;===============================================================================
SECTION header vstart= ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00] ;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code.start ;段地址[0x06] realloc_tbl_len dw (header_end-realloc_begin)/
;段重定位表项个数[0x0a] realloc_begin:
;段重定位表
code_segment dd section.code.start
data_segment dd section.data.start
stack_segment dd section.stack.start header_end:
;===============================================================================
SECTION code align= vstart= ;定义代码段(16字节对齐)
put_string: ;显示字符串(0结尾)
;输入:DS:BX=串地址
push ax
push bx
push si mov ah,0x0e ;INT 0x10第0x0e号功能
mov si,bx ;字符串起始偏移地址
mov bl,0x07 ;显示属性 .gchr:
mov al,[si] ;逐个取要显示的字符,0x10功能就是al是要显示的儿子字符,ah功能号为0x0e
or al,al ;如果AL内容为零,则
jz .rett ;跳转到过程返回指令
int 0x10 ;BIOS字符显示功能调用
inc si ;下一个字符
jmp .gchr .rett:
pop si
pop bx
pop ax
ret
;-------------------------------------------------------------------------------
write_dsp:
push dx
push ax mov dx,0x22c ;不停读取0x22c端口,直到他的第七位变成1为止
.@22c:
in al,dx
and al,1000_0000b
jnz .@22c pop ax
out dx,al
pop dx ret
;-------------------------------------------------------------------------------
read_dsp:
push dx
mov dx,0x22e
.@22e:
in al,dx
and al,0x80 ;监视22e端口的位7,直到它变成1
jz .@22e mov dx,0x22a
in al,dx ;此时可以从22a端口读取数据,返回值必须是0xaa才是对头的 pop dx
ret
;-------------------------------------------------------------------------------
dsp_interrupt: ;中断处理过程
push ax
push bx
push dx ;退出自动初始化模式
mov al,0xda
call write_dsp ;关闭扬声器
mov al,0xd3
call write_dsp mov bx,done_msg
call put_string
mov bx,okay_msg
call put_string mov dx,0x22f ;DSP中断应答
in al,dx ;发送EOI命令到中断控制器(主片)
mov al,0x20 ;中断结束命令EOI
out 0x20,al ;发给主片 pop dx
pop bx
pop ax iret
;-------------------------------------------------------------------------------
start:
mov ax,[stack_segment]
mov ss,ax
mov sp,ss_pointer mov ax,[data_segment]
mov ds,ax mov dx,0x226 ;第一步,先写“1”到复位端口
mov al,
out dx,al xor ax,ax
_wait_int:
dec ax
loop _wait_int out dx,al ;第二步,写“0”到复位端口 call read_dsp
cmp al,0xaa
je _setup _error:
mov bx,err_msg ;如果返回值不是0xaa那么就说明声卡没有安装
call put_string
jmp _idle ;没有声卡就直接停机吧 _setup:
mov bx,intr_msg
call put_string mov bx,0x0d
shl bx, ;8259A的IR5引脚的中断号,找到其在IVT的偏移地址 cli
push es
xor ax,ax
mov es,ax
mov word[es:bx],dsp_interrupt ;安装相应的中断处理过程
mov [es:bx+],cs
pop es
sti in al,0x21 ;8259主片的IMR
and al,1101_1111B ;开放IR5
out 0x21,al mov bx,done_msg
call put_string
mov bx,dma_msg
call put_string mov dx,0x0a ;注意0x0a是DMA的主片接口
mov al,00000_1_01B ;禁止操作,禁止DMA主片通道1
out dx,al mov ax,ds
mov bx,
mul bx
add ax,voice_data
adc dx,
mov bx,dx ;BX:AX为20位的基地址 push ax ;先存一下ax的内容
xor al,al
out 0x0c,al ;DMAC1高低触发器清零
pop ax ;第一次写入对应的是低字节,第二次对应的是高字节
mov dx,0x02 ;写通道1基址与当前地址寄存器
out dx,al ;低8位DMA地址
mov al,ah
out dx,al ;高8位DMA地址 ;0x83这个端口是82C37A的8位页面寄存器
mov dx,0x83 ;写DMA通道 1 的页面寄存器
mov al,bl
out dx,al mov dx,0x03 ;写通道1的基字计数与当前字计数器
mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小
dec ax ;DMA要求实际大小减一
out dx,al ;缓冲区长度低8位
mov al,ah
out dx,al ;缓冲区长度高8位 mov al,0101_1001b ;设置DMAC1通道1工作方式:单字节传送/
out 0x0b,al ;地址递增/自动预置/读传送/通道1 mov dx,0x0a ;DMAC1屏蔽寄存器
mov al, ;允许通道1接受请求
out dx,al mov al,0x40 ;直接往0x22c接口写入0x40,表示准备写入回放速率时间常量
call write_dsp mov ax,[voice_data+0x16] ;声道数量
mov bx,[voice_data+0x18] ;采样率
mul bx ;dx:ax为采样率*声道数量,只用ax
mov cx,ax
mov ax,
xor dx,dx
mov bx,
mul bx
;dx:ax为256000000
div cx
;ax为(256000000/ans)
xor dx,dx
mov dx,
sub dx,ax
inc dx
mov ax,dx xchg ah,al ;只使用结果的高8位
call write_dsp mov bx,done_msg
call put_string mov al,0x48
call write_dsp ;往0x22c写入0x48,表示我们要写入数据块的长度
;对于8位段声道音频来说,数据块的长度是DMA缓冲区长度的一半减1
;这样做的目的是允许声卡在播放到一半的时候,发出一个中断,
;以方便立即开始填充已经回放的数据块,避免声音中断
mov ax,init_msg-voice_data ;数据块(当缓冲区用)的大小
shr ax, ;长度设为DMA的一半
dec ax
call write_dsp ;写低字节
xchg ah,al
call write_dsp
mov al,0xd1 ;打开喇叭输出
call write_dsp
mov al,0x1c ;启动DSP的传输的播放
call write_dsp mov bx,play_msg
call put_string
_idle:
hlt
jmp _idle ;注意一定要jmp
;-------------------------------------------------------------------------------
SECTION data align= vstart=
voice_data incbin "baby.wav",
init_msg db 'Initializing sound blaster card...',
intr_msg db 'Installing interrupt vector...',
dma_msg db 'Setup DMA ...',
done_msg db 'Done.',0x0d,0x0a,
play_msg db 'Voice is playing now...',
okay_msg db 'Finished,stop.',
err_msg db 'Sound card init failed.',
;===============================================================================
SECTION stack align= vstart=
times db
ss_pointer:
;===============================================================================
SECTION program_trail
program_end:

ASM:《X86汇编语言-从实模式到保护模式》越计卷:实模式下对DMA和Sound Blaster声卡的控制的更多相关文章

  1. ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构

    ★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...

  2. ASM:《X86汇编语言-从实模式到保护模式》第11章:进入保护模式

    ★PART1:进入保护模式 1. 全局描述符表(Global Descriptor Table,GDT)        32位保护模式下,如果要使用一个段,必须先登记,登记的信息包括段的起始地址,段的 ...

  3. ASM:《X86汇编语言-从实模式到保护模式》5-7章:汇编基础

    第5-7章感觉是这一本书中比较奇怪的章节,可能是作者考虑到读者人群水平的差异,故意由浅入深地讲如何在屏幕上显示字符和使用mov,jmp指令等等,但是这样讲的东西有点重复,而且看了第六,第七章以后,感觉 ...

  4. 存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18

    本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄 ...

  5. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

  6. 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01

    本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...

  7. 程序的载入和运行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25

    程序的载入和运行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文最终把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都 ...

  8. 存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20

    存储器的保护(三) 修改本章代码清单,使之可以检测1MB以上的内存空间(从地址0x0010_0000开始,不考虑高速缓存的影响).要求:对内存的读写按双字的长度进行,并在检测的同时显示已检测的内存数量 ...

  9. 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17

    (十)保护模式下的栈 ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 mov cx,00000000000_11_000B ;加载堆栈段选择子 mov ss,cx mov esp,0x7c00 ...

随机推荐

  1. 2015年3月阿里内推(c++研发)实习生电面经历

    2015年3月开学开始就听说阿里会有内推,果不其然在师兄的引荐下推了菜鸟网络,但是在学校的BBS上看到了阿里云部门,而且要会C++,这使我更感兴趣,重新选择了方向,当然最后选择了阿里云.在此分享一下三 ...

  2. C#进阶系列——动态Lamada(二:优化)

    前言:前几天写了一篇动态Lamada的文章C#进阶系列——动态Lamada,受园友xiao99的启发,今天打算来重新优化下这个动态Lamada的工具类.在此做个笔记,以免以后忘了. 一.原理分析 上篇 ...

  3. Windows phone应用开发[22]-再谈下拉刷新

    几周之前在博客更新一篇Windows phone应用开发[18]-下拉刷新 博文,有很多人在微博和博客评论中提到了很多问题.其实在实际项目中我基于这篇博文提出解决问题思路优化了这个解决方案.为了能够详 ...

  4. Java构造和解析Json数据

    BaseResult wyComany = propertyService.getWyCompanyById(CommunityInfos.getWyCompany());//这里返回的是json字符 ...

  5. Java 中 手动抛出异常: throw new Exception("错误信息") 错误信息的获得

    当然需要先用try catch捕获,但注意new Exception("")括号里的字符串其实是异常原因,所以获取是要用ex.getCause().getMessage() int ...

  6. 走进AngularJs(二) ng模板中常用指令的使用方式

    通过使用模板,我们可以把model和controller中的数据组装起来呈现给浏览器,还可以通过数据绑定,实时更新视图,让我们的页面变成动态的.ng的模板真是让我爱不释手.学习ng道路还很漫长,从模板 ...

  7. Linux的学习日记

    CURL 在Ubuntu下尝试安装通过包安装Node.js的时候有这样一种安装方法, 那么有必要学学CURL的知识了. curl是利用URL语法在命令行方式下工作的开源文件传输工具.它被广泛应用在Un ...

  8. Thinking in java学习笔记之迭代器

    Map<String,String> map = new HashMap<String,String>(); List list = new ArrayList(); list ...

  9. IDC机房线路质量测试方案

    1.测试节点: 北京:123.206.*.* 上海:139.196.*.* 广州:119.29.*.* 汕头:125.91.*.* 香港:103.20.*.* 美国:198.52.*.* 测试服务器: ...

  10. SDWebImage使用及原理

    第一步,下载SDWebImage,导入工程.github托管地址https://github.com/rs/SDWebImage 第二步,在需要的地方导入头文件 1 #import "UII ...