我有一个带有小型套接字通信的 poll() 循环,我想通过 system() 或 exec() 启动另一个程序,并且我需要 system()/exec() 的返回值,但我不知道不想在子进程运行时停止主循环,所以我想我在线程中启动它,但我不确定如何设置 pollfd 以在线程完成时捕获线程,我正在使用 c/c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <poll.h>
#include <iostream>
#include <string>
#include <thread>
#include <future>
#define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket"
int runProgram(const std::string &programName, const std::string &fileName) {
return system((programName + " " + fileName).c_str());
}
int main(int argc, char *argv[]) {
struct sockaddr_un server;
int sock;
char buf[1024];
unlink(SOCKET_NAME);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1){
perror("socket");
exit(EXIT_FAILURE);
}
memset(&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, SOCKET_NAME, sizeof(server.sun_path) - 1);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sock, 3) < -1) {
perror("listen");
exit(EXIT_FAILURE);
}
struct pollfd fds[2];
fds[0].fd = sock;
fds[0].events = POLLIN;
std::future<int> ret = std::async(&runProgram, "cat", "test.txt");
while (true) {
poll(fds, 2, -1);
if(fds[0].revents & POLLIN) {
int new_sd = accept(fds[0].fd, NULL, NULL);
if (new_sd < 0) {
perror("accept");
}
fds[1].fd = new_sd;
}
if (fds[0].revents & POLLIN) {
int rv = recv(fds[1].fd, buf, 1024, 0);
if (rv < 0)
perror("recv");
else if (rv == 0) {
printf("disconnet\n");
close(fds[1].fd);
} else {
printf("%s\n", buf);
send(fds[1].fd, buf, 1024, 0);
}
memset(buf, 0, 1024);
}
}
close(sock);
return(EXIT_SUCCESS);
}
因此,我想在 pollfd (fds[ret.get()]) 中再添加一个,并在线程完成后在 fds[2] 上获取 POLLIN,并且可以获得返回值 (ret.get() ),这里我使用了一个示例命令 cat,但在我的最终代码中,该命令需要更多时间,所以我等不及完成
最佳答案
最简单的解决方案是创建一个匿名管道(或者,因为您说您在 Linux 上,所以是 eventfd )并将数据写入 runProgram
中管道的一端。一旦调用 system
即可函数返回。然后,您可以将管道的读取端包含在您的文件描述符集中 poll
ing。
int process_eventfd = eventfd(0, EFD_CLOEXEC);
if (process_eventfd == -1) exit(1); // change this to handle appropriately
struct pollfd fds[3];
fds[0].fd = sock;
fds[0].events = POLLIN;
fds[1].fd = process_eventfd;
fds[1].events = POLLIN;
// use fds[2] instead of fds[1] for your socket connection, etc.
您可以将 eventfd 编号作为参数添加到 runProgram
。现在看起来应该是这样的:
int runProgram(const std::string &programName, const std::string &fileName, int process_eventfd) {
return system((programName + " " + fileName).c_str());
uint64_t value = 1;
write(process_eventfd, &value, 8);
}
顺便说一句,您当前的程序有一个错误:您总是将 2 作为文件描述符的数量传递给 poll
,甚至在您在数组中设置第二个文件描述符之前。您应该只传递数组中实际存在的有效描述符的数量。
但是,如果您不需要使用system
并可以使用exec
,不需要再创建一个线程;只需执行以下步骤:
- 屏蔽(但不要忽略)SIGCHLD 信号。 (您可能需要设置一个信号处理程序,即使它什么也不做;我不记得这对于 Linux 是否适用)。
- 通过
fork
创建您的外部流程/exec
- 使用
ppoll
而不是poll
,并在要启用的信号中包含 SIGCHLD - 如果
ppoll
调用返回EINTR
错误,使用waitpid
获取 child 身份
子进程将与您的程序并行运行。
关于c++ - poll() 捕获线程返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41850655/