• Windows进程间利用管道通信


    实验一

    一、实验内容或题目:

    在父进程中创建一个子进程,并建立一个管道,子进程向管道中写入一个字符串,父进程从管道中读出字符串。

    二、实验目的与要求:

    利用CRT相关接口,学习在父子进程间实现管道通信。

    三、实验步骤(以windows和Visual Studio为例):

    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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

    总结

    使用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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    在这里插入图片描述
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    在这里插入图片描述

    五、总结:

    在这里插入图片描述
    在这里插入图片描述
    通过这个实验发现,fork创建进程还是比较复杂的,简单说明一下这个执行过程。在没有使用“setvbuf(stdout, NULL, _IONBF, 0)”,它的输出是第一张图这样的,起初我以为,每次fork一个子进程,子进程都会重新执行main,父进程则继续执行(根据图片输出其实也不是,如果是这个逻辑则应该输出,第一行则应该是aaaabbbbaaaabbbbFrom等,所以很困惑),但查阅资料后,知道父进程执行过的代码子进程不会执行,所以想到了Windows示例代码中使用的setvbuf,使得输出没有缓冲(具体怎么缓冲也不是特别清楚),然后输出为第二张图,至此结合第三张图也大概理解了整个流程,最关键的就是“父进程执行过的代码子进程不会执行”,为了不让子进程在循环中再在自己的进程中发动fork,只需加个判断,使得子进程退出循环。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    06:串口通信一
    Java 反射设置List属性
    2023年全国职业院校技能大赛 高等职业教育组(信息安全管理与评估)正式赛题
    ESANet语义分割与realsense D455的结合
    JZ65 [剑指 Offer 62] 圆圈中最后剩下的数字
    轻松玩转树莓派Pico之三、Windows+Ubuntu虚拟机模式下VSCode C语言开发环境搭建
    pytorch: hook机制,取网络层的输入输出和梯度
    链式二叉树【递归】
    【网络安全】面试中常见问题--sql注入篇
    按头安利!好看又实用的电机 SolidWorks模型素材看这里
  • 原文地址:https://blog.csdn.net/Fishermen_sail/article/details/127946520