我写了一个 C 程序,它应该创建一定数量的子进程,每个子进程都必须更改字符串中的 1 个字母。从键盘读取字符串和子进程数。
我想用管道来做。它应该像这样工作: parent 改变一个字母,然后第一个 child 接受 parent 修改的字符串并再改变一个字母。第二个 child 接受第一个修改的字符串(2 个字母已经更改)并再更改一个,依此类推。我是 C 的新手,不太确定它是如何工作的,尤其是管道。
children 也可以通过管道在他们之间链接,或者他们只能链接到父级并且它必须是这样的:第一个 child 改变一个字母,将字符串返回给父级然后第二个 child 从那里读取,修改字母并返回。 如果是这样,有什么方法可以确保不会发生这种情况:Apples 变成 AppleD,然后是 AppleX,然后是 AppleQ?
例如:
input:
3 Apples
output:
Applex Appldx Apqldx
我的问题是:我没有从 child 那里得到任何输出。不确定我做错了什么。非常感谢您的帮助,提前致谢!
这是我的代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
void error(char* msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
char* modify(char msg[])
{
srand(time(NULL));
int pos1=rand()%((int)strlen(msg));
srand(time(NULL));
int pos2=rand()%26;
srand(time(NULL));
int big=rand()%2;
if(big==1)
{
msg[pos1]=(char)(((int)'A')+pos2);
}
else
{
msg[pos1]=(char)(((int)'a')+pos2);
}
return msg;
}
int main(int argc, char *argv[])
{
if(argc!=3)
{
error("Wrong number of arguments\n");
}
int nrch;
nrch=atoi(argv[1]);
char* msg=argv[2];
printf("Parent: erhalten: %s\n", msg);
int i=0;
msg=modify(argv[2]);
printf("Parent: weiter: %s\n", msg);
pid_t pids[10];
int fd[2];
if(pipe(fd) == -1)
{
error("Can't create the pipe");
}
dup2(fd[1], 1);
close(fd[0]);
fprintf(stdout, msg);
/* Start children. */
for (i = 0; i < nrch; ++i)
{
if ((pids[i] = fork()) < 0)
{
error("Can't fork process");
}
else if (pids[i] == 0)
{
dup2(fd[0], 0);
close(fd[1]);
fgets(msg,255,stdin);
printf("child%d: erhalten: %s\n", (i+1), msg);
modify(msg);
printf("child%d: weiter: %s\n", (i+1), msg);
if (pipe(fd) == -1)
{
error("Can’t create the pipe");
}
fprintf(stdout, msg);
dup2(fd[1], 1);
close(fd[0]);
exit(0);
}
}
/* Wait for children to exit. */
int status;
pid_t pid;
while (nrch > 0)
{
pid = wait(&status);
printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status);
--nrch;
}
}
最佳答案
你看不到 child 的输出的一个原因是你将他们的标准输出挂接到管道的写入端,所以当他们写入标准输出时,它进入管道,而不是屏幕(或你发送的任何地方程序的标准输出为原始)。
如果 child 不打算执行需要标准输入和标准输出到管道的程序,请不要使用 I/O 重定向。只需在管道的正确端写入和读取即可。
如果你有多个 child ,你可能需要每个 child 一个管道,但父进程将需要进行创建。您的代码在 child 中创建了一个管道;那个 pipe 没用,因为只有 child 知道。您可能可以用一个管道完成所有操作,但它变得不确定子项将按哪个顺序运行。如果确定性很重要,请使用多个 pipe()
调用,并且 的调用次数至少是两倍close()
调用。
单管解决方案
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static void error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
exit(1);
}
static void modify(char msg[])
{
int pos1 = rand() % ((int)strlen(msg));
int pos2 = rand() % 26;
int big = rand() % 2;
if (big == 1)
msg[pos1] = (char)(((int)'A') + pos2);
else
msg[pos1] = (char)(((int)'a') + pos2);
}
static int read_pipe(int fd, char *buffer, size_t buflen)
{
int nbytes = read(fd, buffer, buflen);
if (nbytes <= 0)
error("Unexpected EOF or error reading pipe");
assert((size_t)nbytes < buflen);
buffer[nbytes] = '\0';
return nbytes;
}
int main(int argc, char *argv[])
{
if (argc != 3)
error("Usage: %s number 'message'", argv[0]);
srand(time(NULL));
int nrch = atoi(argv[1]);
char *msg = argv[2];
size_t len = strlen(msg);
printf("Parent: erhalten: %s\n", msg);
modify(msg);
printf("Parent: weiter: %s\n", msg);
int fd[2];
if (pipe(fd) == -1)
error("Can't create the pipe");
if (write(fd[1], msg, len) != (ssize_t)len)
error("Failed to write to pipe");
/* Start children. */
for (int i = 0; i < nrch; ++i)
{
int pid;
if ((pid = fork()) < 0)
error("Can't fork process");
else if (pid == 0)
{
char buffer[255];
int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));
printf("child%d: erhalten (%d): %s\n", (i + 1), nbytes, buffer);
modify(buffer);
printf("child%d: weiter (%d): %s\n", (i + 1), nbytes, buffer);
write(fd[1], buffer, nbytes);
exit(0);
}
else
printf("Random: %d\n", rand());
}
/* Wait for children to exit. */
while (nrch > 0)
{
int status;
pid_t pid = wait(&status);
printf("Child with PID %ld exited with status 0x%.4X.\n", (long)pid, status);
--nrch;
}
char buffer[255];
int nbytes = read_pipe(fd[0], buffer, sizeof(buffer));
printf("Parent: weiter (%d): %s\n", nbytes, buffer);
return 0;
}
示例输出
文件p1.c
中的代码:
$ make p1 && ./p1 4 "Absolutely nothing to do with me"
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror p1.c -o p1
Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: AbsolutEly nothing to do with me
Random: 1120753102
child1: erhalten (32): AbsolutEly nothing to do with me
Random: 918317477
child1: weiter (32): AbsolutEly notzing to do with me
child2: erhalten (32): AbsolutEly notzing to do with me
child2: weiter (32): AbsolwtEly notzing to do with me
Random: 196864950
child3: erhalten (32): AbsolwtEly notzing to do with me
child3: weiter (32): AbsolwtEly notzing to ao with me
Random: 1584398270
Child with PID 42928 exited with status 0x0000.
Child with PID 42927 exited with status 0x0000.
Child with PID 42926 exited with status 0x0000.
child4: erhalten (32): AbsolwtEly notzing to ao with me
child4: weiter (32): AbsolwtEly notzing to ao with Ue
Child with PID 42929 exited with status 0x0000.
Parent: weiter (32): AbsolwtEly notzing to ao with Ue
$
请注意循环中对 rand()
的误用。它确保 children 改变消息中的不同字母。否则,他们最终都会更改消息中相同“随机”位置的相同“随机”字母。
如果您愿意,您可以创建一个多管道解决方案。我从单管道解决方案中得到了看似确定性的行为,但不能保证顺序。例如,如果每个 child 使用 nanosleep()
或等价物等待随机延迟:
struct timespec nap = { .tv_sec = 0, .tv_nsec = (rand() % 1000) * 1000000 };
nanosleep(&nap, 0);
然后你在子处理中得到一个任意序列。例如:
Parent: erhalten: Absolutely nothing to do with me
Parent: weiter: Absolutely nothinglto do with me
Random: 2028074573
Random: 988903227
Random: 1120592056
Random: 359101002
child4: erhalten (32): Absolutely nothinglto do with me
child4: weiter (32): vbsolutely nothinglto do with me
Child with PID 43008 exited with status 0x0000.
child3: erhalten (32): vbsolutely nothinglto do with me
child3: weiter (32): vbsolutelyGnothinglto do with me
Child with PID 43007 exited with status 0x0000.
child2: erhalten (32): vbsolutelyGnothinglto do with me
child2: weiter (32): vbsolutelyGnothinglto doawith me
Child with PID 43006 exited with status 0x0000.
child1: erhalten (32): vbsolutelyGnothinglto doawith me
child1: weiter (32): vbsolutelyGnothinglto doawimh me
Child with PID 43005 exited with status 0x0000.
Parent: weiter (32): vbsolutelyGnothinglto doawimh me
关于c - 子进程之间的管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23482355/