进程间通讯—管道

进程间通讯:

       每个进程都有自己的用户地址空间,一个进程的全局变量在另一个进程中是看不到的,如果两个进程想要交换信息,就需要通过内核,在内核中开辟一块缓存区,把进程1里的数据拷到内核,进程2从内核读走。内核提供的这种机制就是进程间通讯。

      本质:让不同的进程访问同一块系统资源。

    

     1、进程间通讯机制——管道

   

          从本质上来说,管道也是文件,但它与一般的文件不相同,它克服了使用文件通讯的缺点:

   (1):管道文件只占据inode,只是传输数据,不在磁盘上占据空间。

           (2):管道在内存上也是一个固定大小的缓存区,该缓冲区的大小为1页,即4k字节。文件就不会限制大小,除非磁盘已满。但在写管道时,管道写满,当这种情况发生,随后再对管道的write()调用将被阻塞,等待数据被读取,留出空间再write()。

                从管道中读文件是一次性操作,数据一旦被读取,它就从管道中消失,释放空间。

       2、管道操作:

             有名管道:应用于任意两个进程数据的单向传递

              创建:命令方式:mkfifo                              函数方式:mkfifo();

              打开: int  open(const char *pathname,int flag);

              写数据: int  write(int fd,char* buff,int size);

              读数据:int   read(int fd,char *buff,size);

            关闭:int close(int fd);        

            代码实现:

         功能: 我们需要运行两个不同的程序,实现两个进程之间的数据传递。

         1、利用命令 创建管道文件

   


         2、在  a.c  里实现用户循环输入字符串,并写进管道;在  b.c 里实现读取用户输入的字符串

      (1)  a.c 文件向管道里写:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main()
{
    int fd=open("FIFO",O_WRONLY);

	if(fd==-1)
	{
	    exit(0);
	}

	char buff[128]={0};
	printf("please input:\n");
	fgets(buff,128,stdin);

	while(strncmp(buff,"end",3)!=0)
	{

	    write(fd,buff,strlen(buff)-1);
		memset(buff,0,128);
		fgets(buff,128,stdin);
	}
	close(fd);
}

    (2)b.c文件从管道里读:

#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>



int main()
{
    int fd=open("FIFO",O_RDONLY);

	if(fd==-1)
	{
	    exit(0);
	}

	char buff[128]={0};
	while(read(fd,buff,127)>0)
	{
	    printf("%s\n",buff);
	}
	close(fd);
}

测试运行结果:




      3、最重要的点来了:有名管道的特点就是阻塞运行

          阻塞运行函数:函数调用以后并不会立即返回,需要等待某些条件的发生才能返回。

         (1):如果一个进程以只写方式打开一个管道文件,那么open函数会阻塞运行,直到有一个进行以只读方式打开管道文件,open才会返回,进程才会接着进行。

         (2):如果一个进程以只读方式打开一个管道文件,那么open函数会阻塞运行,直到有一个进程以只写方式打开管道文件,open才会返回,进程才会接着进行。

               你运行上面的代码就会知道,两个文件必须同时打开才能运行。

        (3):read 函数也会阻塞运行,直到写端写入数据,或者所有的写端都关闭。

        (4):read读取数据,并将内存上的已读数据清空。

                 如果你让写文件  sleep(10);先执行读,那么能读出来吗?

                 等10s 后,写进去了,才能读出来,那么是谁阻塞?就是read函数啦!

        (5):write函数也会阻塞运行,因为管道是在内存中的一小块,当这一小块存满以后,再进行写的时候只能等待,等一部分数据被读走以后,再接着写。


  4、无名管道

          相对于有名管道,无名管道是使用时产生,不使用时释放,在系统上不会留下一丝痕迹。

          那么来无影去无踪的,连名字也不知道,它该如何找到呢?

           我们通过父子进程实现。为什么要用父子进程实现?

          父进程创建子进程,子进程和父进程指向同一块区域,那么我们就可以利用来通讯。而且父进程结束,子进程也就结束了。那么依赖父子进程的特性就可以同时占用一个管道,进行通讯了。

   

          一个管道由一个进程创建,然后该进程调用fork。父子进程就可以应用该管道。

         管道的创建 :int pipe(int fd[2]);

         打开:  fd[0] 读   fd[1]  写

          读:  read(fd[0],buff,size);

          写: write(fd[1],buff,len);

         关闭:close(fd[0]);

                    close(fd[1]);

         那么如何实现通讯呢?

         

        fork产生后:4个读写,但管道属于半双工通讯,只有一个读,一个写。  父读子写或者父写子读。

         代码实现:fifo 文件

         实现功能为:父进程进行写,从用户端循环输入字符,写进管道里,子进程从管道里进行读,并输出字符串和长度。

#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    
	int fd[2];
	pid_t pid;
	char buff[128];
	char buff1[128];

	if(pipe(fd)<0)
	{
	    printf("pipe error\n");
	}
	pid=fork();
	if(pid<0)
	{
	    printf("fork error\n");
	}
	else if(pid>0)
	{
	    close(fd[0]);
		printf("plesae input\n");
		fgets(buff,128,stdin);
		while(strncmp(buff,"end",3))
		{
			
            write(fd[1],buff,strlen(buff)-1);
			memset(buff,0,128);
		
			fgets(buff,128,stdin);
		}
	}
	else
	{
	    close(fd[1]);

	    while(read(fd[0],buff1,127)>0)
		{
		    printf("%s\n",buff1);
	    	printf("num=%d\n",strlen(buff1));
			printf("please input\n");
		}
	}

}

运行结果:


        

          

猜你喜欢

转载自blog.csdn.net/lyt15829797751/article/details/78294491