分类 Coding 下的文章

不要使用 cp 替换正在使用中的Linux 动态库

记得以前如果一个.so被 load 了,应该和程序本身一样,不能覆盖啊,强行覆盖也不会覆盖原来的inode,而是创建新的inode并替换当前目录里面的inode, 原来打开的文件还是正确的。

但是最近发现在 Linux i386 上面 load .so 以后覆盖 .so ,没有任何提示,正在运行的程序退出时侯则通常都会 segfault.  和 sparc 很像了。

难道我记错了吗?

用 strace 看了一下,cp 打开目标文件的时侯,都是在 open 加 O_WRONLY|O_TRUNC|O_LARGEFILE 参数,但是打开正在使用的程序时,就会返回 ETXTBSY, 动态库直接成功。

怀疑 ld-linux.so 在处理 .so 时侯是不是有什么问题,于是 strace 了一下动态库的 load 过程, 发现就是 open O_RDONLY 以后mmap 加 MAP_DENYWRITE 参数,但是 man 了一下 mmap, man page 说这个 MAP_DENYWRITE 参数现在已经被忽略了,原因是会造成 denial of service (我不是很明白这个为啥会造成 DoS),看起来就是这个原因了,mmap 这样并不能阻挡其他程序改写 .so 文件。

但是为啥主程序就可以呢?还是得看 kernel 代码。发现 kernel 内部在加载主程序的时侯,是用的 do_mmap 函数来 mmap 文件的(binfmt_elf.c),参数也有 MAP_DENYWRITE,因为是 kernel 内部函数,所以这个 MAP_DENYWRITE 实际是有效的,在 do_mmap_pg_off 函数(mmap.c)里面也确实可以看到对 MAP_DENYWRITE 参数的处理。

结论就是,Linux ,至少是现在的 Linux 下面无法像 Windows 一样,保护正在使用的 .so 文件,因此开发过程中如果要更新代码,请不要使用 cp 这么粗暴原始的替换方式,还是用 install 来做吧

mod_extract_forwarded for lighttpd

今天花了一点时间,用 lighttpd 的 mod_skeleton.c 划拉了一个 mod_extforward.c 出来,功能类似 Apache 上面的 mod_extract_forwarded.c ,都是用来从 X-Forwarded-For 里面截取出来对方真实 IP 用,特别适合在 squid 后面的机器用,或者有人像我一样,爱好用 mod_proxy 穿来穿去的。不过这个 mod 功能弱很多就是了。

使用方法很简单 , 编译出来这个 module 并复制到 lighttpd 的 lib 目录里面。

在 lighttpd.conf  的 server.modules 里面加上 mod_extforward

配置类似下面这样

extforward.forwarder = ("10.10.10.1" => "trust" , "192.168.1.2" => "trust" )

会自动跳过所有 trusted IP .

另外 由于 lighttpd 本身 plugin 结构的局限,加载 mod_extforward 需要在所有模块之后,否则其他都可以正常工作,但是 access log  打出来的对方地址还是 proxy 的。此外还有局限就是,$HTTP["REMOTEIP"] 的变量不会被 mod_extforward 影响,这个没有办法,对这个的判断先于所有的  plugin 加载。

 下载地址

Update: 修正了和条件语句冲突的情况。

用 swig 给脚本语言做 wrapper

  工程里面有时候需要将某些C或者C++编写的模块封装成脚本语言的扩展,以便在脚本语言里面调用,这个工作通常很枯燥,会写出来很多重复代码,人一向都是会偷懒的动物,因此就诞生了一些工具来帮助人们做这件事情,其中比较有名的就是 swig, 此外还有 boost::python 等,不过我觉得还是 swig 最好,因为他可以同时支持很多种脚本语言。
  SWIG 是一种接口编译器,专门用来连接 C/C++ 程序和脚本语言的,它可以直接处理 C/C++ 头文件里面的声明,生成包装代码。和原有代码编译链接以后,生成多种脚本语言的扩展,可以加载进脚本语言解释器中使用。经过不少年的发展,他已经对C/C++语言支持得非常好了,甚至包括对模版的支持。swig 生成的代码基本都是可以直接用的,不需要再手动填空。他支持的脚本语言更是非常之多,还包括一些编译类型的伪码语言比如 Java。我的Fedora Core 4 系统带的 swig 1.3.24 支持这些脚本语言:ALLEGROL,CHICKEN,C#,GUILE,Java,Modula3,Mzscheme,Ocaml,Perl,PHP,Pike,Python,Ruby,Lisp,Tcl,XML(???这个怎么都有)。很多都没有听说过,不过常用的Perl/PHP/Python都是支持的。
  swig 接受的输入是类似 C 的头文件一样的一种声明文件,支持很多以 % 开头的宏。同时他可以识别C/C++语言的声明,从而对应生成包装代码,常见的数据结构都可以建立对应关系,例如 int <-> int, char * <=> string 等等。下面是一个例子:

  fileio.i
%module fileio
  %{
  /* 这里放的是后面声明所需要的其他 header ,以及一些私有声明之类,会被 swig 原封抄到生成的代码里面去*/
  #include <stdio.h>
  %}
  /* 这后面写需要生成包装代码的方法和对象声明,这个例子是C的,因此只有简单的函数声明,虽然 stdio.h 里面声明了很多函数和数据类型,但是我们想要包装的只有 fopen 和 fclose 两个函数 */

  FILE *fopen(const char *filename, char *mode);
  int fclose(FILE *fp);
这就是一个简单的 swig 输入文件, %module 是给这个扩展起名字,将作为这个扩展在脚本语言中的名字,如果在python里面,使用时候就要import fileio。

  用 swig 处理 fileio.i 会得到一个 .c 或者 .cxx 文件,取决于 swig 是否加了 -c++ 参数。如果用 c++ 方式处理,那么 swig 生成代码会有所不同,比如分配内存会用 new 来作,但总的来说,区别不大。

  [root@kangkang.org ~] swig -python -o fileio_wrap.c fileio.i

  然后
  [root@kangkang.org ~] gcc -shared -o _fileio.so -I/usr/include/python2.4 fileio_wrap.c

  本例里面因为我们没有任何现有代码,封装的是libc里面已经有的 fopen 和 fclose ,因此只要单独编译包装代码 fileio_wrap.c 就可以了,如果是封装已有工程的代码,就需要将已有工程代码一起编译链接成动态库。

  在执行 swig 的时候,swig 除了生成 fileio_wrap.c 包装代码之外,还会负责生成脚本语言端需要的声明代码,本例中它会生成 fileio.py,负责声明和加载 _fileio.so ,在 python 运行环境里面,只要 import fileio ,就可以自动载入 _fileio.so 。然后就可以使用 fileio.fopen 和 fileio.fclose 了。

  这只是一个简单的例子,实际 swig 的功能远比这个例子来得强大,需要进一步了解的,可以读他的说明文档

最近评论

时光机

其他