C multiprocessing/pipes
C multiprocessing/pipes
我正在学习 fork() 在 C 中的工作原理。
这个想法是产生 3 个子进程,每个子进程向父进程发送一些信息。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd[2];
int pid[3] = {0,0,0};
int status = 0;
for (int i = 0; i < 3; i++)
{
pipe(fd);
pid[i] = fork();
if (pid[i] < 0)
{
return -1;
}
if (pid[i] == 0)
{
close(fd[0]);
char *arr = malloc(sizeof(char));
sprintf(arr, "%i", i);
write(fd[1], arr, 1);
exit(0);
}
}
for(int j=0; j < 3; j++)
{
close(fd[1]);
if (pid[j] > 0)
{
sleep(0);
pid[j] = wait(&status);
char *out = malloc(20 *sizeof(char));
read(fd[0], out, 6);
printf("%s\n", out);
free(out);
printf("I am the parent\n");
}
}
}
预期的输出是:
1
I am the parent
2
I am the parent
3
I am the parent
实际输出是:
2个
我是家长
2个
我是家长
2个
我是家长
为什么会这样?
您正在打电话
pipe(fd);
在第一个 for
循环中多次。考虑在循环之前移动它,因为它 returns 每次调用它时 reading/writing 一对新的文件描述符。
此外,close(fd[1])
只能从父进程调用一次。
几点:
您在第一个 for
循环中调用了 pipe(fd)
三次,并且在创建三个管道时,您只保存了对其中一个管道的引用他们。因此,在你的第二个 for
循环中,你每次都从你创建的第三个管道中读取。您应该有一个数组来存储对所有三个管道的引用。
您应该检查 所有 可能失败的系统调用中的 return。由于上面的第 1 点,如果您这样做,close(fd[1])
将失败三分之二,并且它会提醒您某些事情已经发生。从系统调用中检查 return 不仅是为了防止不太可能发生错误的可能性,也是为了帮助您调试,因为它们失败的最可能原因是您做错了什么,就像这里的情况一样。
这里完全没有必要使用malloc()
——一个常规的char
数组就可以了。此外,malloc(sizeof(char));
为 一个 字符分配 space,当您至少需要两个字符(即一个数字和终止空字符)时。此外,您应该始终检查 malloc()
中的 return,因为它可能会失败。另外,sizeof(char)
根据定义总是 1
,所以它总是多余的。
要获得所需的输出,您应该将 1
添加到 i
,否则 i
将是 0
,然后 1
,然后是 2
,但是您的示例输出说您想要 1
,然后是 2
,然后是 3
。
waitpid()
比 wait()
好,因为您想等待特定进程。同样,当您使用它时,您的 sleep()
调用是多余的。
虽然没有必要在您退出之前 close()
您的管道,但有时这样做是有帮助的,因为如果您已经完成某些操作,它可能会引起您的注意错了。
你的 if (pid[j] > 0)
检查是不必要的,因为如果 fork()
失败或者如果它是 0
,你已经终止了,所以你已经知道它会大于0
当你到达这里时。
如果您不打算使用它,则不需要 status
变量来检索进程的退出状态 - 您只需将 NULL
传递给wait()
或 waitpid()
.
次要点,但是您不需要为第二个 for
循环使用不同的变量名(即 j
),因为 [=23] 的范围=] 在您的第一个 for
循环中仅限于该循环。如果您要使用 i
作为循环计数器的通用名称,您最好尽可能在任何地方使用它。
return EXIT_FAILURE
比 return -1
好,无论如何当您检索退出状态时,该值将转换为 255
。
现实中出问题的可能性很小,但是fork()
returns类型pid_t
,而不是类型int
,所以更好使 pid
成为该类型的数组。
这是一些修改后的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[3][2];
pid_t pid[3];
for ( int i = 0; i < 3; ++i ) {
if ( pipe(fd[i]) == -1 ) {
perror("pipe() failed");
return EXIT_FAILURE;
}
if ( (pid[i] = fork()) == -1 ) {
perror("fork() failed");
return EXIT_FAILURE;
}
else if ( pid[i] == 0 ) {
if ( close(fd[i][0]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
char arr[100];
sprintf(arr, "%d", i + 1);
if ( write(fd[i][1], arr, 1) == -1 ) {
perror("write() failed");
return EXIT_FAILURE;
}
if ( close(fd[i][1]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
else {
if ( close(fd[i][1]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
}
}
for ( int i = 0; i < 3; ++i ) {
if ( waitpid(pid[i], NULL, 0) == -1 ) {
perror("waitpid() failed");
return EXIT_FAILURE;
}
char out[100] = {0};
if ( read(fd[i][0], out, 99) == -1 ) {
perror("read() failed");
return EXIT_FAILURE;
}
printf("%s\nI am the parent\n", out);
if ( close(fd[i][0]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
输出:
paul@horus:~/src/sandbox$ ./pipe
1
I am the parent
2
I am the parent
3
I am the parent
paul@horus:~/src/sandbox$
我正在学习 fork() 在 C 中的工作原理。 这个想法是产生 3 个子进程,每个子进程向父进程发送一些信息。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd[2];
int pid[3] = {0,0,0};
int status = 0;
for (int i = 0; i < 3; i++)
{
pipe(fd);
pid[i] = fork();
if (pid[i] < 0)
{
return -1;
}
if (pid[i] == 0)
{
close(fd[0]);
char *arr = malloc(sizeof(char));
sprintf(arr, "%i", i);
write(fd[1], arr, 1);
exit(0);
}
}
for(int j=0; j < 3; j++)
{
close(fd[1]);
if (pid[j] > 0)
{
sleep(0);
pid[j] = wait(&status);
char *out = malloc(20 *sizeof(char));
read(fd[0], out, 6);
printf("%s\n", out);
free(out);
printf("I am the parent\n");
}
}
}
预期的输出是:
1
I am the parent
2
I am the parent
3
I am the parent
实际输出是: 2个 我是家长 2个 我是家长 2个 我是家长
为什么会这样?
您正在打电话
pipe(fd);
在第一个 for
循环中多次。考虑在循环之前移动它,因为它 returns 每次调用它时 reading/writing 一对新的文件描述符。
此外,close(fd[1])
只能从父进程调用一次。
几点:
您在第一个
for
循环中调用了pipe(fd)
三次,并且在创建三个管道时,您只保存了对其中一个管道的引用他们。因此,在你的第二个for
循环中,你每次都从你创建的第三个管道中读取。您应该有一个数组来存储对所有三个管道的引用。您应该检查 所有 可能失败的系统调用中的 return。由于上面的第 1 点,如果您这样做,
close(fd[1])
将失败三分之二,并且它会提醒您某些事情已经发生。从系统调用中检查 return 不仅是为了防止不太可能发生错误的可能性,也是为了帮助您调试,因为它们失败的最可能原因是您做错了什么,就像这里的情况一样。这里完全没有必要使用
malloc()
——一个常规的char
数组就可以了。此外,malloc(sizeof(char));
为 一个 字符分配 space,当您至少需要两个字符(即一个数字和终止空字符)时。此外,您应该始终检查malloc()
中的 return,因为它可能会失败。另外,sizeof(char)
根据定义总是1
,所以它总是多余的。要获得所需的输出,您应该将
1
添加到i
,否则i
将是0
,然后1
,然后是2
,但是您的示例输出说您想要1
,然后是2
,然后是3
。waitpid()
比wait()
好,因为您想等待特定进程。同样,当您使用它时,您的sleep()
调用是多余的。虽然没有必要在您退出之前
close()
您的管道,但有时这样做是有帮助的,因为如果您已经完成某些操作,它可能会引起您的注意错了。你的
if (pid[j] > 0)
检查是不必要的,因为如果fork()
失败或者如果它是0
,你已经终止了,所以你已经知道它会大于0
当你到达这里时。如果您不打算使用它,则不需要
status
变量来检索进程的退出状态 - 您只需将NULL
传递给wait()
或waitpid()
.次要点,但是您不需要为第二个
for
循环使用不同的变量名(即j
),因为 [=23] 的范围=] 在您的第一个for
循环中仅限于该循环。如果您要使用i
作为循环计数器的通用名称,您最好尽可能在任何地方使用它。return EXIT_FAILURE
比return -1
好,无论如何当您检索退出状态时,该值将转换为255
。现实中出问题的可能性很小,但是
fork()
returns类型pid_t
,而不是类型int
,所以更好使pid
成为该类型的数组。
这是一些修改后的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[3][2];
pid_t pid[3];
for ( int i = 0; i < 3; ++i ) {
if ( pipe(fd[i]) == -1 ) {
perror("pipe() failed");
return EXIT_FAILURE;
}
if ( (pid[i] = fork()) == -1 ) {
perror("fork() failed");
return EXIT_FAILURE;
}
else if ( pid[i] == 0 ) {
if ( close(fd[i][0]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
char arr[100];
sprintf(arr, "%d", i + 1);
if ( write(fd[i][1], arr, 1) == -1 ) {
perror("write() failed");
return EXIT_FAILURE;
}
if ( close(fd[i][1]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
else {
if ( close(fd[i][1]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
}
}
for ( int i = 0; i < 3; ++i ) {
if ( waitpid(pid[i], NULL, 0) == -1 ) {
perror("waitpid() failed");
return EXIT_FAILURE;
}
char out[100] = {0};
if ( read(fd[i][0], out, 99) == -1 ) {
perror("read() failed");
return EXIT_FAILURE;
}
printf("%s\nI am the parent\n", out);
if ( close(fd[i][0]) == -1 ) {
perror("close() failed");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
输出:
paul@horus:~/src/sandbox$ ./pipe
1
I am the parent
2
I am the parent
3
I am the parent
paul@horus:~/src/sandbox$