我在大学里被告知在后台处理流程的最简单方法是使用管道{{1}挂起 子/父进程 的运行功能。
老实说,我已经做了两个星期的家庭作业,我无法解决异步过程处理问题。
我最小化了我的代码,编写了一个处理2个管道的子进程,并使用read()
函数阻止了父进程和子进程。您可以在下面找到我的代码的当前状态:
read()
错误如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char pipeMessage[100];
char* readFromPipe(int pipe)
{
pipeMessage[0] = '\0';
read(pipe, pipeMessage, sizeof(pipeMessage));
fflush(NULL);
return pipeMessage;
}
void writeToPipe(int pipe, char *input)
{
char text[strlen(input) + 1];
strncpy(text, input, (int)strlen(input));
strncat(text, "\0", 1);
printf("TEXT: %s\n", text);
write(pipe, text, sizeof(text));
fflush(NULL);
}
int main(void)
{
int pipes[2][2];
pid_t pid;
if(pipe(pipes[0]) < 0 || pipe(pipes[1]) < 0)
{
printf("[ERROR] create pipes\n");
return -1;
}
printf("[PARENT] Create child1\n");
if((pid = fork()) < 0)
{
printf("[ERROR] Fork error\n");
return -1;
}
if(pid == 0)
{
// Child code
close(pipes[0][0]);
writeToPipe(pipes[0][1], "TEST MESSAGE");
printf("[CHILD1] pipe message: %s\n", readFromPipe(pipes[1][0]));
writeToPipe(pipes[0][1], "-1");
}
else if(pid > 0)
{
// Parent code
close(pipes[0][1]);
close(pipes[1][0]);
char *message;
do
{
message = readFromPipe(pipes[0][0]);
printf("[PARENT] pipe message: %s\n", message);
writeToPipe(pipes[1][1], "-1");
}
while(atoi(message) != -1);
}
return 0;
}
我尝试使用信号实现此进程处理,但在最终的应用程序中,我将需要3个不同的子进程,异步运行会导致信号处理问题。 我也尝试在网上找到一个教程,但每个多处理主题都涵盖了从父到子的简单单一消息解决方案,反之亦然。但是我在父进程中需要一个基于字符的菜单,所以孩子们应该连续等待父信号/消息,而父母也需要等待孩子完成实际任务。 请帮帮我,因为我真的卡住了。如果您有任何正常的流程处理解决方案,请告诉我,因为我知道这段代码很糟糕。唯一的原因是缺乏正确的教程。 提前谢谢。
答案 0 :(得分:1)
好的,我有一个专业用于嵌入式解决方案的模板。使用这个,我创建了一个工作解决方案,它包含客户端循环,但只声明了服务器循环。应该清楚如何制定解决方案。
应该清楚如何将多个读/写器添加到select调用中。注意使用SOCK_DGRAM。除了socketpairs之外,还可以添加管道(这对于从流程到流程的流式传输原始数据是首选)。我希望你能从中学到一些东西。我们应该删除它(有时)。它需要更多的工作才能完成(服务器循环)
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <errno.h>
#include <cstring> // strerror
#include <unistd.h> // pipe
#include <fcntl.h>
#include <stdint.h>
#include <algorithm>
#define CallErrExit(fun, arg, retval) { \
if ((fun arg)<0) { \
FailErr(#fun); \
return retval; \
} \
}
#define FailErr(msg) { \
(void)fprintf(stderr, "%s, %s(%d)\n", \
msg, strerror(errno), errno); \
(void)fflush(stderr);}
const int parentsocket = 0;
const int childsocket = 1;
int disk_main(int comm_server[]);
int server_main(int comm_disk[]);
int main(int argc, char* argv[]) {
int status; // Status parameter used waitpid
// Communication sockets
int comm_server_disk[2];
// Process Id's
pid_t proc_server;
pid_t proc_disk;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
return EXIT_FAILURE;
}
proc_server = getpid();
int socket;
// SOCK_DGRAM are connectionless as opposed to SOCK_STREAM or SOCK_SEQPACKET
CallErrExit(socketpair, (AF_UNIX, SOCK_DGRAM, 0,
comm_server_disk), EXIT_FAILURE);
CallErrExit(proc_disk = fork,(),EXIT_FAILURE);
if (proc_disk == 0) {
// Disk process
proc_disk = getpid();
// TODO: Try the alternative, use the names '/proc/<process id>/fd/
printf("disk_main started with sockets:\n"
"\tserver->disk: /proc/%d/fd/%d\n"
"\tdaup->disk: /proc/%d/fd/%d\n",
proc_disk, socket+1,
proc_disk, socket+3);
/*
* Closing comm_server_disk[1] before function entry
*/
close(comm_server_disk[childsocket]); // Write to server
return disk_main(comm_server_disk);
}
else {
// Server process, closes comm_server_disk[0]
server_main(comm_server_disk);
// Never reached
// calling process sets SIGCHLD to SIG_IGN, ECHILD is set
CallErrExit(waitpid, (proc_disk,&status,0), EXIT_FAILURE);
return EXIT_SUCCESS;
}
}
typedef struct internal_cmd {
char cmd[32];
} internal_cmd_t;
enum e_cmd {
eExit = 0x01,
};
// TODO: Some map between enums and cmd[32]
int disk_main(int comm_server[]) {
char buf[1024];
int select_width;
fd_set rfds, wfds, efds;
int next_select_width;
fd_set next_rfds, next_wfds, next_efds;
int n_fds;
struct timeval timeout = { 10, 0 };
FD_ZERO(&next_rfds);
FD_ZERO(&next_wfds);
FD_ZERO(&next_efds);
FD_SET(comm_server[0], &next_rfds);
// Default: is blocking, but be sure
fcntl(comm_server[0],
F_SETFL, fcntl(comm_server[0], F_GETFL, 0) & ~O_NONBLOCK);
// Add other file descriptors
int ofd = comm_server[0];
next_select_width = std::max(comm_server[0],ofd) + 1;
do {
// Update read/write state
select_width = next_select_width;
rfds = next_rfds;
wfds = next_wfds;
efds = next_efds;
// Wait for interrupt
n_fds = select(select_width,&rfds, &wfds, &efds, &timeout);
if (n_fds < 0) {
fprintf(stderr,"ERROR\n");
exit(1);
}
if (FD_ISSET(comm_server[0], &wfds))
printf("Disk process can write to server\n");
if (FD_ISSET(comm_server[0], &rfds)) {
printf("Disk process received message from server\n");
int rv = recv(comm_server[0], buf, sizeof(buf), MSG_DONTWAIT);
if (rv < 0) {
printf("Disk process - %s(%d)\n", strerror(errno), errno);
exit (1);
}
printf("Disk process received %d bytes:\n", rv);
// Interpret command
if (rv == sizeof(internal_cmd_t)) {
// Here interpret command
e_cmd cmd;
if (cmd == eExit) {
printf("Exiting\n");
close(comm_server[0]);
return EXIT_SUCCESS;
}
}
}
FD_ZERO(&next_rfds); FD_ZERO(&next_wfds); FD_ZERO(&next_efds);
FD_SET(comm_server[0], &next_rfds);
fcntl(comm_server[0], F_SETFL, fcntl(comm_server[0], F_GETFL, 0) & ~O_NONBLOCK);
int ofd = comm_server[0];
next_select_width = std::max(comm_server[0],ofd) + 1;
}
while (true);
close(comm_server[0]);
return EXIT_SUCCESS;
}
答案 1 :(得分:1)
你写逻辑是有问题的,你的管道布局过于复杂。下面是您的代码,我可以根据自己的需要进行简单的调整。包含评论以帮助您。我发现在处理链式管道(这对于所有意图都是这样的)时最简单的是布置一个由显示链接的宏索引的描述符数组:
// some hand macros for access the correct pipes
#define P_READ 0
#define C_WRITE 1
#define C_READ 2
#define P_WRITE 3
#define N_PIPES 4
以上将在最终的源列表中。这些标记应该是不言而喻的,但是如果它们不是,P_XXX
注意到父进程使用的管道,C_XXX
注意到管道子进程使用。看到代码时请记住这一点:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
// some hand macros for access the correct pipes
#define P_READ 0
#define C_WRITE 1
#define C_READ 2
#define P_WRITE 3
#define N_PIPES 4
// reads a buffer up-to len size.
ssize_t readFromPipe(int pipe, char *buff, size_t len)
{
buff[0] = 0;
ssize_t res = read(pipe, buff, len);
assert(res >= 0 && "Failed to read from pipe");
return res;
}
ssize_t writeToPipe(int pipe, const char *input)
{
size_t len = strlen(input)+1;
ssize_t res = write(pipe, input, len);
assert(res == len && "Failed to write to pipe");
return res;
}
int main(void)
{
int pipes[N_PIPES];
char msg[128] = {0};
pid_t pid;
if(pipe(pipes) < 0 || pipe(pipes+2) < 0)
{
printf("[ERROR] create pipes\n");
return EXIT_FAILURE;
}
if((pid = fork()) < 0)
{
printf("[ERROR] Fork error\n");
return EXIT_FAILURE;
}
// parent code
if(pid > 0)
{
// Parent code. close down the child pipes; don't need them
printf("parent(%d) create: child(%d)\n", getpid(), pid);
close(pipes[C_WRITE]);
close(pipes[C_READ]);
do
{
if (readFromPipe(pipes[P_READ], msg, sizeof(msg)) > 0)
{
printf("parent(%d) read : %s\n", getpid(), msg);
writeToPipe(pipes[P_WRITE], "-1");
}
else break;
}
while(atoi(msg) != -1);
// close remaining pipes. no longer needed
close(pipes[P_READ]);
close(pipes[P_WRITE]);
}
else if(pid == 0)
{
// Child code. don't need parent write or child-read lines
close(pipes[P_READ]);
close(pipes[P_WRITE]);
// write message
writeToPipe(pipes[C_WRITE],"test message");
// read test message
if (readFromPipe(pipes[C_READ], msg, sizeof(msg)) > 0)
printf("child(%d) read : %s\n", getpid(), msg);
// write another message
writeToPipe(pipes[C_WRITE], "-1");
// close remaining pipes. no longer needed
close(pipes[C_READ]);
close(pipes[C_WRITE]);
}
return EXIT_SUCCESS;
}
管道描述符数组无法承受,其中最大的变化是简化的writeToPipe
逻辑,它只是将终结符C字符串写入管道,包括终止空字符。
ssize_t writeToPipe(int pipe, const char *input)
{
size_t len = strlen(input)+1;
ssize_t res = write(pipe, input, len);
assert(res == len && "Failed to write to pipe");
return res;
}
调用者检查结果以确保它写入了所有请求的数据,并且嵌入的assert()宏将在失败时使调试器跳闸。读取功能存在类似的逻辑。
输出(因进程ID而异)
parent(2067) create: child(2068)
parent(2067) read : test message
child(2068) read : -1
parent(2067) read : -1
我希望这会有所帮助。在处理管道时,尤其是在不太遥远的未来(see spoiler here)可能遇到的stdio重定向时,为代码提供有意义的助记符会有很大帮助,例如我使用的宏以上用于索引管道数组。
祝你好运。