详解ARM的AMBA设备中的DMA设备(Linux驱动之DMA)

在开始分析代码之前,先简要介绍一下DMA的基础知识。

1.       什么是DMA

DMA,Direct Memory Access,直接内存访问。

既然叫 直接内存访问,那么相对应地,应该就有 “间接的内存访问”。

间接的内存访问,我的理解是,就是指最常见的,我们利用CPU的指令,去从一个内存地址中读出数据,然后写到另外一个内存地址中,完成对应的赋值操作。

此过程,完全都是CPU去操作的,如果是单个这样的数据读取和写入,还没啥,但是如果数据量很大,比如我们用memcpy(addr1,addr2,1024)去从地址addr1地址开始,拷贝1024个字节到内存addr2处,那么CPU这段时间,就不要干别的事情了,就一直这么的给你读取,写入数据吧,另外的还有,常见于驱动中的,尤其是涉及到和外设打交道,我们让CPU从内存一个地址,读取了一个数据,然后写入到某个设备的FIFO或者DATA寄存器中,咋写入之前,常常会等待FIFO不是满的,然后才能写入数据,要从FIFO中读取数据,要等到FIFO不是空,才能读取,这样来来回回,会比较消耗资源。

鉴于此,才出现了DMA这个硬件设备,专门设计用来处理这些相对用CPU去操作这样的事情,效率很低,换做专门的硬件的DMA来负责数据的读取和写入,释放了CPU这个苦力,可以让,在DMA忙着数据传输的过程中,CPU去忙其他更重要的事情。而专门的DMA硬件负责这样的数据传输,效率也会更高。

之所以这样,才叫做内存直接访问的。

2.       DMA的一些基础概念

DMA传输,总的来说就是,

硬件上,会有对应的控制寄存器ctrl和配置寄存器config,

比如你想要从内存一个地址addr传输,N个字word(32bit)的数据到设备dev上,

那么你就要先去根据你的请求,去配置config寄存器,首先是传输方向,

是DMA_TO_DEVICE,然后是源地址source address是你的内存地址addr,

和目标destination address是你的dev的DATA寄存器地址,

然后要传输额transfer size是N个,每个位宽是32bit,

将源地址,目标地址,位宽,DMA传输方向设置好,

整理成一个结构,专有名称叫做LLI(Link List Item),把这个LLi设置到ctrl里面。

然后去enable DMA,DMA就可以按照你的要求,把数据传输过去了。

这样的DMA叫做single DMA传输,LLI中的next lli的域设置为空,表示就一个LLI要传输。

如果源地址或目标地址是多个分散的地址,叫做scatter/gather DMA,

就要将这些LLI组合一下,即将第一个LLI的next lli那个域,设置成下一个LLI的地址,这样一个个链接起来,最后一个LLI的nex lli的域为空,这样设置好后,将第一个LLI的值写入到ctrl中,DMA就会自动地去执行第一个LLI的数据传输,传完后,发现next lli不为空,就找到next lli的位置,

找到对应的配置,开始这个lli的数据传送,直至传完所有的数据为止。

说完了DMA的由来和基本概念后,下面来分析一下,具体的ARM的PL08x驱动是如何实现的。

*/

/*

* The AMBA DMA API is modelled on the ISA DMA API and performs

* single asynchronous transfers between a device and memory

* i.e. some platform fixed device address and a driver defined memory address

/*

此AMBA DMA驱动,基于ISA DMA的API,好像应该就是那个DMA engine的架构吧,对应的,是这两个相关文件:

\include\linux\dmaengine.h

\drivers\dma\dmaengine.c

主要实现了异步传输,细看内部实现,就是,你设置好所有的参数之后,就提交你的请求后,然后此dma驱动会去在合适的时候帮你实现你的dma请求。因此,不保证是立刻就去执行你的请求的,此之所以称作异步。

正如上面的解释,常见的应用就是,

对应某个外设有某个固定的设备地址,一般都是某个FIFO的地址,或者DATA之类的寄存器,然后你的DMA请求是,从内存某个地址传输一定数据到你这个设备的FIFO或者data寄存器,即往你设备里面写数据,或者相反,从你的设备的FIFO地址中,读取一定量数据到内存某个位置,即从你设备里面读取数据。

*/

*

* Memory to peripheral transfer may be visualized as

*   Get data from memory to DMAC

*    Until no data left

*           On burst request from peripheral

*                  Destination burst from DMAC to peripheral

*                  Clear burst request

*    Raise terminal count interrupt

*

* For peripherals with a FIFO:

* Source      burst size == half the depth of the peripheral FIFO

* Destination burst size == width of the peripheral FIFO

/*

关于提交DMA传输请求的时候,对于突发传输大小(burst size)的设置,虽然此处建议对于source burst size,设置成你的FIFO大小的一半,而对于destination burst size,设置为你的FIFO大小等同,但是,实际一般是根据你的外设控制器的设置而去具体设置的,比如你的nand flash控制器有个fifo是36个word,但是,其nand flash controller中关于burst size的说明是,,DMA模式时候,当fifo中小于4个word并且将要写入数据大于32个word的时候,才会发送write
burst信号给dma,要求burst传输,期望一下子传输32个word,这样,一下子传输32个word,写入到nand flash的fifo里面,这样就比dma传输一次一个word,即single word transfer的效率高多了。此时,你的destination burst size,就应该设置成32个word,之前,我以为也可以设置成16,8之类比32小的值,但是除了理论上理解的,没有充分利用硬件的能力、效率低之外,,实际上,驱动并不能正常工作,数据总是少不部分,就是说,硬件上人家有burst的dma请求了,就是已经准备了32个数据让你传,结果你只传输了部分,所以数据就少了一些,就乱了。一般来说,source
burst size,多数和destination burst size相等。具体,还要去看你的设备的datasheet。

*/

*

* (Bursts are irrelevant for mem to mem transfers - there are no burst signals)

*

* Details of each tranfer on a particular channel

* are held in the DMA channel array

* A workqueue uses those details to initiate the actual transfer

* The DMAC interrupt clears the relevant transfer in the channel array

*

* ASSUMES only one DMAC device exists in the system

* ASSUMES default (little) endianness for DMA transfers

*

* Only DMAC flow control is implemented

*

*/

#include <linux/device.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/pci.h>

#include <linux/interrupt.h>

#include <linux/workqueue.h>

#include <linux/dmapool.h>

#include <asm/dma.h>

#include <asm/mach/dma.h>

#include <asm/atomic.h>

#include <asm/processor.h>

#include <linux/amba/bus.h>

#include <linux/dmaengine.h>

#include <asm/cacheflush.h>

#include <linux/amba/pl08x.h>

int ctr_chan[2];

/*

* Predeclare the DMAENGINE API functions

*/

static int  pl08x_alloc_chan_resources(struct dma_chan *chan,

struct dma_client *client);

static void       pl08x_free_chan_resources(struct dma_chan *chan);

static struct     dma_async_tx_descriptor *pl08x_prep_dma_memcpy(

struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,

size_t len, unsigned long flags);

static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(

struct dma_chan *chan, unsigned long flags);

static enum     dma_status pl08x_dma_is_complete(struct dma_chan *chan,

dma_cookie_t cookie, dma_cookie_t *last,

dma_cookie_t *used);

static void       pl08x_issue_pending(struct dma_chan *chan);

static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(

struct dma_chan *chan, struct scatterlist *sgl,

unsigned int sg_len, enum dma_data_direction direction,

unsigned long flags);

static void pl08x_terminate_all(struct dma_chan *chan);

/*

下面这个dma_device是在dmaengine.h中定义的,其就是定义了一个架构,实现了DMA驱动的与具体设备无关的部分,然后提供具体接口,然后你具体的DMA驱动,去实现对应的接口,这样的好处是,省却了你的具体驱动,去关心太多那些通用dma驱动都应该要实现的一些功能等,省却了具体驱动编程者的精力

*/

struct dma_device dmac = {

.device_alloc_chan_resources = pl08x_alloc_chan_resources,

.device_free_chan_resources  = pl08x_free_chan_resources,

.device_prep_dma_memcpy          = pl08x_prep_dma_memcpy,

.device_prep_dma_xor          = NULL,

.device_prep_dma_zero_sum = NULL,

.device_prep_dma_memset           = NULL,

.device_prep_dma_interrupt  = pl08x_prep_dma_interrupt,

.device_is_tx_complete         = pl08x_dma_is_complete,

.device_issue_pending           = pl08x_issue_pending,

.device_prep_slave_sg           = pl08x_prep_slave_sg,

.device_terminate_all            = pl08x_terminate_all,

};

/*

如果_cctl_data,可以从DDI0196中找到对应的详细定义,下面根据datasheet内容和自己的理解,详细阐述一下:

(1)tsize:12

即TransferSize,传输大小,bit0-bit11,共12位。

表示,如果当前传输是DMA控制的情况下,当前此次DMA传输要传输的个数,其中每个具体大小是几个字节,由下面的位宽决定。

在配置完dma,开始传输后,此传输大小从你设置的值,即要传输的数,一点点减少直至0.如果读取此域,得到的值,表示当前还剩下多少个要传输。

不过特殊一点是,如果当前正在进行DMA传输,由于正在传输,所以你读取到的值,并不一定是真正的还剩多少个要传输的。一般用法是,在启用DMA传输,后来又由于传输完成后或者出错等特殊情况又禁用了DMA,此时再去读此域的值,就能真正有效地表示还多少没有传输的。

如果不是DMA控制数据流的传输,(之后会提到,关于DMA数据传输方向,除了DMA_TO_DEVICE,DMA_FROM_DEVICE之外,还有MEM_TO_MEM之类的,非DMA控制数据传输的情况)那么就应该在开始配置的时候,将此域值设置成0.

(2)sbsize:3, dbsize:3

即source burst size和destination burst size,即前面提到的,当设备发送给DMA控制器一个突发传输信号之后,DMA要传输多少个数据,就由此处设置的值决定。

支持的burst size有1,4,8,。。。,128,256。

前面已经解释过了,具体此处需要设置成多少,要根据你的硬件的datasheet中描述的你的硬件的能力去决定。

Datasheet中写的DMACxBREQ信号线,就是设备的控制器,如果支持DMA,都会有此信号线接出来的,这样,接到DMA的对应的DMACxBREQ引脚上,这样,如果设备控制器,发现当前的条件,满足DMA burst传输,比如FIFO一共36个word,发现FIFO中数据>=32个word了,快满了,就会向DMA控制器PL080,通过这个信号线发送burst read请求,然后PL080就会根据你DMA传输前此处设置的值,比如是32,去你的设备的FIFO中,一次性地读取你之前设置的32个word。

其中具体满足什么条件,才会触发DMA burst读取或写入的请求信号,都是对应你的设备的控制器的datasheet中描述的,也就是你的硬件本身的能力决定的。

(3)swidth:3, dwitch:3

即source width和destination width。

具体支持的位宽类型有,byte(8bit),half word(16bit),word(32bit)等。

关于source和destination,解释一下,

比如,你要从你的设备读取数据,即从你设备的FIFO中读取数据到内存某个位置,那么你的设备就是source,此时你的设备的FIFO的位宽,就是source width,比如你FIFO位宽是32bit的,那么此处,根据datasheet,就应该设置为word(32bit)。

(4)smaster:1,dmaster:1

即source master和destination master,当你的传输由DMA控制时,

比如上面说的DMA_FROM_DEVICE,DMA从设备的FIFO中读取数据到内存中,此时,你要设置一下,你的DMA是用哪一个,即source master是谁。因为,此处的DMA的控制者有2个,可以简单理解为,有两个dma控制器,master1和master2,你具体选哪个master来控制你的DMA传输。关于master的选择,后面的代码分析中,会再次提及。

(5)si:1,di:1

即source increment和destination increment,此处可翻译为,源地址递增和目标地址递增,

关于什么叫递增,为何要递增,可以从最开始提到的,DMA的常见应用情况中来解释,

因为常见的DMA传输,

是从某个设备读取数据到某块内存区域,即DMA_FROM_DEVICE,以DMA模式,将数据“From从”你的设备中,读取到某块内存里面;或者将某块内存区域内数据,写入到设备里面,即DMA_TO_DEVICE。

而设备往往都是只有一个DATA寄存器(或是FIFO),此地址是固定不变的,

因此:

对于DMA_FROM_DEVICE,你传输完一个数据了,再传下一个数据的时候,此时你的

Source源,还是你的设备的那个寄存器的地址,没有变化,而目标地址,往往要增加一个你的destination width,比如是一个word,32bit,4个字节,即地址要加4了,对应的就要将destination increment设置成1,表示,你的dma每次传输完,目标地址要增加的。

当然具体增加的大小,由你的设置的destination width的值决定。

相应地,如果是DMA_TO_DEVICE,那么就是DMA将数据从内存中写入到你的设备里面

,每次传输完后,就是源source地址要增加,source increment要设置为1.

同理,如果是MEM_TO_MEM类型的,内存到内存,就是两者都要设置成1了,因为DMA传输完单个数据后,源地址和目标地址都要改变,要增加相应位宽的大小的。

(6)prot

即Protection,保护位,共3bit。

一般很少用到此域,我也没有完全理解,故不多解释,仅简述其义:

Bit0: 0是普通用户模式,1是特权模式;

Bit1: 0是non-bufferable,1是bufferable;

Bit2:0是non-cacheable,1是cacheable.

(7)intr:1

是否启用计数终止中断(Terminal count interrupt)。

关于此位,之前一直很迷惑,后面终于看懂了。

就是说,对于DMA的传输中的单个LLI来说,当前传输完成了,对应的寄存器中的transfer size域的值,也就是从设置的值,递减到0,也就是此计数递减到0,即结束了,即terminal count,而Terminal Count Interrupt,即传输完了,达到了terminal count时候,发送一个中断,告诉设备此次传输完成了。

如果此位被设置为0,那么传输完当前的LLI,就不会发送这个中断了,设置为1,就发送此中断。

*/

/*

* PL08X private data structures  ==== START

*/

struct _cctl_data{

unsigned int tsize:12;

unsigned int sbsize:3;

unsigned int dbsize:3;

unsigned int swidth:3;

unsigned int dwidth:3;

unsigned int smaster:1;

unsigned int dmaster:1;

unsigned int si:1;

unsigned int di:1;

unsigned int prot:3;

unsigned int intr:1;

};

/*

此处之所以定义成union类型,就是方便,在设置好了之后,将此32位的值,直接写入对应的32位的寄存器中。

*/

union _cctl{

struct _cctl_data bits;

unsigned int val;

};

/*

下面这个就是DMA里面最核心的概念,LLI,Link List Item,包括了

(1)源地址;

(2)目标地址;

(3)下一个LLI的地址:如果其为0/NULL/空,说明当前只需要传输一个信息,如果非空,传完当前的LLI,就会跳转到对应的地址,执行下一个LLI的传输;

(4)对应的控制信息cctrl - channel control。

这四个值,会分别写入到对应的四个寄存器。

这样配置好了之后,再去启用DMA,DMA就会按照你的要求,把数据从源地址传送到目的地址。

下面的那个英文解释的意思是,next域,即下一个LLI的地址,其中的bit0,是bus bit,即指示当前用哪个bus,现将datasheet中相关解释截图如下:

图 1 LLI寄存器定义

如图1,对应的bit[0],LM位,就表示了,当前使用哪个AHB master,而真正的LLI的地址,是存放在bit[2-31]。

/*

* An LLI struct - see pl08x TRM

* Note that next uses bit[0] as a bus bit,

* start & end do not - their bus bit info

* is in cctl

*/

struct _lli{

dma_addr_t src;

dma_addr_t dst;

dma_addr_t next;

union _cctl cctl;

};

/*

LLI结构体,存放LLI的dma地址(供DMA控制器访问的) bus_list,和虚拟地址(普通的地址,CPU可以访问的地址),va_list,

*/

struct _chan_lli {

dma_addr_t bus_list;

void *va_list;

};

struct pl08x_driver_data {

/*

记录对dma控制器硬件基地址ioremap之后的,驱动中可以直接访问的那个基地址

加上对应寄存器偏移量,就可以访问寄存器了

*/

void __iomem *base;

/* 此pl08x的DMA控制也是一个amba设备 */

struct amba_device *dmac;

struct pl08x_platform_data *pd;

/* dma内存池,供之后驱动每次的LLI的存放 */

/*

* Single dma_pool for the LLIs

*/

struct dma_pool *pool;

/* 记录已使用内存池的数量,当达到足够大一个值的时候,做一次清理动作 */

int pool_ctr;

/* 一个工作队列,后面可以看到,挂了一个函数,作用是清理释放dma pool */

struct work_struct dwork;

wait_queue_head_t *waitq;

int max_num_llis;

/*

* LLI details for each DMA channel

*/

struct _chan_lli *chanllis;

/* 将DMA 通道相关的信息,打包在这个变量里 */

/* Wrappers for the struct dma_chan */

struct pl08x_dma_chan *chanwrap[MAX_DMA_CHANNELS];

/* List of descriptors which can be freed */

spinlock_t             lock;

};

/*

* PL08X private data structures  ==== END

*/

/* 暂时没有实现Linux中PM 的支持 */

/*

* Power Management.

* PM support is not complete. Turn it off.

*/

#undef CONFIG_PM

#ifdef CONFIG_PM

#else

# define pl08x_do_suspend    NULL

# define pl08x_do_resume    NULL

# define pl08x_suspend         NULL

# define pl08x_resume          NULL

#endif

#ifdef MODULE

# error "AMBA PL08X DMA CANNOT BE COMPILED AS A LOADABLE MODULE AT PRESENT"

/*

a) Some devices might make use of DMA during boot

(esp true for DMAENGINE implementation)

b) Memory allocation will need much more attention

before load/unload can be supported

*/

#endif

struct pl08x_driver_data pd;

/*

* PL08X specific defines

*/

/* Minimum period between work queue runs */

#define PL08X_WQ_PERIODMIN      20

/* 对于其他使用此DMA 的驱动中,会去提交DMA请求,此处就是指一次DMA请求中,最大所允许的传输大小,此处是0x2000= 8192 */

/* Size (bytes) of each buffer allocated for one transfer */

# define PL08X_LLI_TSFR_SIZE       0x2000

/* Maximimum times we call dma_pool_alloc on this pool without freeing */

# define PL08X_MAX_ALLOCS  0x40

/* 后面代码中会看到,对于你的驱动提交的DMA请求,此DMA驱动内部会自动帮你转换成对应的一个个LLI,此数值就是限制一次DMA请求中,最大支持多少个LLI */

#define MAX_NUM_TSFR_LLIS       (PL08X_LLI_TSFR_SIZE/sizeof(struct _lli))

#define PL08X_ALIGN        8

#define PL08X_ALLOC        0

/* 一些全局的寄存器的偏移地址,这些值都是根据datasheet中定义出来的 */

/* Register offsets */

#define    PL08X_OS_ISR            0x00

#define    PL08X_OS_ISR_TC             0x04

#define    PL08X_OS_ICLR_TC   0x08

#define    PL08X_OS_ISR_ERR   0x0C

#define    PL08X_OS_ICLR_ERR 0x10

#define    PL08X_OS_CFG          0x30

#define    PL08X_OS_CCFG        0x10

#define    PL08X_OS_ENCHNS          0x1C

#define    PL08X_OS_CHAN              0x20

#define    PL08X_OS_CHAN_BASE    0x100

/* DMA控制器,有很多个通道channel,每个channel都对应有自己的一些寄存器,下面就是这些寄存器的地址偏移和位域值的含义 */

/* Channel registers */

#define    PL08X_OS_CSRC        0x00

#define    PL08X_OS_CDST        0x04

#define    PL08X_OS_CLLI          0x08

#define    PL08X_OS_CCTL         0x0C

/* register masks */

#define    PL08X_MASK_CFG            0xFFFFFFF1

#define    PL08X_MASK_EN              0x00000001

#define    PL08X_MASK_CLLI           0x00000002

#define    PL08X_MASK_TSFR_SIZE 0x00000FFF

#define    PL08X_MASK_INTTC  0x00008000

#define    PL08X_MASK_INTERR      0x00004000

#define    PL08X_MASK_CCFG          0x00000000

#define    PL08X_MASK_HALT          0x00040000

#define    PL08X_MASK_ACTIVE      0x00020000

#define    PL08X_MASK_CEN            0x00000001

#define    PL08X_MASK_ENCHNS     0x000000FF

#define PL08X_WIDTH_8BIT     0x00

#define PL08X_WIDTH_16BIT    0x01

#define PL08X_WIDTH_32BIT    0x02

/* 下面这几个宏定义,好像没用到 */

/*

* Transferring less than this number of bytes as bytes

* is faster than calculating the required LLIs....

* (8 is the real minimum

* >7 bytes must have a word alignable transfer somewhere)

*/

#define PL08X_BITESIZE           0x10

/* 下面这个宏定义,好像也没用到,具体的流控制flow control的设置以及宏定义,在对应头文件中:

#define PL08X_CCFG_FCTL_MEM_TO_MEM         (0)

#define PL08X_CCFG_FCTL_MEM_TO_PERI         (1)

…….

*/

/*

* Flow control bit masks

*/

#define PL08X_FCMASK_M2M_DMA      0x00000000

#define PL08X_FCMASK_M2P_DMA       0x00000800

#define PL08X_FCMASK_P2M_DMA       0x00001000

#define PL08X_FCMASK_P2P_DMA 0x00001800

#define PL08X_FCMASK_P2P_DST   0x00002000

#define PL08X_FCMASK_M2P_PER  0x00002800

#define PL08X_FCMASK_P2P_PER   0x00003000

#define PL08X_FCMASK_P2P_SRC   0x00003800

/* Max number of transfers which can be coded in the control register */

#define PL08X_MAX_TSFRS             0xFFF

#define PL08X_CODING_ERR    0xFFFFFFFF

/* 根据设置的值,解码出实际位的宽度 */

static unsigned int pl08x_decode_widthbits(unsigned int coded)

{

/*

根据datasheet中的解释:

所以,目前只支持8.,16,32字节,位域的值分别是0,1, 2,所以此处判断小于3,才是有效的,然后1<<coded,得到的结果分别是1,2,4个字节。

*/

if (coded < 3)

return 1 << coded;

dev_err(&pd.dmac->dev, "%s - illegal width bits 0x%08x\n", __func__,

coded);

return PL08X_CODING_ERR;

}

/* 上述函数的逆过程 */

static unsigned int pl08x_encode_width(unsigned int unencoded)

{

unsigned int retval = unencoded >> 1;

if (unencoded == (1 << retval))

return retval;

dev_err(&pd.dmac->dev, "%s - illegal width 0x%08x\n", __func__,

unencoded);

return PL08X_CODING_ERR;

}

时间: 2024-10-28 10:45:05

详解ARM的AMBA设备中的DMA设备(Linux驱动之DMA)的相关文章

.net的session详解 存储模式 存到数据库中 使用范围与大小限制 生命周期

Session又称为会话状态,是Web系统中最常用的状态,用于维护和当前浏览器实例相关的一些信息.举个例子来说,我们可以把已登录用户的用户名放在Session中,这样就能通过判断Session中的某个Key来判断用户是否登录,如果登录的话用户名又是多少. 我们知 道,Session对于每一个客户端(或者说浏览器实例)是"人手一份",用户首次与Web服务器建 立连接的时候,服务器会给用户分发一个 SessionID作为标识.SessionID是一个由24个字符组成的随机字符串.用户每次提

光盘中自带的linux驱动是基于内核2.6的,ubuntu14的内核是3.13,根据光盘中默认的驱动

光盘中自带的linux驱动是基于内核2.6的,ubuntu14的内核是3.13,根据光盘中默认的驱动,得知nw336 model是RTL8188CUS,官方下载地址:http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=3&PNid=21&PFid=48&Level=5&Conn=4&DownTypeID=3&GetDown=false,当前unix最新版为4.0.2,支持2.6.18以后

Android 网络框架之Retrofit2使用详解及从源码中解析原理

就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全的.并使用注解方式的网络请求框架. 2 . 我们为什么要使用Retrofit,它有哪些优势? 首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂: 其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意: 再者,Retr

iOS Core Animation详解(四)AutoLayout中的动画

原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK详解专栏 http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html 前言:AutoLayout定义了View的位置,也就是说,在Auto Layout的工程里,如果不修改约束本身,在视图重新绘制的时候,还会回到最开始的位置.AutoLayout中的动画与视图的位置和大小有关. 先看看效果 实现过程 在Storyboard上拖拽一

【Git使用详解】Egit使用过程中遇到的问题及解决办法

1.   Git错误non-fast-forward后的冲突解决 问题(Non-fast-forward)的出现原因在于:git仓库中已经有一部分代码,所以它不允许你直接把你的代码覆盖上去.于是你有2个选择方式: 1.强推,即利用强覆盖方式用你本地的代码替代git仓库内的内容 git push –f 如果你用的是Egit则可以在推送界面选择"Force Update All Specs"即可,如下图: 2.先把git的东西fetch到你本地然后merge后再push $ git fet

Windows Nano Server安装配置详解05:在虚拟机中部署NanoServer

1.将 NanoServerImageGenerator 文件夹从 Windows Server 2016 ISO 中 \NanoServer 文件夹复制到你硬盘上的文件夹. 拷贝到C盘根目录. 2.以管理员身份启动 Windows PowerShell,将目录更改为 NanoServerImageGenerator 文件夹所在的文件夹,然后导入模块,其方法为 Import-Module .\NanoServerImageGenerator -Verbose 3.通过运行以下命令(将提示你输入新

JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 exec方法的返回值 exec方法返回的其实并不是匹配结果字符串,而是一个对象,简单地修改一下execReg函数,来做一个实验就可以印证这一点: function execReg(reg, str) { var result = reg.exec(str); alert(typeof result

logback的使用和logback.xml详解,在Spring项目中使用log打印日志

logback的使用和logback.xml详解 一.logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: logback-core:其它两个模块的基础模块 logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging logback-access:访问模块与Servlet容器集

Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

文章目录:                   1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Application 有何妙用: 5. MJ0011 关于 Native Application 的文章整理: 6. 互联网上其他关于 Native Application 的文章整理: 7. 小结: 1. 引子: 其实在好久以前就看了 MJ0011 翻译的那个<Native 应用程序详细>系列的文