tools:context=”activity name”作用

更新ADT20以后,新建的Activity布局文件的控件属性中多了一句tools:context=”.KeyScanActivity”,网上搜索后才明白:tools:context=”activity name”这一句不会被打包进APK。只是ADT的Layout Editor在你当前的Layout文件里面设置对应的渲染上下文,说明你当前的Layout所在的渲染上下文是activity name对应的那个activity,如果这个activity在manifest文件中设置了Theme,那么ADT的Layout Editor会根据这个Theme来渲染你当前的Layout。就是说如果你设置的MainActivity设置了一个Theme.Light(其他的也可以),那么你在可视化布局管理器里面看到的背景阿控件阿什么的就应该是Theme.Light的样子。仅用于给你看所见即所得的效果而已。

中国大陆开源镜像站汇总

1.企业贡献:

搜狐开源镜像站:http://mirrors.sohu.com/

网易开源镜像站:http://mirrors.163.com/

2.大学教学:

北京理工大学:
http://mirror.bit.edu.cn (IPv4 only)

http://mirror.bit6.edu.cn (IPv6 only)

北京交通大学:
http://mirror.bjtu.edu.cn (IPv4 only)
http://mirror6.bjtu.edu.cn (IPv6 only)
http://debian.bjtu.edu.cn (IPv4+IPv6)

兰州大学:http://mirror.lzu.edu.cn/

厦门大学:http://mirrors.xmu.edu.cn/

上海交通大学:
http://ftp.sjtu.edu.cn/ (IPv4 only)
http://ftp6.sjtu.edu.cn (IPv6 only)

清华大学:

http://mirrors.tuna.tsinghua.edu.cn/ (IPv4+IPv6)
http://mirrors.6.tuna.tsinghua.edu.cn/ (IPv6 only)
http://mirrors.4.tuna.tsinghua.edu.cn/ (IPv4 only)

天津大学:http://mirror.tju.edu.cn/

中国科学技术大学:
http://mirrors.ustc.edu.cn/ (IPv4+IPv6)
http://mirrors4.ustc.edu.cn/
http://mirrors6.ustc.edu.cn/

西南大学:http://linux.swu.edu.cn/swudownload/Distributions/

东北大学:
http://mirror.neu.edu.cn/ (IPv4 only)
http://mirror.neu6.edu.cn/ (IPv6 only)

电子科技大学:http://ubuntu.uestc.edu.cn/

青岛大学:http://mirror.qdu.edu.cn/

Linux 动态库剖析

转自:IBM developerWorks 中国

进程与 API

M. Tim Jones, 顾问工程师, Emulex Corp.

 

简介: 动态链接的共享库是 GNU/Linux® 的一个重要方面。该种库允许可执行文件在运行时动态访问外部函数,从而(通过在需要时才会引入函数的方式)减少它们对内存的总体占用。本文研究了创建和使用静态库的过程,详细描述了开发它们的各种工具,并揭秘了这些库的工作方式。

库用于将相似函数打包在一个单元中。然后这些单元就可为其他开发人员所共享,并因此有了模块化编程这种说法 — 即,从模块中构建程序。Linux 支持两种类型的库,每一种库都有各自的优缺点。静态库包含在编译时静态绑定到一个程序的函数。动态库则不同,它是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。图 1 展示了 Linux 中的库的层次结构。
图 1. Linux 中的库层次结构
Linux 中的库层次结构。

使用共享库的方法有两种:您既可以在运行时动态链接库,也可以动态加载库并在程序控制之下使用它们。本文对这两种方法都做了探讨。

静态库较适宜于较小的应用程序,因为它们只需要最小限度的函数。而对于需要多个库的应用程序来说,则适合使用共享库,因为它们可以减少应用程序对内存(包括运行时中的磁盘占用和内存占用)的占用。这是因为多个应用程序可以同时使用一个共享库;因此,每次只需要在内存上复制一个库。要是静态库的话,每一个运行的程序都要有一份库的副本。

GNU/Linux 提供两种处理共享库的方法(每种方法都源于 Sun Solaris)。您可以动态地将程序和共享库链接并让 Linux 在执行时加载库(如果它已经在内存中了,则无需再加载)。另外一种方法是使用一个称为动态加载的过程,这样程序可以有选择地调用库中的函数。使用动态加载过程,程序可以先加载一个特定的库(已加载则不必),然后调用该库中的某一特定函数(图 2 展示了这两种方法)。这是构建支持插件的应用程序的一个普遍的方法。我稍候将在本文探讨并示范该应用程序编程接口(API)。
图 2. 静态链接与动态链接
图 2. 静态链接与动态链接

用 Linux 进行动态链接

现在,让我们深入探讨一下使用 Linux 中的动态链接的共享库的过程。当用户启动一个应用程序时,它们正在调用一个可执行和链接格式(Executable and Linking Format,ELF)映像。内核首先将 ELF 映像加载到用户空间虚拟内存中。然后内核会注意到一个称为.interp 的 ELF 部分,它指明了将要被使用的动态链接器(/lib/ld-linux.so),如清单 1 所示。这与 UNIX® 中的脚本文件的解释器定义(#!/bin/sh)很相似:只是用在了不同的上下文中。
清单 1. 使用 readelf 来显示程序标题

mtj@camus:~/dl$ readelf -l dl

Elf file type is EXEC (Executable file)
Entry point 0x8048618
There are 7 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
  INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00958 0x00958 R E 0x1000
  LOAD           0x000958 0x08049958 0x08049958 0x00120 0x00128 RW  0x1000
  DYNAMIC        0x00096c 0x0804996c 0x0804996c 0x000d0 0x000d0 RW  0x4
  NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

  ...

mtj@camus:~dl$

 

注意,ld-linux.so 本身就是一个 ELF 共享库,但它是静态编译的并且不具备共享库依赖项。当需要动态链接时,内核会引导动态链接(ELF 解释器),该链接首先会初始化自身,然后加载指定的共享对象(已加载则不必)。接着它会执行必要的再定位,包括目标共享对象所使用的共享对象。LD_LIBRARY_PATH 环境变量定义查找可用共享对象的位置。定义完成后,控制权会被传回到初始程序以开始执行。

再定位是通过一个称为 Global Offset Table(GOT)和 Procedure Linkage Table(PLT)的间接机制来处理的。这些表格提供了 ld-linux.so 在再定位过程中加载的外部函数和数据的地址。这意味着无需改动需要间接机制(即,使用这些表格)的代码:只需要调整这些表格。一旦进行加载,或者只要需要给定的函数,就可以发生再定位(稍候在 用 Linux 进行动态加载 小节中会看到更多的差别)。

再定位完成后,动态链接器就会允许任何加载的共享程序来执行可选的初始化代码。该函数允许库来初始化内部数据并备之待用。这个代码是在上述 ELF 映像的 .init 部分中定义的。在卸载库时,它还可以调用一个终止函数(定义为映像的 .fini 部分)。当初始化函数被调用时,动态链接器会把控制权转让给加载的原始映像。

回页首

用 Linux 进行动态加载

Linux 并不会自动为给定程序加载和链接库,而是与应用程序本身共享该控制权。这个过程就称为动态加载。使用动态加载,应用程序能够先指定要加载的库,然后将该库作为一个可执行文件来使用(即调用其中的函数)。但是正如您在前面所了解到的,用于动态加载的共享库与标准共享库(ELF 共享对象)无异。事实上,ld-linux 动态链接器作为 ELF 加载器和解释器,仍然会参与到这个过程中。

动态加载(Dynamic Loading,DL)API 就是为了动态加载而存在的,它允许共享库对用户空间程序可用。尽管非常小,但是这个 API 提供了所有需要的东西,而且很多困难的工作是在后台完成的。表 1 展示了这个完整的 API。
表 1. Dl API

函数 描述
dlopen 使对象文件可被程序访问
dlsym 获取执行了 dlopen 函数的对象文件中的符号的地址
dlerror 返回上一次出现错误的字符串错误
dlclose 关闭目标文件

该过程首先是调用 dlopen,提供要访问的文件对象和模式。调用 dlopen 的结果是稍候要使用的对象的句柄。mode 参数通知动态链接器何时执行再定位。有两个可能的值。第一个是 RTLD_NOW,它表明动态链接器将会在调用 dlopen 时完成所有必要的再定位。第二个可选的模式是 RTLD_LAZY,它只在需要时执行再定位。这是通过在内部使用动态链接器重定向所有尚未再定位的请求来完成的。这样,动态链接器就能够在请求时知晓何时发生了新的引用,而且再定位可以正常进行。后面的调用无需重复再定位过程。

还可以选择另外两种模式,它们可以按位 OR 到 mode 参数中。RTLD_LOCAL 表明其他任何对象都无法使加载的共享对象的符号用于再定位过程。如果这正是您想要的的话(例如,为了让共享的对象能够调用原始进程映像中的符号),那就使用 RTLD_GLOBAL 吧。

dlopen 函数还会自动解析共享库中的依赖项。这样,如果您打开了一个依赖于其他共享库的对象,它就会自动加载它们。函数返回一个句柄,该句柄用于后续的 API 调用。dlopen 的原型为:

#include <dlfcn.h>

void *dlopen( const char *file, int mode );

 

有了 ELF 对象的句柄,就可以通过调用 dlsym 来识别这个对象内的符号的地址了。该函数采用一个符号名称,如对象内的一个函数的名称。返回值为对象符号的解析地址:

void *dlsym( void *restrict handle, const char *restrict name );

 

如果调用该 API 时发生了错误,可以使用 dlerror 函数返回一个表示此错误的人类可读的字符串。该函数没有参数,它会在发生前面的错误时返回一个字符串,在没有错误发生时返回 NULL:

char *dlerror();

 

最后,如果无需再调用共享对象的话,应用程序可以调用 dlclose 来通知操作系统不再需要句柄和对象引用了。它完全是按引用来计数的,所以同一个共享对象的多个用户相互间不会发生冲突(只要还有一个用户在使用它,它就会待在内存中)。任何通过已关闭的对象的 dlsym 解析的符号都将不再可用。

char *dlclose( void *handle );

 

回页首

动态加载示例

了解了 API 之后,下面让我们来看一看 DL API 的例子。在这个应用程序中,您主要实现了一个 shell,它允许操作员来指定库、函数和参数。换句话说,也就是用户能够指定一个库并调用该库(先前未链接于该应用程序的)内的任意一个函数。首先使用 DL API 来解析该库中的函数,然后使用用户定义的参数(用来发送结果)来调用它。清单 2 展示了完整的应用程序。
清单 2. 使用 DL API 的 Shell

#include <stdio.h>
#include <dlfcn.h>
#include <string.h>

#define MAX_STRING      80

void invoke_method( char *lib, char *method, float argument )
{
  void *dl_handle;
  float (*func)(float);
  char *error;

  /* Open the shared object */
  dl_handle = dlopen( lib, RTLD_LAZY );
  if (!dl_handle) {
    printf( "!!! %sn", dlerror() );
    return;
  }

  /* Resolve the symbol (method) from the object */
  func = dlsym( dl_handle, method );
  error = dlerror();
  if (error != NULL) {
    printf( "!!! %sn", error );
    return;
  }

  /* Call the resolved method and print the result */
  printf("  %fn", (*func)(argument) );

  /* Close the object */
  dlclose( dl_handle );

  return;
}

int main( int argc, char *argv[] )
{
  char line[MAX_STRING+1];
  char lib[MAX_STRING+1];
  char method[MAX_STRING+1];
  float argument;

  while (1) {

    printf("> ");

    line[0]=0;
    fgets( line, MAX_STRING, stdin);

    if (!strncmp(line, "bye", 3)) break;

    sscanf( line, "%s %s %f", lib, method, &argument);

    invoke_method( lib, method, argument );

  }

}

 

要构建这个应用程序,需要通过 GNU Compiler Collection(GCC)使用如下的编译行。选项 -rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 来实现向后跟踪)。-ldl 表明一定要将 dllib 链接于该程序。

gcc -rdynamic -o dl dl.c -ldl

 

再回到 清单 2main 函数仅充当解释器,解析来自输入行的三个参数(库名、函数名和浮点参数)。如果出现 bye 的话,应用程序就会退出。否则的话,这三个参数就会传递给使用 DL API 的 invoke_method 函数。

首先调用 dlopen 来访问目标文件。如果返回 NULL 句柄,表示无法找到对象,过程结束。否则的话,将会得到对象的一个句柄,可以进一步询问对象。然后使用 dlsym API 函数,尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针,或者是得到一个 NULL 并返回一个错误。

在 ELF 对象中解析了符号后,下一步就只需要调用函数。要注意一下这个代码和前面讨论的动态链接的差别。在这个例子中,您强行将目标文件中的符号地址用作函数指针,然后调用它。而在前面的例子是将对象名作为函数,由动态链接器来确保符号指向正确的位置。虽然动态链接器能够为您做所有麻烦的工作,但这个方法会让您构建出极其动态的应用程序,它们可以再运行时被扩展。

调用 ELF 对象中的目标函数后,通过调用 dlclose 来关闭对它的访问。

清单 3 展示了一个如何使用这个测试程序的例子。在这个例子中,首先编译程序而后执行它。接着调用了 math 库(libm.so)中的几个函数。完成演示后,程序现在能够用动态加载来调用共享对象(库)中的任意函数了。这是一个很强大的功能,通过它还能够给程序扩充新的功能。
清单 3. 使用简单的程序来调用库函数

mtj@camus:~/dl$ gcc -rdynamic -o dl dl.c -ldl
mtj@camus:~/dl$ ./dl
> libm.so cosf 0.0
  1.000000
> libm.so sinf 0.0
  0.000000
> libm.so tanf 1.0
  1.557408
> bye
mtj@camus:~/dl$

 

回页首

工具

Linux 提供了很多种查看和解析 ELF 对象(包括共享库)的工具。其中最有用的一个当属 ldd 命令,您可以使用它来发送共享库依赖项。例如,在 dl 应用程序上使用 ldd 命令会显示如下内容:

mtj@camus:~/dl$ ldd dl
        linux-gate.so.1 =>  (0xffffe000)
        libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7fdb000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7eac000)
        /lib/ld-linux.so.2 (0xb7fe7000)
mtj@camus:~/dl$

 

ldd 所告诉您的是:该 ELF 映像依赖于 linux-gate.so(一个特殊的共享对象,它处理系统调用,它在文件系统中无关联文件)、libdl.so(DL API)、GNU C 库(libc.so)以及 Linux 动态加载器(因为它里面有共享库依赖项)。

readelf 命令是一个有很多特性的实用程序,它让您能够解析和读取 ELF 对象。readelf 有一个有趣的用途,就是用来识别对象内可再定位的项。对于我们这个简单的程序来说(清单 2 展示的程序),您可以看到需要再定位的符号为:

mtj@camus:~/dl$ readelf -r dl

Relocation section '.rel.dyn' at offset 0x520 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049a3c  00001806 R_386_GLOB_DAT    00000000   __gmon_start__
08049a78  00001405 R_386_COPY        08049a78   stdin

Relocation section '.rel.plt' at offset 0x530 contains 8 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049a4c  00000207 R_386_JUMP_SLOT   00000000   dlsym
08049a50  00000607 R_386_JUMP_SLOT   00000000   fgets
08049a54  00000b07 R_386_JUMP_SLOT   00000000   dlerror
08049a58  00000c07 R_386_JUMP_SLOT   00000000   __libc_start_main
08049a5c  00000e07 R_386_JUMP_SLOT   00000000   printf
08049a60  00001007 R_386_JUMP_SLOT   00000000   dlclose
08049a64  00001107 R_386_JUMP_SLOT   00000000   sscanf
08049a68  00001907 R_386_JUMP_SLOT   00000000   dlopen
mtj@camus:~/dl$

 

从这个列表中,您可以看到各种各样的需要再定位(到 libc.so)的 C 库调用,包括对 DL API(libdl.so)的调用。函数__libc_start_main 是一个 C 库函数,它优先于程序的 main 函数(一个提供必要初始化的 shell)而被调用。

其他操作对象文件的实用程序包括:objdump,它展示了关于对象文件的信息;nm,它列出来自对象文件(包括调试信息)的符号。还可以将 EFL 程序作为参数,直接调用 Linux 动态链接器,从而手动启动映像:

mtj@camus:~/dl$ /lib/ld-linux.so.2 ./dl
> libm.so expf 0.0
  1.000000
>

 

另外,可以使用 ld-linux.so 的 --list 选项来罗列 ELF 映像的依赖项(ldd 命令也如此)。切记,它仅仅是一个用户空间程序,是由内核在需要时引导的。

回页首

结束语

本文只涉及到了动态链接器功能的皮毛而已。在下面的 参考资料 中,您可以找到对 ELF 映像格式和过程或符号再定位的更详细的介绍。而且和 Linux 其他所有工具一样,你也可以下载动态链接器的源代码(参见 参考资料)来深入研究它的内部。

看看有没有掌握C【转载】

以下是某前辈说的话,很久很久以前就看了,一直在机子里保存着。当是勉励自己。

———————————————————————————————————————————

我所说的掌握,是指已经视C、C++、Java、PHP等类似语法或者pascal这类完全不同语法的编程语言及相关软件为干活的工具,能够根据实际的需求,随心所欲的选择语言、工具、平台,进行独立或者协同开发,或者领导开发。

之所以要特别提到C,并不是因为C更优秀之类的理由。一个真正的程序员,是肯定了解CPU,了解OS的,ASM和C都是理当掌握的基础。学习是个枯燥的过程,对于不同基础,不同条件的人,花费的时间可能会有很大的差别。就我的感觉,没有三年的刻苦学习,就不会有牢固的基础。这一阶段,你不会有什么实质的成果,没有办法把知识拿出来向他人炫耀。如果你认为自己轻易就很“厉害”了,那只是说明了你的浮浅和无知。

之后,在实战中体会到了程序设计的艺术,丝毫不会被语法、库函数、算法、软件结构、设计模式等限制住之后,这时就应该开始考虑广度了。光被动的积累经验是不够的,主动的结合早先的理论知识来指导自己,会得到另一次巨大进步。这个时期,个人的意志,生活和工作环境都是至关重要的。当然最关键的还是主观能动性。经历了多次的成功和失败之后,你要么成为了高手,要么成为所谓老手(就此停止,从此走下坡路)。软件开发是一个不间断的学习过程,保持年轻的心态很重要,不能满足于自己的成功经验。

不要担心记忆力的下降,也不要担心不能面面俱到。那些能把任何语法,任何算法都牢记于心的,一般都是初学者。作为“掌握”了C的人,是不需要记住每一样东西的,你的目标肯定不是成为生物版的man手册。更实质的是驾驭语言工具,分析和解决实际问题的能力。如果你思考时会几乎忘了开发工具(当然,前提是已经选好了最适合的工具),那说明,你真正掌握了C。就像一个学英语的人,某天突然发现自己早已在用英语思考了,这样的境界,可称为掌握了。

图像识别练习(字符验证码、车牌号、身份证号)

光学图像识别(OCR)是非常有用的技术。在验证码识别、车牌号识别、文字识别方面,基于字符的识别技术算是比较容易上手的了(相比图文识别)。

闲来看到有朋友研究验证码识别,一时手痒,野比自己动手来做做验证码识别。当然,肯定只是简单的验证码。

名为验证码,实际上并不限于,还可以识别车牌号、身份证号、门牌号等各种乱七八糟的内容。

识别的流程很明确:

1、预处理图像

2、做y轴的投影

3、分析直方图分区

4、根据分区拆分图像为多个字符(很关键,拆得越好,后续识别率越高)

5、丢弃空白或无效字符

6、自动旋转字符(如果有倾斜),识别字符

如果样本中个图像有粘连,则可能造成分区不准确。这种情况下,需要进行旋转,但是怎样自动旋转,是个难题。

目前已可拆分出字符,下一步准备研究如何识别。(如果单个字符比较规范,可以利用现成的OCR控件)

这里有一些例子。

普通的验证码(毫无难度)

带干扰的验证码

较高强度干扰(目前使用的分区算法不能解决,需要更好的算法,比如动态阈值)

CSDN的验证码(毫无压力)

身份证号码

车牌号

补充个QQ验证码,用单一阈值方法,识别很困难,需要结合字符宽度进行判断

这是单一阈值分区的结果(没有限制宽度),可以看到效果很差。

继续研究如何优化分区算法,如何识别单个文字(可以考虑多重识别+样本训练)。

 

附上太平洋网站验证码。

有些粘连,但是可以通过固定字符宽度解决(宽度基本一致)

参考这张图(获得整个宽度,然后除以字符数得到每个宽度,分别提取)

二值化我用的Otsu算法,参考文献:”A threshold selection method from gray-level histograms”, IEEE Trans. Systems, Man and Cybernetics 9(1), pp. 62–66, 1979

关于验证码,这篇论文很不错,建议参考:”Text-based CAPTCHA Strengths and Weaknesses”, ACM Computer and Communication security 2011 (CSS’2011)

裸设备

在计算机,特别是在类Unix操作系统中,裸设备是一种特殊类型的块设备文件,允许以直接访问硬盘的方式访问一个存储设备,而不经过操作系统的高速缓存和缓冲器(尽管可能仍旧使用硬件高速缓存)。应用程序如数据库管理系统可以直接使用裸设备,启用它们来管理数据的缓存方式,而不将这些任务推给操作系统。

FreeBSD系统中,所有的块设备实际都是裸设备。为了简化缓存的管理以及增强可测量性和性能,FreeBSD 4.0中停止了对非裸设备的支持。

在Linux中,裸设备被反对并列入了移除计划,因为可以通过使用O_DIRECT标志来替代。[1]为了代替访问裸设备文件,应用程序可以(且必须!)通过启用O_DIRECT标志来访问一个文件,且高速缓存将被禁用。由于现在裸设备仍然有一定用户群且仍有一些应用程序没有对O_DIRECT的支持,有关在Linux内核中将裸设备移除仍然是一个有争议的话题。裸设备是一种设备文件系统(major number 162)。Linux为裸设备预留了(i.e. 0),通常情况可以在/dev/rawctl找到它。有人主张,裸设备技术可以将一个裸设备绑定至一个已有的块设备[2]。“已有的块设备”可能是Linux所能支持的所有类型接口的磁盘或CDROM/DVD。(如,IDE/ATA或SCSI)

 

转自维基百科