在父进程中创建一个子进程,并建立一个管道,子进程向管道中写入一个字符串,父进程从管道中读出字符串。
利用CRT相关接口,学习在父子进程间实现管道通信。
1、建立解决方案和项目(略)
2、参照课本3.7.5章节的例1,利用CRT LIB提供的相似性接口,实现相同的功能(使用Linux的同学调用相应的接口)
3、CRT相关api:_pipe, _spawn, _cwait, _read, _write。示例代码可以参照 :https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/pipe?view=msvc-160
1)对示例代码的详细批注
#include
#include
#include
#include
#include
#include
enum PIPES { READ, WRITE };
#define NUMPROBLEM 8
int main(int argc, char* argv[])
{
int i;
int fdpipe[2];
char hstr[20];
int pid, problem, c;
int termstat;
//如果是父进程argc则会等于1,子进程则等于2、3...
if (argc == 1)
{
//定更改流输出流没有缓冲罐
setvbuf(stdout, NULL, _IONBF, 0);
//fdpipe:两个指针,用于保存读和些写描述
if (_pipe(fdpipe, 256, O_BINARY) == -1)
exit(1);
//_itoa_s:将整数转换成字符串的函数,让fdpipe[READ]可以存入字符串,使得管道可以存放传输字符串
_itoa_s(fdpipe[READ], hstr, sizeof(hstr), 10);
//_spawnl:创建子进程,启动子进程再次执行main()
if ((pid = _spawnl(P_NOWAIT, argv[0], argv[0], hstr, NULL)) == -1)
printf("Spawn failed");
for (problem = 1000; problem <= NUMPROBLEM * 1000; problem += 1000)
{
printf("From parent:the number is %d\n", problem);
//向fdpipe[WRITE]写入数据problem
_write(fdpipe[WRITE], (char*)&problem, sizeof(int));
}
printf("dsa");
//等待子进程结束,回收子进程
_cwait(&termstat, pid, WAIT_CHILD);
if (termstat & 0x0)
printf("Child failed\n");
_close(fdpipe[READ]);
_close(fdpipe[WRITE]);
}
else
{
//atoi:将参数转换为整数
fdpipe[READ] = atoi(argv[1]);
for (c = 0; c < NUMPROBLEM; c++)
{
_read(fdpipe[READ], (char*)&problem, sizeof(int));
printf("From child, the number is %d\n", problem);
}
}
}
2)由于持续报这个错误,或者无法运行,网上资料太少,官网的解释也没能太看懂,调试了几天,依然未能够解决,放弃了使用_sprawl(createProcess只是在创建子进程时比它能控制更多东西)。目前只能实现父给子传(官网例子),无法实现子给父传。
3)最后下载了Cygwin,该软件可以在Windows上仿真Linux操作系统,将其整合在了DEV C++中,使用Linux相关操作完成本实验。
#include
#include
#include
int main()
{
//两个文件描述符 ,fd[0]指向管道的读端(将fd文件传送到buf所指内存中),fd[1]指向管道的写端(将buf所指内存存入fd中)
int pipefds[2];
int status;
int pid;
char writemessage[40];
char readmessage[40];
status = pipe(pipefds);
if (status == -1)
{
printf("Failed to create pipe");
return 0;
}
//创建子进程
pid = fork();
if (pid == 0)
{
//子进程
strcpy(writemessage, "I am the child process");
printf("From child process: %s\n", writemessage);
write(pipefds[1], writemessage, sizeof(writemessage));
}
else
{
//父进程
read(pipefds[0], readmessage, sizeof(readmessage));
printf("From parent process: %s\n", readmessage);
close(pipefds[0]);
close(pipefds[1]);
}
return 0;
}
使用Linux来完成程序,使得可读性比较好,便于分析,利于初步学习。对于Windows报错的原因,我目前觉得原因是“fdpipe[0] = atoi(argv[1])”这里的问题,访问了野指针。argv[1]这个参数究竟能干什么,也不是特别清除,打印出来只是一个数字,但不这样写就无法运行,如果直接用属性中高级设置给argv[1]它一个值也不行。但根据示例代码,在read之前要使用这条语句转变fdpipe[0],如果让父进程来read,父进程是没有argv[1]的,可能因为该数据不共享,如果在子进程使用这条语句也无法传给父进程,目前还找不到解决方式。
在父进程中创建两个子进程,并建立一个管道,两个子进程分别向管道中写入一个字符串,父进程从管道中读出字符串。
利用CRT相关接口,学习在父子进程间实现一对多的管道通信。
1、建立解决方案和项目(略)
2、参照课本3.7.5章节的例2,利用CRT LIB提供的相似性接口,实现相同的功能(使用Linux的同学调用相应的接口)
3、CRT相关api:_pipe, _spawn, _cwait, _read, _write。示例代码可以参照 :https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/pipe?view=msvc-160
1)让子进程再fork出一个子进程
#include
#include
#include
int main()
{
int pipefds[2];
int status;
int pid1, pid2;
char writemessage[40];
char readmessage[40];
status = pipe(pipefds);
if (status == -1)
{
printf("Failed to create pipe");
return 0;
}
//创建子进程
pid1 = fork();
//一个fork得放里面,否则子进程执行时会在多创建一个进程
if (pid1 == 0)
{
//子进程2
strcpy(writemessage, "I am No.1");
printf("From child process: %s\n", writemessage);
write(pipefds[1], writemessage, sizeof(writemessage));
pid2 = fork();
if (pid2 == 0)
{
//子进程2
strcpy(writemessage, "I am No.2");
printf("From child process: %s\n", writemessage);
write(pipefds[1], writemessage, sizeof(writemessage));
return 0;
}
return 0;
}
if (pid1 != 0)
{
//父进程
sleep(2);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("From parent process: %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("From parent process: %s\n", readmessage);
close(pipefds[0]);
close(pipefds[1]);
}
return 0;
}
2)在父进程下fork出两个子进程
#include
#include
#include
int main()
{
int pipefds[2];
int status;
int pid;
char writemessage[40];
char readmessage[40];
status = pipe(pipefds);
if (status == -1)
{
printf("Failed to create pipe");
return 0;
}
//创建子进程
for (int i = 0; i <= 1; i++)
{
pid = fork();
if (pid == 0)
break;
}
if (pid == 0)
{
strcpy(writemessage, "I am a child");
printf("From child process: %s\n", writemessage);
write(pipefds[1], writemessage, sizeof(writemessage));
return 0;
}
else
{
sleep(2);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("From parent process: %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("From parent process: %s\n", readmessage);
close(pipefds[0]);
close(pipefds[1]);
}
return 0;
}
通过这个实验发现,fork创建进程还是比较复杂的,简单说明一下这个执行过程。在没有使用“setvbuf(stdout, NULL, _IONBF, 0)”,它的输出是第一张图这样的,起初我以为,每次fork一个子进程,子进程都会重新执行main,父进程则继续执行(根据图片输出其实也不是,如果是这个逻辑则应该输出,第一行则应该是aaaabbbbaaaabbbbFrom等,所以很困惑),但查阅资料后,知道父进程执行过的代码子进程不会执行,所以想到了Windows示例代码中使用的setvbuf,使得输出没有缓冲(具体怎么缓冲也不是特别清楚),然后输出为第二张图,至此结合第三张图也大概理解了整个流程,最关键的就是“父进程执行过的代码子进程不会执行”,为了不让子进程在循环中再在自己的进程中发动fork,只需加个判断,使得子进程退出循环。