笔记三:进程间的通信(管道)

笔记三:进程间的通信(管道)
1)内核空间的对象是不存在的;
2)进程间的通信每一种通信方式都是基于文件IO思想;open函数,read函数,write函数,close函数等等;
3)通过open函数打开或者创建一个文件(只能创建普通文件),当我们打开一个文件,就会在内核空间开辟一个缓存,通过write往内核写,以及用户空间从内核read;
对象不一样意味着通信方式不一样
2.有哪几种通信方式?
管道通信: 无名管道、有名管道(文件系统中有名)
信号通信: 信号(通知)通信包括:信号的发送、信号的接收和信号的处理。
IPC(Inter-Process Communication)通信: 共享内存、消息队列和信号灯。
以上是单机模式下的进程通信(只有一个Linux内核)
Socket通信: 存在于一个网络中两个进程之间的通信(两个Linux内核)。
管道: 无名管道和有名管道(管道其实就是一个队列)只能在两端就行插入和删除    出队:读   入队:写
无名管道: 在文件系统中无文件节点或者说无文件名(特殊的文件);
有名管道: 在文件系统中有文件节点或者说有文件名(特殊的文件);
二:无名管道
通信原理: 管道文件是一个特殊的文件,是由队列来实现的。
无名管道通过pipe函数创建特殊文件(管道)返回一个文件描叙符
在文件IO中创建一个文件或打开一个文件是由open函数来实现的,它不能创建管道文件。只能用pipe函数来创建管道。
函数形式:int pipe(int fd[2])
功能:创建管道,为系统调用:unistd.h
参数:就是得到的文件描述符。可见有两个文件描述符:fd[0]和fd[1],管道有一个读端fd[0]用来读和一个写端fd[1]用来写,这个规定不能变。
返回值:成功是0,出错是-1;
例1:pipe函数使用
/*无名管道*/
#include "unistd.h"
#include "string.h"
#include "stdio.h"
int main()
{
  int fd[2];
  int ret;
  char buf[]="hello linux\n";
  char readbuf[128]={0};
  ret=pipe(fd);//创建管道
  if(ret < 0)
   {
        printf("创建管道文件失败");
        return -1;
   }
  printf("%d,%d\n",fd[0],fd[1]);//fd[0] read fd[1]  write
  write(fd[1],buf,sizeof(buf));//往管道中写
  read(fd[0],readbuf,128);
  printf("readbuf=%s",readbuf);
  memset(readbuf,0,128);//清空数组
  read(fd[0],readbuf,128);//从管道中读取
  printf("second read :readbuf=%s",readbuf);
  close(fd[0]);
  close(fd[1]);
  return 0;
}

注意:
管道中的东西,读完了就删除了;队列
如果管道中没有东西可读,则会读阻塞。
例2:验证读阻塞。
        读取完以后如果在读取进程将处于阻塞状态
例3:验证写阻塞:可以计算出内核开辟的管道有多大。5456 处于不阻塞     5457处于阻塞状态

/*无名管道*/
#include "unistd.h"
#include "string.h"
#include "stdio.h"

int main()
{
    int fd[2];
    int ret;
    int i = 0;
    char buf[]="hello linux\n";
    char readbuf[128]={0};
    ret=pipe(fd);//创建管道
    if(ret < 0)
    {
        printf("创建管道文件失败");
        return -1;
    }
    printf("%d,%d\n",fd[0],fd[1]);//fd[0]:read  fd[1]:write
    while(i < 5500)
    {   
        write(fd[1],buf,sizeof(buf));//往管道中写
        i++;
    }
    /*总共写5500次(hello linux),如果阻塞已经超过范围,那么下面一句话将不会打印出来*/
    printf("write pipe end\n");
    close(fd[0]);
    close(fd[1]);
    return 0;
}

例4:实现进程通信
通过管道实现用户间的进程通信(实现A与B通信,要对同一个管道进行通信)通过fork函数把父进程都进行了拷贝,除了pid号
/*通过pipe管道实现两个进程间的通信,如果没有管道进程是无法在用户空间进行通信的*/
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
    int pid;
    int i;
    char flag=0;
    int fd[2];
    pipe(fd);//必须在创建父子进程之前创建管道,保证对同一个管道进行通信(操作);
    pid=fork();
    if(pid < 0)
        return -1;
    /*达到父进程先运行,子进程才运行目的*/
    if(pid == 0)
    {
        read(fd[0],&flag,1);//pipd empty  --- > sleep
       // while(flag ==0 );
        if(flag == 1)
        {
            for(i=0;i<10;i++)
            {
                usleep(100);
                printf("this is child process i=%d\n",i);
            }
        }
        while(1);
     }
   if(pid > 0)
   {
        for(i=0;i<10;i++)
        {
            usleep(100);
            printf("this is parent process i=%d\n",i);
        }
            flag =1 ;
            write(fd[1],&flag,1);
            while(1);
    }
     return 0;
}
无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信,正由于这无名管道的缺点,对无名管道进行改进:有名管道。
所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个inode号
而且这是一个特殊的文件类型:p管道类型。
1. 创建这个文件节点,不可以通过open 函数,open 函数只能创建普通文件,不能创建特殊文件(管道-mkfifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod,符号链接文件-ln –s,目录文件mkdir)
2. 管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间。
3.  mkfifo   用来创建管道文件的节点,没有在内核中创建管道。
只有通过open 函数打开这个文件时才会在内核空间创建管道。
有名管道(由于无名管道只能实现父子进程或有情缘关系进程的通信,但是往往我们的程序(实际应用)都是无亲缘关系,所以就衍生了有名管道)
普通文件          ------------>    ‘_’      通过open函数创建
目录文件          ------------>   'd'       通过mkdir函数创建
链接文件(主要是指软链接)----------->     ‘l’   ln -s   选项创建
管道文件      --------------->        通过mkfifo或者pipe系统调用函数创建
三.有名管道
mkfifo(只是生成了一个文件,并没有在内核生成一个管道,当通过open函数打开这个节点时,会在内核空间生成一个管道(缓存))
函数形式:int mkfifo(const char *filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。
返回值:创建成功返回0,创建失败返回-1。
例1:mkfifo的用法。
#include "unistd.h"
#include "stdio.h"

int main()
{
    int fd;
      fd = mkfifo("./a.c",0777);//在内核创建一个文件,此时并没有在内核创建管道,只有通过open函数打开时才会在内核创建一个管道
      if(fd < 0)
      {
          printf("creat mkfifo failure\n");
            return -1;
      }     
      return 0;
}  
例子:通过管道实现无情缘进程间的通信
/*mkfifo创建管道(mkfifo.c)*/
#include "stdio.h"
#include "unistd.h"

int main()
{
    int fd,fd1;
      fd = mkfifo("./a.c",0777);
      if(fd < 0)
      {
          printf("creat myfifo a.c failure\n");
            return -1;
      }     
      fd1 = mkfifo("./b.c",0777);
      if(fd1 < 0)
      {
          printf("creat myfifo b.c failure\n");
            return -2;
      }     
      return 0;
}
/*read端(read.c)*/
#include "unistd.h"
#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main()
{
      int fd;
      int ret;
      int pid;
      char writebuf[128] = {0},readbuf[128] = {0};
      pid = fork();
      if(pid < 0)
      {
          printf("creat process failure\n");
            return -1;
      }     
      if(pid > 0)
      {
            fd = open("./b.c",O_WRONLY);//当open函数打开这个管道文件时,会在内核生成一个管道(管道);
          while(1)
            {
                fgets(writebuf,128,stdin);
                  if(strncmp(writebuf,"quit",4) == 0)
                        break;
                  write(fd,writebuf,strlen(writebuf));
                  memset(writebuf,0,128);
            }     
      }     
    if(pid == 0)
      {
          fd =open("./a.c",O_RDONLY);
            while(1)
            {
                read(fd,readbuf,128);
                  if(strncmp(readbuf,"quit",4) == 0)
                        break;
                  printf("from a.c read=%s\n",readbuf);
                  memset(readbuf,0,128);
            }     
      }     
            return 0;

}     
/*write端(write.c)*/
#include "unistd.h"
#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main()
{
      int fd;
      int ret;
      int pid;
      char writebuf[128] = {0},readbuf[128] = {0};
      pid = fork();
      if(pid < 0)
      {
          printf("creat process failure\n");
            return -1;
      }     
      if(pid > 0)
      {
            fd = open("./a.c",O_WRONLY);
          while(1)
            {
                fgets(writebuf,128,stdin);
                  if(strncmp(writebuf,"quit",4) == 0)
                        break;
                  write(fd,writebuf,strlen(writebuf));
                  memset(writebuf,0,128);
            }     
      }     
    if(pid == 0)
      {
          fd =open("./b.c",O_RDONLY);
            while(1)
            {
                read(fd,readbuf,128);
                  if(strncmp(readbuf,"quit",4) == 0)
                        break;
                  printf("from b.c read=%s\n",readbuf);
                  memset(readbuf,0,128);
            }     
      }     
            return 0;
} 
posted @ 2017-02-26 16:28  奔涌吧,后浪  阅读(19)  评论(0编辑  收藏  举报