简体   繁体   English

从 Python 2 迁移到 3 时的文件描述符/套接字处理差异

[英]File descriptor / socket handling differences when migrating from Python 2 to 3

I'm migrating an application from Python 2 to 3. The app involves in a Python script that orchestrates a few instances of a C app.我正在将一个应用程序从 Python 2 迁移到 3。该应用程序涉及一个 Python 脚本,该脚本编排了一个 C 应用程序的几个实例。 The python script opens a socket for each app and then passes the corresponding file descriptor to the C program. python 脚本为每个应用程序打开一个套接字,然后将相应的文件描述符传递给 C 程序。 This works with the original version in Python 2.7 but breaks with Python 3.6 or 3.9.这适用于 Python 2.7 中的原始版本,但与 Python 3.6 或 3.9 不同。

I was able to find one change: the file descriptors apart from stdin , stdout and stderr are not inherited by the child processes by default (more info here )我能够找到一个变化:默认情况下,除了stdinstdoutstderr之外的文件描述符不被子进程继承(更多信息在这里

What I do is the following:我要做的是:

import socket                                
import os                                    
import subprocess                            

sock = socket.socket()                       
sock.bind(('10.80.100.32',0))                
sock                                         
# Out[6]: <socket.socket fd=11, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('10.80.100.32', 36737)>

env  = os.environ.copy()                     
env["LD_LIBRARY_PATH"] = env["LD_LIBRARY_PATH"] + ":%s" % os.getcwd()                             
p = subprocess.Popen(["./app", "--sockfd", "11"], close_fds = False, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)                                               
p.pid                                       
# Out[10]: 393727

Then I check the corresponding process: either it exists and there is a server waiting for a connection in case of Python 2 or the process is dead in case of Python 3.然后我检查相应的进程:在 Python 2 的情况下它存在并且有一个服务器在等待连接,或者在 Python 3 的情况下该进程已死。

I tried to set the file descriptor to be inheritable:我试图将文件描述符设置为可继承:

os.get_inheritable(11)                      
# Out[15]: False
os.set_inheritable(11, True)

However that did not help, the app still crashes.然而这并没有帮助,应用程序仍然崩溃。

I also tried to explicitly pass pass_fds = [11] to Popen , that also did not help.我还尝试将pass_fds = [11]显式传递给Popen ,但这也没有帮助。

If I run the app and let it create the socket on its own then it works fine including when it is started from the Python script.如果我运行应用程序并让它自己创建套接字,那么它可以正常工作,包括从 Python 脚本启动时。 So at this point I'm fairly certain that the problem has to do with some changes from Python 2 to Python 3.所以在这一点上,我相当肯定这个问题与从 Python 2 到 Python 3 的一些变化有关。

Are there any other changes that could be having an impact on the observed behavior?是否有任何其他变化可能对观察到的行为产生影响? What else could I try to make it work?我还能尝试什么让它发挥作用?

The problem here appears to be that you're never calling listen() on your socket.这里的问题似乎是你从来没有在你的套接字上调用listen() If I modify your code to both (a) set the inheritable flag, and (b) call listen , so that it looks like this:如果我将您的代码修改为(a)设置inheritable标志和(b)调用listen ,它看起来像这样:

import socket
import os
import subprocess

sock = socket.socket()
sock.set_inheritable(True)
sock.bind(("0.0.0.0", 0))
sock.listen(5)
print("listening on", sock.getsockname()[1])

env = os.environ.copy()
p = subprocess.Popen(
    ["./socklisten", "{}".format(sock.fileno())],
    close_fds=False,
    env=env,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
p.wait()

Where socklisten is the following simple program that prints out a string on a given file descriptor:其中socklisten是以下简单程序,它在给定文件描述符上打印出一个字符串:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>

int main(int argc, char **argv) {
    int sock;

    if (argc <= 1) {
        fprintf(stderr, "missing socket\n");
        exit(1);
    }

    sock = atoi(argv[1]);
    if (sock == 0) {
        fprintf(stderr, "invalid socket number\n");
        exit(1);
    }

    while (1) {
        int client;
        char msg[100];
        struct sockaddr_in clientaddr;
        socklen_t clientlen = sizeof(struct sockaddr_in);
        if (-1 == (client = accept(sock, (struct sockaddr *)&clientaddr, &clientlen))) {
            perror("accept");
            exit(1);
        }

        sprintf(msg, "This is a test.\r\n");
        write(client, msg, strlen(msg)+1);
        close(client);
    }

}

It all works as expected.这一切都按预期工作。 If I run the Python code, it opens the socket and waits for the child process to exit:如果我运行 Python 代码,它会打开套接字并等待子进程退出:

$ python server.py
listening on 51163

If I connect to that port, I see the expected response:如果我连接到该端口,我会看到预期的响应:

$ nc localhost 51163
This is a test.

If I remove either the call to sock.listen or the call to sock.set_inheritable , the code fails as you describe in your question.如果我删除对sock.set_inheritable的调用或对sock.listen的调用,则代码将按照您在问题中的描述失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM