我在操作系统类中,我有一个处理分支和管道的项目。在开始我的项目之前,我试图更好地理解叉子和管道,所以我正在研究一个例子。这是有问题的代码(当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:错误:预期表达式 char str1 [MSGSIZE]; ^
pipe3.c:49:13:错误:使用未声明的标识符'str1' fgets(str1,MSGSIZE,stdin); ^
pipe3.c:62:19:错误:使用未声明的标识符'str1' 写(p [1],str1,MSGSIZE); ^
生成3个错误。
我最初在main函数的开头声明了str1,str2和str3(我知道此时它仍然是父进程)并且在编译时没有得到这些错误。我尝试在子进程中声明这些内容的唯一原因是因为我试图了解子进程的范围及其能力,这意味着我只是想尝试并绕过合法和非法的内容。 。我真的不明白为什么编译器会关心子进程是创建和初始化变量的那个进程,然后只在子进程的范围内使用它们来写入管道。所以我想我要问的主要问题是,这实际上是管道的限制吗?我尝试使用char * str1;在子进程中,然后使用fgets存储输入,但我仍然得到相同的编译器错误。另外,我错误地认为子进程的范围只是在0和0之间的代码;或者,子进程是否获取父进程的整个副本,因为父进程中的其余代码被忽略,因为子进程执行代码的唯一时间是pid == 0?我知道这可能是一些基本的东西,但操作系统是第一个让我作为程序员彻底难倒的课程。
以下是我的上下文和完整性的完整源代码:
#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;
}
答案 0 :(得分:1)
编译器不了解进程。它只关心牙箍。您正在获取错误,因为您处于c89模式,其中必须在范围的开头声明变量(请注意case
不打开范围)。
尝试将case 0
的全部内容放入块中,或将-std=c99
选项传递给编译器。
答案 1 :(得分:1)
标准C(在C99之前)需要在一个集团的开头声明变量。在这个意义上,一个函数(包括main
)是一个集团。
switch
可能是一个块,但case
不是,因此您无法在案例中声明新变量。 实际错误位于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;
...
一种不那么惯用但可编译的方法是强制用虚拟创建一个集团,如果:
...
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:
...