首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从c/cpp库调用python回调时的分段故障11

从c/cpp库调用python回调时的分段故障11
EN

Stack Overflow用户
提问于 2022-04-09 11:26:43
回答 4查看 170关注 0票数 0

我正在从c/cpp库运行一个UDP套接字,并从python传入一个回调。

回调运行良好,直到我尝试修改python应用程序的一个成员变量。当我尝试修改成员变量时,在任意时间之后,我会收到分段错误11。

我很好奇这是否意味着我需要通过在py_BEGIN_ALLOW_THREADS和py_END_ALLOW_THREADS:https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock中包装回调调用来处理GIL。

如果可能的话,我想避免包含,因为这是一个抽象的库,目的是与.net兼容

.cpp回调定义

代码语言:javascript
复制
#ifdef _WIN32
typedef void(__stdcall* UDPReceive)(const char* str);
#else
typedef void (*UDPReceive)(const char* str);
#endif

.cpp线程启动

代码语言:javascript
复制
ReceiveThread = std::async(std::launch::async, &MLFUDP::ReceivePoller, this, callback);

.h ReceiveCallback

代码语言:javascript
复制
UDPReceive ReceiveCallback = nullptr;

.cpp接收触发python回调的线程

代码语言:javascript
复制
void UDP::ReceivePoller(UDPReceive callback)
{
    ReceiveCallback = callback
    ReceiverRunning = true;

    UDPLock *receiveLock = new UDPLock();

#ifdef _WIN32
    int socketLength = sizeof(ClientAddr);
    int flags = 0;
#else
    socklen_t socketLength = sizeof(ClientAddr);
    int flags = MSG_WAITALL;
#endif

    int result;
    char buffer[MAXLINE];
    while(ReceiverRunning)
    {
        try {
            memset(buffer,'\0', MAXLINE);
            result = recvfrom(RecvSocketDescriptor,
                              (char*)buffer,
                              MAXLINE,
                              flags,
                              (struct sockaddr*)&ClientAddr,
                              &socketLength);
#ifdef _WIN32
            if (result == SOCKET_ERROR)
            {
                Log::LogErr("UDP Received error: " + std::to_string(WSAGetLastError()));
            }
#else
            if(result < 0)
            {
                Log::LogErr("UDD Received error: " + std::to_string(result));
            }
#endif
            buffer[result] = '\0';

#ifdef _WIN32
            char* data = _strdup(buffer);
#else
            char* data = strdup(buffer);
#endif
            //handle overlfow
            if(data == nullptr) {continue;}
            receiveLock->Lock();
            //Fire Callback
            ReceiveCallback(data); 
            receiveLock->Unlock();

        }
        catch(...)
        {
            //okay, we want graceful exit when killing socket on close
        }
    }

}

**.py库初始化**

代码语言:javascript
复制
    def __init__(self, udp_recv_port, udp_send_port):
        libname = ""
        if platform == "win32":
            print("On Windows")
            libname = pathlib.Path(__file__).resolve().parent / "SDK_WIN.dll"
        elif platform == "darwin":
            print("on Mac")
            libname = pathlib.Path(__file__).resolve().parent / "SDK.dylib"
            print(libname)
        elif platform == "linux":
            print("on linux")

        UDP_TYPE_BIND = 0

        #Load dynamic library
        self.sdk = CDLL(str(libname))

        callback_type = CFUNCTYPE(None, c_char_p)
        log_callback = callback_type(sdk_log_function)
        self.sdk.InitLogging(2, log_callback)

        recv_callback = callback_type(self.sdk_recv_callback)
        self.sdk.InitUDP(udp_recv_port, udp_send_port, UDP_TYPE_BIND, recv_callback)

.py recv_callback definition如果我运行这个回调--一切正常,已经发送了几百万条消息。

代码语言:javascript
复制
    @staticmethod
    def sdk_recv_callback(message):
        print(message.decode('utf-8'))
        string_data = str(message.decode('utf-8'));
        if len(string_data) < 1:
            print("Returning")
            return

然而,如果我随后将此消息添加到线程安全FIFO queue.Queue()中,则在接收消息时在任意(短)时间内接收分段错误11。

代码语言:javascript
复制
 @staticmethod
    def sdk_recv_callback(message):
        print(message.decode('utf-8'))
        string_data = str(message.decode('utf-8'));
        if len(string_data) < 1:
            print("Returning")
            return

        message_queue.put(string_data)

.py poller函数吞食消息队列

代码语言:javascript
复制
    def process_messages(self):
        while self.is_running:
            string_message = message_queue.get();
            data = json.loads(string_message);
            print(data)

我大部分时间都在学习(在一个筒仓里),所以我认为我很有可能错过了一些基本的东西。我将非常感谢任何关于更好的方法或仅仅是另一组眼睛的建议。谢谢。

目前正在用m1芯片上的cmake在macos上进行编译。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2022-04-23 21:29:51

原来我不需要在c库中使用python.h来处理GIL。由于我使用的是ctype,所以每次调用回调(这里很详细)时,它都会通过旋转一个临时python线程来“神奇地”处理GIL。

这个seg错误是因为我从一个线程运行的process_message函数造成的。seg错误是因为我在类中初始化了ctype库而导致的。相反,我在main上插入SDK,并将一个引用传递给该类。

代码语言:javascript
复制
if __name__ == "__main__":

faulthandler.enable()
libname = ""
if platform == "win32":
    print("On Windows")
    libname = pathlib.Path(__file__).resolve().parent / "SDK_WIN.dll"
elif platform == "darwin":
    print("on Mac")
    libname = pathlib.Path(__file__).resolve().parent / "MLFSDK.dylib"
    print(libname)
elif platform == "linux":
    print("on linux")

sdk = CDLL(str(libname))

app = the_app(sdk,6666,7777)

在这之后,所有的线程都在一起。

票数 1
EN

Stack Overflow用户

发布于 2022-04-19 09:10:58

嗯嗯。这个问题很复杂。我唯一能想到的就是UDP::ReceivePoller函数中的缓冲区溢出。您可以用char *声明char buffer[MAXLINE];。例如,假设MAXLINE = 1024。因此,buffer将是char *到内存库1024。很好。然后,您将memset buffer转到\0,长度为1024字节。很好。那你就做了

代码语言:javascript
复制
result = recvfrom(RecvSocketDescriptor,
                  (char*)buffer,
                  MAXLINE,
                  flags,
                  (struct sockaddr*)&ClientAddr,
                  &socketLength);

理论上可以从套接字读取1024个最大字节。将1024返回到resultbuffer中,设置为读取的1024字节。然后将缓冲区的buffer[result] = '\0';设置索引1024设置为null。但是,索引是从0开始的,而不是从1开始。因此,这将在1024保留为'\0‘之后设置一个字节。而且我想还不错(因为它只少了一个字节)。最终,buffer被放在了它不应该访问的内存中某个地方的旁边,并且它被塞住了。所以我的猜测是:

( a)将recvfrom(...)更新为仅准备好的MAXLINE - 1字节。这样,您只从套接字中准备了1023字节。在缓冲区中保留1024‘字节为null。

( b)将buffer更新为char buffer[MAXLINE + 1];以使1字节额外.(请记住将memset也更新为MAXLINE+1)

票数 0
EN

Stack Overflow用户

发布于 2022-04-23 17:49:52

部分问题可能是,由于您有一个seg错误,很难获得有关错误发生的位置的信息。

您可能希望在程序开始时使用import faulthandler并调用faulthander.enable() (参见https://docs.python.org/3/library/faulthandler.html#faulthandler.enable)。使用faulthandler可以提供一些关于seg故障的最小堆栈跟踪信息,并帮助您发现问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71807649

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档