理解 glibc malloc

本篇文章主要完成了对《Understanding glibc malloc》的翻译工作。限于本人翻译水平与专业技术水平(纯粹为了了解内存分配而翻),本文章必定会有很多不足之处,请大家见谅,也欢迎大家的指正!
联系邮箱:974985526@qq.com


堆内存是一个很有意思的领域,这样的问题:

堆内存是如何从内核中分配的?
内存管理效率怎样?
它是由内核、库函数还是应用本身管理的?
堆内存可以开发吗?

我也困惑了很久,但是直到最近我才有时间去了解它。下面就让我来谈谈我的研究成果。开源社区提供了很多现成的内存分配器(memory allocators ):

  • dlmalloc – General purpose allocator
  • ptmalloc2 – glibc
  • jemalloc – FreeBSD and Firefox
  • tcmalloc – Google
  • libumem – Solaris

每一种分配器都宣称自己快(fast)、可拓展(scalable )、效率高(memory efficient)!但是并非所有的分配器都适用于我们的应用。内存吞吐量大(memory hungry)的应用程序的性能很大程度上取决于内存分配器的性能。

在这篇文章中,我将只谈论“glibc malloc”内存分配器。为了更好地理解“glibc malloc”,我会联系最近的源代码

历史ptmalloc2基于dlmalloc开发,并添加了对多线程的支持,于2006年公布。在公布之后,ptmalloc2被整合到glibc源代码中,此后ptmalloc2所有的修改都直接提交到glibc的malloc部分去了。因此,ptmalloc2的源码和glibc的 malloc源码有很多不一致的地方。

系统调用

之前的文章中提到过malloc的内部调用为brkmmap

译者注:其中有一张关于虚拟地址空间分布的图片,我觉得很有助于本篇文章的理解,因此把它放在此处。

这里写图片描述

线程处理

Linux的早期版本使用dlmalloc为默认内存分配器,但是因为ptmalloc2提供了多线程支持,所以Linux后来采用ptmalloc2为默认内存分配器。多线程支持可以提升内存分配器的性能,进而间接提升应用的性能。在dlmalloc中,当有两个线程同时调用malloc时,只有一个线程能够访问临界区(critical section):因为空闲列表数据结构(freelist data structure)被所有可用线程共享。正如此,dlmalloc内存分配器在多线程应用中颇为耗时,性能不好。而在ptmalloc2中,当有两个线程同时调用malloc时,内存均会得到立即分配:因为每个线程都维护着一个独立的堆段。因此空闲列表数据结构维护的堆也是独立的。这种为每个线程独立地维护堆和空闲列表数据结构的行为就称为per thread arena

Example:

/* Per thread arena example. */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void* threadFunc(void* arg) {
        printf("Before malloc in thread 1\n");
        getchar();
        char* addr = (char*) malloc(1000);
        printf("After malloc and before free in thread 1\n");
        getchar();
        free(addr);
        printf("After free in thread 1\n");
        getchar();
}

int main() {
        pthread_t t1;
        void* s;
        int ret;
        char* addr;

        printf("Welcome to per thread arena example::%d\n",getpid());
        printf("Before malloc in main thread\n");
        getchar();
        addr = (char*) malloc(1000);
        printf("After malloc and before free in main thread\n");
        getchar();
        free(addr);
        printf("After free in main thread\n");
        getchar();
        ret = pthread_create(&t1, NULL, threadFunc, NULL);
        if(ret)
        {
                printf("Thread creation error\n");
                return -1;
        }
        ret = pthread_join(t1, &s);
        if(ret)
        {
                printf("Thread join error\n");
                return -1;
        }
        return 0;
}

输出分析:

在主线程 malloc 之前:

在如下的输出里我们可以看到,这里还 没有 堆段 也没有 线程栈,因为 thread1 还没有创建!

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

在主线程 malloc 之后:

在如下的输出中我们可以看到堆段产生在数据段(0804b000-0806c000)之上,这表明堆内存是通过更高级别的中断产生(也即brk中断)。此外,请注意,尽管用户只申请了1000字节的内存,但是实际产生了132KB的堆内存。这个连续的堆内存区域被称为arena。因为这个arena是被主线程建立的,因此称为main arena。接下来的申请会继续分配这个arena的132KB中剩余的部分,直到用尽。当用尽时,它可以通过更高级别的中断扩容,在扩容之后,top chunk的大小也随之调整以圈进这块额外的空间。相应地,arena也可以在top chunk空间过大时缩小。

注意:top chunk是一个arena中最顶层的chunk。有关top chunk的更多信息详见下述“top chunk”部分。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
...
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

在主线程 free 之后:

在如下的输出结果中我们可以看出当分配的内存区域free掉时,其后的内存并不会立即释放给操作系统。分配的内存区域(1000B)仅仅是移交给了’glibc malloc’,把这段free掉的区域添加在了main arenas bin中(在glibc malloc中,空闲列表数据结构被称为bin)。随后当用户请求内存时,’glibc malloc’就不再从内核中申请新的堆区了,而是尝试在bin中找到空闲区块,除非实在找不到。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
...
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7e05000-b7e07000 rw-p 00000000 00:00 0 
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

在thread1 malloc 之前:

在如下的输出中我们可以看见此时并没有thread1的堆段,但是其每个线程栈都已建立。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

在thread1 malloc 之后:

在如下的输出结果中我们可以看出thread1的堆段建立在内存映射段区域(b7500000-b7521000,132KB),这也表明了堆内存是使用mmap系统调用产生的,而非同主线程一样使用sbrk系统调用。同样地,尽管用户只请求了1000B,1MB的堆内存还是被映射到了进程地址空间。在这1MB,只有132KB被设置了读写权限并成为该线程的堆内存。这段连续内存(132KB)被称为 thread arena。

注意:当用户请求超过128KB大小并且此时arena中没有足够的空间来满足用户的请求时,内存将通过使用mmap系统调用(不再是sbrk)来分配而不论请求是发自main arena还是 thread arena。

ploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
After malloc and before free in thread 1
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7500000-b7521000 rw-p 00000000 00:00 0 
b7521000-b7600000 ---p 00000000 00:00 0 
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

在thread1 free 之后:

在如下的输出结果中我们可以看出free掉的分配的内存区域这一过程并不会把堆内存归还给操作系统,而是仅仅是移交给了’glibc malloc’,然后添加在了thread arenas bin中。

sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ ./mthread 
Welcome to per thread arena example::6501
Before malloc in main thread
After malloc and before free in main thread
After free in main thread
Before malloc in thread 1
After malloc and before free in thread 1
After free in thread 1
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$ cat /proc/6501/maps
08048000-08049000 r-xp 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
08049000-0804a000 r--p 00000000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804a000-0804b000 rw-p 00001000 08:01 539625     /home/sploitfun/ptmalloc.ppt/mthread/mthread
0804b000-0806c000 rw-p 00000000 00:00 0          [heap]
b7500000-b7521000 rw-p 00000000 00:00 0 
b7521000-b7600000 ---p 00000000 00:00 0 
b7604000-b7605000 ---p 00000000 00:00 0 
b7605000-b7e07000 rw-p 00000000 00:00 0          [stack:6594]
...
sploitfun@sploitfun-VirtualBox:~/ptmalloc.ppt/mthread$

Arena

Arena的数量:

在如下的例子中,我们可以看见主线程包含main arena而thread 1包含它自有的thread arena。所以若不计线程的数量,在线程和arena之间是否存在一对一映射关系?当然不存在,部分极端的应用甚至运行比处理器核心的数量还多的线程,在这种情况下,每个线程都拥有一个arena开销过高且意义不大。因此,应用的Arena数量限制是基于系统的核心数的。

For 32 bit systems:
     Number of arena = 2 * number of cores + 1.
For 64 bit systems:
     Number of arena = 8 * number of cores + 1.

Multiple Arena:

举例而言:让我们来看一个运行在单核计算机上的32位操作系统上的多线程应用(4线程=主线程+3个用户线程)的例子。这里线程数量(4)大于核心数的二倍加一,因此在这种条件下,’glibc malloc’认定multiple arenas会被所有可用线程共享。那么它是如何共享的呢?

  • 当主线程第一次调用malloc时,已经建立的 main arena会被没有任何竞争地使用。
  • 当thread 1 和 thread 2第一次调用malloc时,一块新的arena就被创建且会被没有任何竞争地使用。此时线程和arena之间有着一对一的映射关系。
  • 当thread3第一次调用malloc时,arena的数量限制被计算出来。这里超过了arena的数量限制,因此尝试复用已经存在的arena(Main arena 或 Arena 1 或 Arena 2)。
  • 复用:
    • 一旦遍历出可用arena,就开始自旋申请该arena的锁。
    • 如果上锁成功(比如说main arena上锁成功),就将该arena返回用户。
    • 如果查无可用arena,thread 3的malloc操作阻塞,直到有可用的arena为止。
  • 当thread 3第二次调用malloc时,malloc会尝试使用上一次使用的arena(main arena)。当main arena可用时就用,否则thread 3就一直阻塞直至main arena被free掉。因此现在main arena实际上是被main thread和thread 3所共享。

Multiple Heaps:

在’glibc malloc’中主要发现了3种数据结构:
heap_info -Heap Header- 一个thread arena可以有多个堆。每个堆都有自己的堆头。为什么需要多个堆?每个thread arena都只包含一个堆,但是当这个堆段空间耗尽时,新的堆(非连续区域)就会被mmap到这个aerna。
malloc_state -Arena header- 一个thread arena可以有多个堆,但是所有这些堆只存在arena header。arena header包括的信息有:bins、top chunk、last remainder chunk…
malloc_chunk -Chunk header- 一个堆根据用户请求被分为若干chunk。每个这样的chunk都有自己的chunk header。

注意

  • Main arena 没有多个堆,因此没有heap_info结构。当main arena空间耗尽时,就拓展sbrk’d堆段(拓展后是连续内存区域),直至碰到内存mapping区域为止。
  • 不像thread arena,main arena的arena header不是sbrk’d堆段的一部分,而是一个全局变量,因此它可以在libc.so的 数据段中被找到。

main arena和thread arena的图示如下(单堆段):
main arena和thread arena
thread arena的图示如下(多堆段):
thread arena

Chunk

堆段中能找到的chunk类型如下:

  • Allocated chunk
  • Free chunk
  • Top chunk
  • Last Remainder chunk

Allocated chunk:

Allocated chunk

prev_size:若上一个chunk可用,则此结构成员赋值为上一个chunk的大小;否则若上一个chunk被分配,此此结构成员赋值为上一个chunk的用户数据大小。
size:此结构成员赋值为已分配chunk大小,其最后三位包含标志(flag)信息。

  • PREV_INUSE (P) – 置“1”表示上一个chunk被分配;
  • IS_MMAPPED (M) – 置“1”表示这一个chunk被nmap’d;
  • NON_MAIN_ARENA (N) – 置“1”表示这一个chunk属于一个thread arena。

注意

  • malloc_chunk 中的其余结构成员,如fd、 bk不用于已分配的chunk,因此它们被拿来存储用户数据;
  • 用户请求的大小被转换为可用大小(内部显示大小),因为需要一些额外的空间存储malloc_chunk,此外还需要考虑对齐的因素。

Free chunk:

Free chunk

prev_size:两个空闲chunk不能毗连,而应合并成一个。因此前一个chunk和这一个空闲chunk都会被分配,此时prev_size中保存上一个chunk的用户数据。
size:该结构成员保存本空闲chunk的大小。
fd:Forward pointer - 指向同一bin中的下一个chunk(而非物理内存中下一块)。
bk:Backward pointer – 指向同一bin中的上一个chunk(而非物理内存中上一块)。

Bins

Bins就是空闲列表数据结构。它们用以保存空闲chunk。基于chunk的大小,有下列几种可用bins:

  • Fast bin
  • Unsorted bin
  • Small bin
  • Large bin

保存这些bin的数据结构为:

fastbinsY:这个数组用以保存fast bins。
bins:这个数组用以保存unsorted、small以及large bins,共计可容纳126个:

  • Bin 1 – unsorted bin
  • Bin 2 to Bin 63 – small bin
  • Bin 64 to Bin 126 – large bin

Fast Bin:

大小为16~80字节的chunk被称为fast chunk。在所有的bin中,fast bin在内存分配以及释放上有更快的速度。

  • 数量 - 10
    • 每个fast bin都记录着一条free chunk的单链表(称为binlist ,采用单链表是出于fast bin中链表中部的chunk不会被摘除的特点),增删chunk都发生在链表的前端。 -LIFO
  • 大小 - 8字节递增
    • fast bins 记录着大小以8字节递增的bin链表。也即,fast bin(index 0)记录着大小为16字节的chunk的binlist、fast bin(index 1)记录着大小为24字节的chunk的binlist 依次类推……
    • 指定fast bin中所有chunk大小均相同。
  • 在malloc初始化过程中,最大的fast bin的大小被设置为64而非80字节。因为默认情况下只有大小16~64的chunk被分类为fast chunk。
  • 不能合并 - 两个毗连的空闲chunk并不会被合并成一个空闲chunk。不合并可能会导致碎片化问题,但是却可以大大加速释放的过程!
  • malloc(fast chunk)
    • 初始情况下fast bin最大内存容量以及指针域均未初始化,因此即使用户请求fast chunk,服务的也将是small bin code而非fast bin code。
    • 当它非空后,fast bin索引将被计算以检索对应binlist。
    • binlist中被检索的第一个个chunk将被摘除并返回给用户。
  • free(fast chunk)
    • fast bin索引被计算以索引相应binlist。
    • free掉的chunk将被添加在索引到的binlist的前端。

fast bin snapshot

Unsorted Bin:

当small chunk和large chunk被free掉时,它们并非被添加到各自的bin中,而是被添加在unsorted bin中。这种途径给予 ‘glibc malloc’ 重新使用最近free掉的chunk的第二次机会,这样寻找合适bin的时间开销就被抹掉了,因此内存的分配和释放会更快一点。

  • 数量 -1
    • unsorted bin包括一个用于保存空闲chunk的双向循环链表(又名binlist)。
  • chunk大小 - 无尺寸限制,任何大小chunk都可以添加进这里。

    unsorted,small and large bin snapshot

    Small Bin:

    大小小于512字节的chunk被称为small chunk,而保存small chunks的bin被称为small bin。small bin比large bin在内存分配回收的速度上更快。

  • 数量 -62

    • 每个small bin都包括一个空闲区块的双向循环链表(也称binlist)。free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除。-FIFO
  • 大小 -8字节递增
    • samll bins 记录着大小以8字节递增的bin链表。也即,第一个small bin(Bin 2)记录着大小为16字节的chunk的binlist、small bin(Bin 3)记录着大小为24字节的chunk的binlist 依次类推……
    • 指定samll bin中所有chunk大小均相同,因此无需排序。
  • 合并 - 两个毗连的空闲chunk会被合并成一个空闲chunk。合并消除了碎片化的影响但是减慢了free的速度。
  • malloc(small chunk)
    • 初始情况下,small bin都会是NULL,因此尽管用户请求small chunk,提供服务的将是 unsorted bin code 而不是 small bin code。
    • 同样地,在第一次调用malloc期间,在malloc_state找到的samll bin和large bin数据结构被初始化,bin都会指向它们本身以表示binlist为空。
    • 此后当samll bin非空后,相应的bin会摘除binlist中最后一个chunk并返回给用户。
  • free(small chunk)
    • 在free一个chunk的时候,检查其前或其后的chunk是否空闲,若是则合并,也即把它们从所属的链表中摘除并合并成一个新的chunk,新chunk会添加在unsorted bin链表的前端。

Large Bin:

大小大于等于512字节的chunk被称为large chunk,而保存large chunks的bin被称为large bin。large bin比samll bin在内存分配回收的速度上更快。

  • 数量 -63
    • 每个large bin都包括一个空闲区块的双向循环链表(也称binlist)。free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除。-FIFO(?)
    • 超过63个bin之后
      • 前32个bin记录着大小以64字节递增的bin链表,也即第一个large bin(Bin 65)记录着大小为512字节~568字节的chunk的binlist、第二个large bin(Bin 66)记录着大小为576字节到632字节的chunk的binlist,依次类推……
      • 后16个bin记录着大小以512字节递增的bin链表。
      • 后8个bin记录着大小以4096字节递增的bin链表。
      • 后4个bin记录着大小以32768字节递增的bin链表。
      • 后2个bin记录着大小以262144字节递增的bin链表。
      • 最后1个bin记录着大小为剩余大小的chunk。
    • 不像samll bin,large bin中所有chunk大小不一定相同,因此各chunk需要递减保存。最大的chunk保存在最前的位置,而最小的chunk保存在最后的位置。
  • 合并 - 两个毗连的空闲chunk会被合并成一个空闲chunk。
  • malloc(large chunk)
    • 初始情况下,large bin都会是NULL,因此尽管用户请求large chunk,提供服务的将是 next largetst bin code 而不是 large bin code。
    • 同样地,在第一次调用malloc期间,在malloc_state找到的samll bin和large bin数据结构被初始化,bin都会指向它们本身以表示binlist为空。
    • 此后当samll bin非空后,当最大chunk大小(在相应binlist中的)大于用户所请求的大小时,binlist就从顶部遍历到底部以找到一个大小最接近用户需求的chunk。一旦找到,相应chunk就会分成两块
      • User chunk(用户请求大小)-返回给用户。
      • Remainder chunk(剩余大小) -添加到unsorted bin。
    • 当最大chunk大小(在相应binlist中的)小于用户所请求的大小时,尝试在Next largest bin中查到到所需的chunk以响应用户请求。Next largest bin code会扫描binmaps以找到下一个最大非空bin,如果这样的bin找到了,就从其中的binlist中检索到合适的chunk并返回给用户;反之就使用top chunk以响应用户请求。
  • free(large chunk) -类似于small trunk。

Top Chunk:

一个arena中最顶部的chunk被称为Top Chunk。它不属于任何bin。在所有bin中都没有合适空闲内存区块的时候,才会使用top chunk来响应用户请求。当top chunk大小比用户所请求大小还大的时候,top chunk会分为两个部分:

  • User chunk(用户请求大小)
  • Remainder chunk(剩余大小)

其中Remainder chunk成为新的top chunk。当top chunk大小小于用户所请求的大小时,top chunk就通过sbrk(main arena)或mmap(thread arena)系统调用来扩容。

Last Remainder Chunk:

最后一次small request中因分割而得到的Remainder。Last Remainder Chunk有助于改进引用的局部性,也即连续的对small chunk的malloc请求可能最终导致各chunk被分配得彼此贴近。

但是除了在一个 arena 里可用的的诸chunk,哪些chunk有资格成为Last Remainder Chunk呢?

当一个用户请求small chunk而无法从small bins和unsorted bins得到服务时,binmaps就会扫描下一个最大非空bin(译者注:Top Chunk 不属于任何bin)。正如前文所提及的,如果这样的bin找到了,其中最适chunk就会分割为两部分:返回给用户的user chunk、添加到unsorted bin中的remainder chunk。此外,这一remainder chunk还会成为最新的 last remainder chunk

那么参考局部性是如何实现的呢?

现在当用户随后的请求是请求一块small chunk并且last remainder chunk是unsorted bin中唯一的chunk,last remainder chunk就分割成两部分:返回给用户的user chunk、添加到unsorted bin中的remainder chunk。此外,这一remainder chunk还会成为最新的 last remainder chunk。因此随后的内存分配最终导致各chunk被分配得彼此贴近。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
一、别人记得流水账 MAC上装东西总是遇到些问题,这里做个流水帐。希望能对别人有点帮助哈   1、先下载需要的软件包        OCR工具:   Tesseract-OCR3.0.1  source code      tesseract-ocr-3.01.eng.tar.gz  破验证码用英文就够了。        图像处理工具:   Leptonica   1.68        png识别工具:   libpng         jpeg 识别工具 : libjpeg         tif 识
自动化测试框架UFT BASED 自动化测试,一个现在被炒的火热的词;各大公司都在嚷嚷着要上自动化测试的项目,都在招聘各种自动化测试人员。。。 本材料对于编程基础较低初学者,在编写和学习过程中可以接触到很多旁枝侧节的知识,这些都是做好自动化所有需要的知识;对于有一定技术储备。 本框架不能帮你成为高大上的编程大牛,或者自动化测试的行家。但是,它可以引领你迈入自动化测试的领域。 师傅领进门,修行靠个人;一切的一切都还是要靠你自己去多多实践,不是有一句名言么?实践是检验真理的唯一标准! 一、自动化测试基础 手工

springmvc SSM后台框架源码 - 2016-07-25 17:07:55

获取【下载地址】    QQ: 313596790   【免费支持更新】 三大数据库 mysql  oracle  sqlsever    更专业、更强悍、适合不同用户群体 【 新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统 】 A 集成代码生成器 [ 正反双向 (单表、主表、明细表、树形表, 开发利器 ) +快速构建表单 ; QQ:313596790 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面、建表sql脚本,处理类,service等完整模块 B 集成阿里巴

ip报头分片 - 2016-07-25 17:07:06

分片介绍:          IP分片是网络上传输IP报文的一种技术手段。IP协议在传输数据包时,将数据报文分为若干分片进行传输,并在目标系统中进行重组         在IP协议中的分片算法主要解决不同物理网络最大传输单元(MTU) 的不同造成的传输问题。分组在传输过程中不断地分片和重组会带来很大的工作量还会增加一些不安全的因素。 为什么需要分片:          每一种物理网络都会规定链路层数据帧的最大长度,称为链路层MTU(Maximum Transmission Unit).IP协议在传输数据包
写在前面的 策略模式 概念 具体实例 比较和理解 总结 写在前面的 上一篇文章我们说到,如果我们需要在原本已经整理好的代码中添加新的内容(包括算法或者功能性模块),我们可以应用简单工厂来实现,比如添加算法(活学活用嘛)、在已有的功能模块中再添加新的功能等。但是,我们在上一篇文章中提到过 开放-封闭原则 ,试想一下,如果我们写出来的代码在应用过程中一直需要不断的调整和增加新的功能,那么每次维护都要打开这个工厂,添加相应的功能或算法之后再重新部署,这样无意间会增加我们的工作量,所以简单工厂在某种意义上就不适用
课程概要:          讲解 Struts2 中数据封装的三种方式以及具体实现原理   一、 Struts2 数据封装机制之属性驱动   我们先来看一下传统的 servlet 是如何处理从页面传递过来的数据的。 首先我们在表单发送了对应的数据到 servlet 中去 form action="%=path%/loginservlet"method="post" 账号:inputtype="text"name="username"/br 密码:inputtype="password"name="pas
Spring MVC框架是有一个MVC框架,通过实现Model-View-Controller模式来很好地将数据、业务与展现进行分离。从这样一个角度来说,Spring MVC和Struts、Struts2非常类似。Spring MVC的设计是围绕DispatcherServlet展开的,DispatcherServlet负责将请求派发到特定的handler。通过可配置的handler mappings、view resolution、locale以及theme resolution来处理请求并且转到对应的

通过WSDL生成客户端代码 - 2016-07-25 14:07:03

目录 1.WSDL2Java:Building stubs,skeletons,and data types from WSDL . 1 1.1示例 ... 1 1.2测试 ... 4 1.2.1异常:没有定义com.pan.model.User的序列化的实现 ... 5 1.3WSDL与生成的客户端代码结构分析 ... 5 1.3.1Types . 6 1.3.2Holders . 12 1.3.3PortTypes . 13 1.3.4Bindings . 13 1.3.5 Services . 14
                                               Ofbiz:数据库移植mysql并创建自己的mysql            Ofbiz原生数据库是derby,而作为开发使用,其就不能满足我们需求,ofbiz支持多种数据库,我们就可以将数据移植到mysql.            第一步:找到framework\entity\config\entityengine.xml这个文件,找到之后进行下面相关操作.          1、添加或者修改datasourc

linux的一些简单命令 - 2016-07-25 14:07:59

这里只是列出实际中使用频率较高的,可以通过 man 命令或者 命令 –help 来查看更为详细的内容 文件有关的 1:【ls命令】 ls [option] …[file]… -a all 列出所有的文件 包括隐藏文件 [eg ls -a /home] -l 列出详细的文件信息 可以简写为ll filename [eg: ls -l /home or ll /home ] -h –human-readable 将文件的大小通过字节的方式列出来 -R 递归显示出该目录所有的文件 -d 只显示本文件下面 可以通