管道、子进程和子进程的范围
Pipes, child processes, and the scope of a child process
我在操作系统 class 中,我有一个处理分叉和管道的项目。在开始我的项目之前,我试图更好地理解叉子和管道,所以我正在研究一个例子。这是有问题的代码(当 pid=fork() 等于 0 并且是子进程时的情况 0):
switch(pid = fork())
{
case -1:
perror("fork call");
exit(2);
break;
case 0:
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write str1,str2,str3...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
break;
default:
close(p[1]);
for(j = 0; j < 6; j++)
{
read(p[0], inbuf, MSGSIZE);
printf("%s\n", inbuf);
}
wait(NULL);
}//switch
当我在命令行上编译时出现这些错误:
$ gcc -o p3 pipe3.c
pipe3.c:45:7: 错误:预期的表达式
字符 str1[MSGSIZE];
^
pipe3.c:49:13:错误:使用了未声明的标识符 'str1'
fgets(str1, MSGSIZE, 标准输入);
^
pipe3.c:62:19: 错误:使用了未声明的标识符 'str1'
写(p[1], str1, MSGSIZE);
^
产生了 3 个错误。
我最初在 main 函数的开头声明了 str1、str2 和 str3(我知道此时这仍然是父进程)并且在编译时没有出现这些错误。我试图在子进程中声明这些的唯一原因是因为我试图了解子进程的范围以及它的能力,这意味着我只是想尝试并思考什么是合法的和非法的.我真的不明白为什么编译器关心子进程是创建和初始化变量然后只在子进程范围内使用它们写入管道的进程。所以我想我想问的主要问题是,这实际上是管道的限制吗?我尝试使用 char *str1;在子进程中,然后也使用 fgets 存储输入,但我仍然遇到相同的编译器错误。另外,我是否认为子进程的范围只是 case 0: 和 break; 之间的代码是错误的?或者子进程是否获得父程序的完整副本,因为父进程中的其余代码被忽略,因为子进程执行代码的唯一时间是 pid == 0?我知道这可能是一些基础知识,但操作系统是第一个 class 让我作为程序员彻底难过的东西。
为了上下文和完整性,这是我的整个源代码:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGSIZE 16
int main(int argc, char *argv[])
{
char inbuf[MSGSIZE];
char *msg1 = "Hello world #1";
char *msg2 = "Hello world #2";
char *msg3 = "Hello world #3";
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
int p[2], j;
pid_t pid;
if(pipe(p) == -1)
{
perror("pipe call");
exit(1);
}
switch(pid = fork())
{
case -1:
perror("fork call");
exit(2);
break;
case 0:
//char *str1;
//char *str2;
//char *str3;
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write the input strings...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
break;
default:
close(p[1]);
for(j = 0; j < 6; j++)
{
read(p[0], inbuf, MSGSIZE);
printf("%s\n", inbuf);
}
wait(NULL);
}//switch
return 0;
}
编译器不了解进程。它只关心大括号。您收到错误是因为您处于 c89 模式,其中必须在范围的开头声明变量(请注意 case
而不是 打开范围)。
尝试将case 0
的全部内容做成一个块,或者将-std=c99
选项传递给编译器。
标准 C(C99 之前)需要在块的开头声明变量。一个函数(包括 main
)在这个意义上是一个 bloc。
switch
可以是一个块,但 case
不是,所以你不能在 case 中声明新的变量。 real 错误在 char str1[MSGSIZE];
处,编译器不接受,因此 str1
未声明导致其他 2 个错误的原因。
如果你只想在子进程中声明变量(这对你作为程序员有意义,但对编译器没有意义:fork
只是它的一个函数)你可以分两次完成方式 :
一个干净的方法是将它放在一个函数中并调用该函数:
void doCopy(int *p) {
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write the input strings...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
}
int main(int argc, char *argv[])
...
case 0:
doCopy(p, MSGSIZE);
break;
...
一种不那么惯用但可编译的方法是强制创建带有 dummy 的 bloc if :
...
switch(pid = fork())
{
case -1:
perror("fork call");
exit(2);
break;
case 0:
if(1) {
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write the input strings...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
}
break;
default:
...
我在操作系统 class 中,我有一个处理分叉和管道的项目。在开始我的项目之前,我试图更好地理解叉子和管道,所以我正在研究一个例子。这是有问题的代码(当 pid=fork() 等于 0 并且是子进程时的情况 0):
switch(pid = fork())
{
case -1:
perror("fork call");
exit(2);
break;
case 0:
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write str1,str2,str3...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
break;
default:
close(p[1]);
for(j = 0; j < 6; j++)
{
read(p[0], inbuf, MSGSIZE);
printf("%s\n", inbuf);
}
wait(NULL);
}//switch
当我在命令行上编译时出现这些错误:
$ gcc -o p3 pipe3.c
pipe3.c:45:7: 错误:预期的表达式 字符 str1[MSGSIZE]; ^
pipe3.c:49:13:错误:使用了未声明的标识符 'str1' fgets(str1, MSGSIZE, 标准输入); ^
pipe3.c:62:19: 错误:使用了未声明的标识符 'str1' 写(p[1], str1, MSGSIZE); ^
产生了 3 个错误。
我最初在 main 函数的开头声明了 str1、str2 和 str3(我知道此时这仍然是父进程)并且在编译时没有出现这些错误。我试图在子进程中声明这些的唯一原因是因为我试图了解子进程的范围以及它的能力,这意味着我只是想尝试并思考什么是合法的和非法的.我真的不明白为什么编译器关心子进程是创建和初始化变量然后只在子进程范围内使用它们写入管道的进程。所以我想我想问的主要问题是,这实际上是管道的限制吗?我尝试使用 char *str1;在子进程中,然后也使用 fgets 存储输入,但我仍然遇到相同的编译器错误。另外,我是否认为子进程的范围只是 case 0: 和 break; 之间的代码是错误的?或者子进程是否获得父程序的完整副本,因为父进程中的其余代码被忽略,因为子进程执行代码的唯一时间是 pid == 0?我知道这可能是一些基础知识,但操作系统是第一个 class 让我作为程序员彻底难过的东西。
为了上下文和完整性,这是我的整个源代码:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGSIZE 16
int main(int argc, char *argv[])
{
char inbuf[MSGSIZE];
char *msg1 = "Hello world #1";
char *msg2 = "Hello world #2";
char *msg3 = "Hello world #3";
char str1[MSGSIZE];
char str2[MSGSIZE];
char str3[MSGSIZE];
int p[2], j;
pid_t pid;
if(pipe(p) == -1)
{
perror("pipe call");
exit(1);
}
switch(pid = fork())
{
case -1:
perror("fork call");
exit(2);
break;
case 0:
//char *str1;
//char *str2;
//char *str3;
printf("Enter a string! ");
fgets(str1, MSGSIZE, stdin);
printf("Enter another string! ");
fgets(str2, MSGSIZE, stdin);
printf("Enter the last string! ");
fgets(str3, MSGSIZE, stdin);
close(p[0]);
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
printf("About to write the input strings...\n");
write(p[1], str1, MSGSIZE);
write(p[1], str2, MSGSIZE);
write(p[1], str3, MSGSIZE);
break;
default:
close(p[1]);
for(j = 0; j < 6; j++)
{
read(p[0], inbuf, MSGSIZE);
printf("%s\n", inbuf);
}
wait(NULL);
}//switch
return 0;
}
编译器不了解进程。它只关心大括号。您收到错误是因为您处于 c89 模式,其中必须在范围的开头声明变量(请注意 case
而不是 打开范围)。
尝试将case 0
的全部内容做成一个块,或者将-std=c99
选项传递给编译器。
标准 C(C99 之前)需要在块的开头声明变量。一个函数(包括 main
)在这个意义上是一个 bloc。
switch
可以是一个块,但 case
不是,所以你不能在 case 中声明新的变量。 real 错误在 char str1[MSGSIZE];
处,编译器不接受,因此 str1
未声明导致其他 2 个错误的原因。
如果你只想在子进程中声明变量(这对你作为程序员有意义,但对编译器没有意义:fork
只是它的一个函数)你可以分两次完成方式 :
一个干净的方法是将它放在一个函数中并调用该函数:
void doCopy(int *p) { char str1[MSGSIZE]; char str2[MSGSIZE]; char str3[MSGSIZE]; printf("Enter a string! "); fgets(str1, MSGSIZE, stdin); printf("Enter another string! "); fgets(str2, MSGSIZE, stdin); printf("Enter the last string! "); fgets(str3, MSGSIZE, stdin); close(p[0]); write(p[1], msg1, MSGSIZE); write(p[1], msg2, MSGSIZE); write(p[1], msg3, MSGSIZE); printf("About to write the input strings...\n"); write(p[1], str1, MSGSIZE); write(p[1], str2, MSGSIZE); write(p[1], str3, MSGSIZE); } int main(int argc, char *argv[]) ... case 0: doCopy(p, MSGSIZE); break; ...
一种不那么惯用但可编译的方法是强制创建带有 dummy 的 bloc if :
... switch(pid = fork()) { case -1: perror("fork call"); exit(2); break; case 0: if(1) { char str1[MSGSIZE]; char str2[MSGSIZE]; char str3[MSGSIZE]; printf("Enter a string! "); fgets(str1, MSGSIZE, stdin); printf("Enter another string! "); fgets(str2, MSGSIZE, stdin); printf("Enter the last string! "); fgets(str3, MSGSIZE, stdin); close(p[0]); write(p[1], msg1, MSGSIZE); write(p[1], msg2, MSGSIZE); write(p[1], msg3, MSGSIZE); printf("About to write the input strings...\n"); write(p[1], str1, MSGSIZE); write(p[1], str2, MSGSIZE); write(p[1], str3, MSGSIZE); } break; default: ...