[英]File descriptor / socket handling differences when migrating from Python 2 to 3
我正在将一个应用程序从 Python 2 迁移到 3。该应用程序涉及一个 Python 脚本,该脚本编排了一个 C 应用程序的几个实例。 python 脚本为每个应用程序打开一个套接字,然后将相应的文件描述符传递给 C 程序。 这适用于 Python 2.7 中的原始版本,但与 Python 3.6 或 3.9 不同。
我能够找到一个变化:默认情况下,除了stdin
、 stdout
和stderr
之外的文件描述符不被子进程继承(更多信息在这里)
我要做的是:
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.set_inheritable
的调用或对sock.listen
的调用,则代码将按照您在问题中的描述失败。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.