我正在将一个应用程序从Python 2迁移到Python 3.该应用程序包含一个Python脚本,该脚本协调了几个C应用程序实例.python脚本 for each 应用程序打开一个套接字,然后将相应的文件描述符传递给C程序.这适用于Python 2.7的原始版本,但与Python 3.6或3.9不同.

我发现了一个变化:默认情况下,子进程不会继承stdinstdoutstderr之外的文件描述符(更多信息here)

我所做的是:

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

然后,我判断相应的进程:对于Python 2,要么它存在并且有一个服务器正在等待连接,要么对于Python 3,该进程已死亡.

我试图将文件描述符设置为可继承:

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

然而,这没有帮助,应用程序仍然崩溃.

我还试图显式地将pass_fds = [11]传递到Popen,这也没有帮助.

如果我运行应用程序并让它自己创建套接字,那么它就可以正常工作,包括从Python脚本启动时.因此,在这一点上,我相当确定问题与从Python 2到Python 3的一些更改有关.

是否有其他可能对观察到的行为产生影响的变化?我还能做些什么呢?

推荐答案

这里的问题似乎是,你从来没有在你的插座上拨打listen().如果我将您的代码修改为(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()

其中,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);
    }

}

一切正常.如果我运行Python代码,它会打开套接字并等待子进程退出:

$ python server.py
listening on 51163

如果连接到该端口,我会看到预期的响应:

$ nc localhost 51163
This is a test.

如果我删除对sock.listen的调用或对sock.set_inheritable的调用,那么代码将失败,正如您在问题中所描述的那样.

Python相关问答推荐

max_of_three使用First_select、second_select、

按列分区,按另一列排序

将两只Pandas rame乘以指数

运输问题分支定界法&

如何使Matplotlib标题以图形为中心,而图例框则以图形为中心

如何更新pandas DataFrame上列标题的de值?

我的字符串搜索算法的平均时间复杂度和最坏时间复杂度是多少?

如何在FastAPI中为我上传的json文件提供索引ID?

为什么numpy. vectorize调用vectorized函数的次数比vector中的元素要多?

基于形状而非距离的两个numpy数组相似性

替换现有列名中的字符,而不创建新列

使用Openpyxl从Excel中的折线图更改图表样式

通过追加列表以极向聚合

Polars map_使用多处理对UDF进行批处理

递归函数修饰器

通过对列的其余部分进行采样,在Polars DataFrame中填充_null`?

时间戳上的SOAP头签名无效

Python:在cmd中添加参数时的语法

Pandas 数据框自定义排序功能

使用OpenPYXL切换图表上的行/列