Linux 进程间通信

来源:互联网 发布:windows 2008 server 编辑:程序博客网 时间:2024/06/14 17:27

进程间通信

  • 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
  • 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件。
  • 资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

信号

通过注册信号与全局变量的结合达到进程间通信
优点:实时,不阻塞
缺点:不安全,内容固定
案例:

void func(int n){    printf("ping\n");     // how to send signal 4 to the second process?}void func2(int n){  printf("pong\n");  // how to send signal 3 to the first process?}int main(){  pid_t pid;  int i;  for(i = 0; i < 2; i++){    pid = fork();    if(pid == 0){      if(i == 0){        signal(3, func);      }else{        signal(4, func2);      }      while(1);    }else{      if(i == 1){        sleep(3);        // how to send signal 3 to the first child process?        sleep(3);        // how to kill the two children?      }    }  }  return 0;}

管道

  • 管道是针对于本地计算机的两个进程之间的通信而设计的通信方法,管道建立后,实际获得两个文件描述符:一个用于读取而另外一个用于写入。
  • 最常见的IPC机制,通过pipe系统调用。
  • 管道是单工的,数据只能向一个方向流动,需要双向通信时,需要建立起两个管道。
  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
  • 通过打开两个管道来创建一个双向的管道
  • 管道是阻塞性的,当进程从管道中读取数据,若没有数据进程会阻塞。
  • 当一个进程往管道中不断地写入数据但是没有进程去读取数据,此时只要管道没有满是可以的,但若管道放满数据的则会报错。

匿名管道

  • 在关系进程中进行(父进程和子进程、兄弟进程之间)
  • 由pipe系统调用,管道由父进程建立。
  • 管道位于内核空间,其实是一块缓存。
  • int pipe(int fd[2]);
    • 功能:创建匿名管道pipe
    • 返回: 成功返回0 ,出错返回-1
    • fd[0]: 为pipe的读端,用于读取管道;fd[1]: 为pipe的写端,用于写入管道

案例:

int main(){  //匿名管道,第一步:定义一个整型的长度为2的数组  int fd[2] = { };  if(pipe(fd)){    perror("管道创建失败\n");    exit(-1);  }  int pid = fork();  if(pid == 0){//子进程 -- 读消息    close(fd[1]);    while(1){      printf("子进程正在等待消息...\n");      char buffer[1024] = " ";      read(fd[0],buffer,1024) ;      printf("读取到%s\n",buffer);      if(strcmp(buffer,"end") == 0) break;    }    close(fd[0]);  }else if(pid > 0){//父进程 -- 写消息    close(fd[0]);    while(1){      char buffer[1024] = " ";      printf("请输入你要发送的内容\n");      scanf("%s",buffer);      write(fd[1],buffer,strlen(buffer));      if(strcmp(buffer,"end") == 0) break;    }    wait(NULL);  }else{    perror("进程创建失败\n");    exit(-1);  }  return 0;}

命名管道

  • 两个没有任何关系的进程之间通信可通过命名管道进行数据传输,本质是内核中的一块缓存,另在文件系统中以一个特殊的设备文件(管道文件)存在。
  • 只要对FIFO有适当访问权限,FIFO可用在任何两个没有任何关系的进程之间通信。
  • 本质是内核中的一块缓存,另在文件系统中以一个特殊的设备文件(管道文件)存在。
  • 在文件系统中只有一个索引块存放文件的路径,没有数据块,所有数据存放在内核中。
  • 命名管道必须读和写同时打开,否则单独读或者单独写会引发阻塞。
  • 对FIFO的操作与操作普通文件一样,一般的文件I/O函数都可用于FIFO。
  • 通过系统调用mkfifo创建
  • int mkfifo(const char * pathname, mode_t mode);
    • 功能:创建命名管道
    • 返回:若成功则返回0 ,出错返回-1

案例:

int main(){  if(access("pipe",F_OK)){    if(mkfifo("pipe",0664)){      perror("创建管道文件失败\n");      exit(-1);    }  }  int fd = open("pipe",O_RDWR);  if(fd < 0){    perror("打开文件失败\n");    exit(-1);  }  while(1){    char buffer[1024] = " ";    printf("请输入内容\n");    fgets(buffer,1024,stdin);    write(fd,buffer,strlen(buffer));    if(strcmp(buffer,"end") == 0) break;  }  close(fd);  return 0;}int main(){  if(access("pipe",F_OK)){    if(mkfifo("pipe",0664)){      perror("创建管道文件失败\n");      exit(-1);    }  }  int fd = open("pipe",O_RDWR);  if(fd < 0){    perror("打开文件失败\n");    exit(-1);  }  while(1){    char buffer[1024] = " ";    read(fd,buffer,1024);    printf("%s\n",buffer);    if(strcmp(buffer,"end") == 0) break;  }  close(fd);  return 0;}

popen管道

  • The popen() function opens a process by creating a pipe, forking, and invoking the shell.
  • FILE *popen(const char *cmdstring, const char *type);
    • 返回值: 成功返回文件指针,出错返回NULL
  • int pclose(FILE *fp);
    • 返回值: cmdstring 的终止状态,出错返回-1

案例:

int main(){  // FILE *popen(const char *command, const char *type);  // command:执行的shell命令  // type:打开方式。只能是 "r",或者"w"其他都会报错  // 本质上:创建一个管道,执行command命令,并将执行的结果保存到管道里面。   FILE* fp = popen("ls","r");  if(fp == NULL){    perror("创建管道失败\n");    exit(-1);  }  while(1){    char buffer[1024] = " ";    if(fgets(buffer,1024,fp) == NULL)   break;    printf("%s",buffer);  }  printf("\n");  pclose(fp);  return 0;}

消息队列

共享内存

信号量

原创粉丝点击