进程间的通信

290 阅读4分钟

一、进程的介绍

  1. 进程不会占用磁盘空间,但是系统会给进程分配cpu和内存。
  2. 每个进程对应一个虚拟地址空间。
  3. 进程是程序在执行过程中分配和管理资源的基本单位。
  4. 进程是独立的,对一个进程操作不会影响其他进程。

二、进程间通信的方式

  1. 管道
  2. 信号
  3. 内存映射
  4. 套接字
  5. 共享内存
  6. 消息队列

三、管道

  1. 匿名管道
    • 介绍
    •     匿名管道只能够进行没有血缘关系的进程间通信,必须要在创建子进程之前,创建匿名管道。

    • 函数
    •        #include <unistd.h>
             #include <fcntl.h>  
             int pipe(int pipefd[2]);
                  参数:
                      pipefd[2]传出参数,fd[0]代表管道的读端,fd[1]代表管道的写端
                  返回值:
                      0:成功
                      -1:调用失败
                      
      
      
    • 测试代码
    •  #include <sys/wait.h>                                                 
       #include <stdio.h>
       #include <unistd.h>
       #include <string.h>
       
       int main()
       {
          //创建匿名管道
           int pipe_fd[2];
           if(pipe(pipe_fd)==-1)
           {
               //如果创建失败,打印错误信息并退出进程
               perror("pipe");
               _exit(0);
            }
           //创建子进程
           pid_t pid=fork();
           char buf[1024];
          if(pid>0)
           {
              close(pipe_fd[1]);
               while(1)
               {
                   //父进程读
                   int ret=read(pipe_fd[0],buf,sizeof(buf));
                   switch(ret)
                   {
                   case -1:
                       perror("read");
                       wait(NULL);
                       _exit(0);
                   case 0:
                       printf("read over....\n");
                       close(pipe_fd[0]);
                       wait(NULL);
                       _exit(0);                                                                  
                   default:
                       printf("parent read %d byte date : %s",ret,buf);
                       break;
                   }
               }
           }
           if(0==pid)
           {
               close(pipe_fd[0]);
              int num=0;
               for(;num<10;num++)
               {
                   //子进程写
                   sprintf(buf,"child write %d\n",num*17);
                   int len=strlen(buf)+1;
                   write(pipe_fd[1],buf,len);
                   printf("child send %d byte buf date: %s",len,buf);
                   sleep(1);
              }
               printf("end of write....\n");
               close(pipe_fd[1]);
           }
           return 0;
      }
      
      
      
  2. 有名管道
    • 介绍
    •     会在磁盘上创建一个大小为0的管道文件,能够实现有血缘关系和无血缘关系进程间的通信。

    • 函数
    •        #include <sys/types.h>
             #include <sys/stat.h>
             int mkfifo(const char *pathname, mode_t mode);
                  参数:
                      pathname:传入参数,要创建的管道文件的路径及文件名
                      mode:创建的文件权限,需要与~umask做按位与操作
                  返回值:
                      0:成功
                      -1:失败
      
    • 测试代码
    • write.c

       int main()
       {
           int ret=mkfifo("test",0664);
           if(-1==ret)
               perror("mkfifo");
          int fd=open("test",O_WRONLY);
           if(-1==fd)
              perror("open");
           char buf[64];
           for(int i=0;i<10;++i)
           {
               sprintf(buf,"date : %d\n",i);
               write(fd,buf,strlen(buf)+1);
               sleep(1);                                       
           }
           close(fd);
           return 0;
       }
      
      
      

      read.c

       int main()
       {
           int fd=open("test",O_RDONLY);
           if(-1==fd)
               perror("open");
           char buf[64];
           int ret=0;
           while(1)
           {
              ret=read(fd,buf,sizeof(buf));
               if(0==ret)
              {
                   printf("写端已关闭。。。\n");                                      
                  break;
               }
               printf("%s",buf);
           }
           close(fd);
           return 0;
           
       }
      
  3. 共同点
    • 默认都是是阻塞的,如果只打开了一端,则阻塞。
    • 数据结构都是队列,数据先进先出,并且一个只能被读一次。
    • 如果在通信过程中,写端被关闭,读端解除阻塞。
    • 如果在通信过程中,读端关闭,则操作系统会给写端发送一个SIGPIPE信号,终止写端进程,这个行为称作管道破裂。


    二、内存映射

        内存映射是将磁盘文件映射到内存中,进程通过修改内存就能修改磁盘文件。通信过程中不阻塞。

    • 通信流程
      • 有血缘关系
      •     (1)父进程创建内存映射区。
            (2)父进程创建子进程,它们共享父进程创建出的内存映射区。
      • 无血缘关系
      •     (1)创建一个大小非0的磁盘文件。
            (2)进程1通过这个磁盘文件创建内存映射区。
            (3)进程2通过这个磁盘文件创建内存映射区。
    • 函数
    • #include <sys/mman.h>
      void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
          参数:
              addr: 传出参数,指向创建的内存映射区首地址
              length:要创建的内存映射区的大小
              prot: 对内存映射区的操作权限
                  PROT_READ: 读权限, 这个权限必须要有
                  PROT_WRITE: 写权限
                  PROT_READ | PROT_WRITE: 读写权限
              flags:
                  MAP_SHARED: 映射区数据会自动和磁盘文件进行同步, 进程间通信必须设置这个选项
                  MAP_PRIVATE: 不同步
              fd: open得到的文件描述符,调用是的flags必须和mmap的flags权限相同
              offset: 指针偏移量,需要为4k的整数倍
      
    • 测试代码
    • 有血缘关系

       int main()
       {
           int fd=open("test.txt",O_RDWR);
           int size=lseek(fd,0,SEEK_END);
           void* ptr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
           pid_t pid=fork();
           if(pid>0)
           {
               strcpy((char*)ptr,"hello");
               wait(NULL);
           }
           if(pid==0)
           {
               char buf[32];
               strcpy(buf,(char*)ptr);
               printf("%s\n",buf);                                                                      
           }
           return 0;
       }
      
      

      无血缘关系
         进程1

       int main()
       {
           int fd=open("test.txt",O_RDWR);
           int size=lseek(fd,0,SEEK_END);
           void *ptr=mmap(NULL,size,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
           int i=0;
           char buf[32];                                                                        
           sprintf(buf,"hello%d",i++);
           strcpy((char*)ptr,buf);
           sleep(1);
           return 0;
       }
      
      

         进程2

       int main()
       {
           int fd=open("test.txt",O_RDONLY);
           int size=lseek(fd,0,SEEK_END);
           void *ptr=mmap(NULL,size,PROT_READ,MAP_SHARED,fd,0);
           char buf[32];
           strcpy(buf,(char*)ptr);
           printf("%s\n",buf); 
           return 0;
       }