【问题标题】:Why python c api does not work on apache module written in c?为什么 python c api 不适用于用 c 编写的 apache 模块?
【发布时间】:2021-09-09 20:34:17
【问题描述】:

有一个用c编写的简单模块:

#include <stdio.h>
#include "apr_hash.h"
#include "ap_config.h"
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"

#define PY_SSIZE_T_CLEAN
#include <Python.h>


static int example_handler(request_rec *r) {
    
    if (!r->handler || strcmp(r->handler, "example-handler")){
        return (DECLINED);
    }

    PyObject* py_io = PyImport_ImportModule("io");

    Py_DECREF(py_io);

    ap_set_content_type(r, "text/html");
    ap_rprintf(r, "Filename: %s", r->filename);
    
    return OK;
}

static void register_hooks(apr_pool_t *pool) {
    ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
}

module AP_MODULE_DECLARE_DATA mod_example = {
    STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, register_hooks
};

并在没有apxs 的情况下编译:

# Compile
gcc -D LINUX -D AMD64 \
    $($(apxs -q APR_CONFIG) --cflags --includes) \
    $(python3-config --cflags --includes) \
    -fPIC -DSHARED_MODULE \
    -I$(apxs -q INCLUDEDIR) $(apxs -q CFLAGS) \
    -c mod_example.c;

# Link shared library
gcc -D LINUX -D AMD64 \
    $($(apxs -q APR_CONFIG) --link-ld) \
    $(python3-config --ldflags) \
    -shared -o mod_example.so mod_example.o;

编译成功,没有错误或警告:

$ file build/mod_example.so
build/mod_example.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=10f64f7e7c6d0ec9301e07cb61fe5d0249653704, with debug_info, not stripped

但是当上传到安装了apache2的vm机器并启用模块并重新启动apache时,会说:

apache2:/etc/apache2/apache2.conf 第 146 行的语法错误:/etc/apache2/mods-enabled/mod_example.load 第 1 行的语法错误:无法加载 /usr/lib/apache2/modules/ mod_example.so 进入服务器:/usr/lib/apache2/modules/mod_example.so:未定义符号:PyImport_ImportModule

函数记录在这里:https://docs.python.org/3/c-api/import.html

为什么python.h中声明的python函数编译正确但没有在服务器上声明?,主机(ubuntu-desktop 20.04 lts)和vm机(ubuntu服务器)具有相同的python和apache版本(包括开发头文件) 20.04 升):

$ python3 --version
Python 3.8.5
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 ...
$ find /usr/include -name "apr-*" | grep -o apr.*
apr-1.0

【问题讨论】:

  • 问题是模块和httpd都没有与Python库关联,你可能会使用libpython3.8.so的某个版本。这对适当的编译没有负面影响,并且可能链接器接受它是因为正在构建的对象是共享库,而不是程序。 Apxs 我也将提供有贡献的链接标志。
  • 是的,在/usr/lib/x86_64-linux-gnu/libpython3.8.so。您是说 apache 或模块在启动前需要预加载库吗?
  • 我在/etc/apache2/envvars 中写了完整路径:export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.8.so,并且工作正常:D。我可以在库级别做些什么来避免手动加载 python 库吗?
  • 您可以尝试将$(python3-config --ldflags) 移动到链接命令的末尾或将$(python3-config --libs) 附加到链接命令。可能是任何一种替代方法。链接器对命令行参数的顺序很敏感。

标签: python c linux apache


【解决方案1】:

将我的 cmets 扩展为正确的答案:

问题是模块和 httpd 都没有与 Python 运行时库链接,对您来说,它可能是 libpython3.8.so 的某个版本。这对适当的编译没有负面影响,并且可能链接器接受它是因为正在构建的对象是共享库,而不是程序。 apxs 也可能提供有贡献的链接标志。

如果python3-config 在您的构建环境中行为正确,则它已经发出适当的-L-l 标志,但链接行为对命令行参数的顺序很敏感。特别是,您应该在需要它们的对象之后指定支持库。因此,您可以通过将$(python3-config --ldflags) 移动到链接命令的末尾或将$(python3-config --libs) 附加到该命令来获得在适当位置命名的库。多次指定支持库不是问题。

我不确定 Python 扩展世界中的传统做法是什么,即使有约定。 python3-config 在我看来奇怪地把事情分开了:因为它有单独的选项 --ldflags--libs,我发现第一个请求的输出是第二个请求的输出的超集有点令人惊讶。在我看来,--ldflags 应该给出链接器标志,如果有的话,在命令行上的所有对象名称之前,--libs 应该给出后面的库。但看起来可以使用它们,就好像它们的输出符合我的预期,尽管 --ldflags 的输出似乎不是:

gcc -D LINUX -D AMD64 \
    $($(apxs -q APR_CONFIG) --link-ld) \
    $(python3-config --ldflags) \
    -shared -o mod_example.so mod_example.o \
    $(python3-config --libs)

【讨论】:

  • 谢谢,但我按照与您命令相同的顺序编译了库,但 apache 无法加载 python 库,undefined symbol: Py_Initialize 说,但是当从 apache envvars 添加 LD_PRELOAD 时工作正常。编译命令不起作用。
猜你喜欢
  • 2017-07-25
  • 1970-01-01
  • 2020-08-23
  • 2012-07-23
  • 2020-12-16
  • 1970-01-01
  • 2018-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多