linux 6day

Linux 6day

1.进程间通信

进程间通信 进程是孤立的故此需要去访问其他进程 IPC 进程间通讯

进程通信一般用于 数据传输 通知数据 资源共享 进程控制

进程通信的方法

zc

2.无名管道

1.特点

管道特点

管道抽象图

2.管道创建 pipe函数
 #include <unistd.h>
 int pipe(int pipefd[2]);
 功能:创建无名管道。
 参数:
     pipefd : int 型数组的首地址,其存放了管道的文件描述符 pipefd[0]、pipefd[1]
     当一个管道建立时,它会创建两个文件描述符 fd[0] fd[1]。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。一般文件 I/O的函数都可以用来操作管道(lseek() 除外)
 返回值:
     成功:0
     失败:-1
 int fds[2];
 int ret=-1;
 ret=pipe(fds);
3.利用无名管道进行父子间通信

父子进程管道通信

 int main()
 {
 int fds[2];
 int ret=-1;
 pid_t pid;
 ret=pipe(fds);
 pid=fork();
 if(pid==0)
 {
 char buf[]=" i am cxy";
 write(fds[1],buf,strlen(buf));///管道写
 _exit(0);
 }
 else if(pid>0)
 {
 wait(NULL);
 char str[80]={0};
 read(fds[0],str,sizeof(str));//管道读
 
 printf("str=[%s]\n", str);
 }

管道阻塞属性 当父进程读管道内无数据就会阻塞 只有写了才会有数据

4.管道读写特点

管道读写特点

读写特点

5.查看缓冲区大小

命令查看缓冲区大小

     #include <unistd.h>
 long fpathconf(int fd, int name);
 功能:该函数可以通过name参数查看不同的属性值
 参数:
     fd:文件描述符
     name:
         _PC_PIPE_BUF,查看管道缓冲区大小
         _PC_NAME_MAX,文件名字字节数的上限
 返回值:
     成功:根据name返回的值的意义也不同。
     失败: -1
     long num = fpathconf(fd[0], _PC_PIPE_BUF);
 
     printf("num = %ld\n", num);
6.设置非阻塞
 设置方法:
 //获取原来的flags
 int flags = fcntl(fd[0], F_GETFL);
 // 设置新的flags
 flag |= O_NONBLOCK;
 // flags = flags | O_NONBLOCK;
 fcntl(fd[0], F_SETFL, flags);

3.有名管道

1、有名管道概念

管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道、FIFO文件。

命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,只要可以访问该路径,就能够彼此通过 FIFO 相互通信.

FIFO 文件在内存中->进程退出后也一直在。

2.管道的创建

1.命令法

mkfifo name

2.函数mkfifo

 
 #include <sys/types.h>
 #include <sys/stat.h>
 
 int mkfifo(const char *pathname, mode_t mode);
 功能:
     命名管道的创建。
 参数:
     pathname : 普通的路径名,也就是创建后 FIFO 的名字。
     mode : 文件的权限,与打开普通文件的 open() 函数中的 mode 参数相同。(0666)
 返回值:
     成功:0   状态码
     失败:如果文件已经存在,则会出错且返回 -1
3.管道读写操作

一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。

FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

 
 //进行1,写操作
 int fd = open("my_fifo", O_WRONLY);  
 
 char send[100] = "Hello Mike";
 write(fd, send, strlen(send));
 
 //进程2,读操作
 int fd = open("my_fifo", O_RDONLY);//等着只写  
 
 char recv[100] = { 0 };
 //读数据,命名管道没数据时会阻塞,有数据时就取出来  
 read(fd, recv, sizeof(recv));
 printf("read from my_fifo buf=[%s]\n", recv);
4.利用管道实现简单版本聊天

1.一个为只读而打开一个管道的进程会阻塞直到另外一个进程为只写打开该管道

2一个为只写而打开一个管道的进程会阻塞直到另外一个进程为只读打开该管道

聊天实现原理

进程A

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #define SIZE 128
 int main()
 {
  //进程A
  int ret = -1;
  int fdr = -1;
  int fdw = -1;
  pid_t pid = -1;
  char buf[SIZE];
 
  //只读打开管道1 只写打开管道2
  fdr = open("gd1", O_RDONLY);
  if (-1 == fdr)
  {
  perror("open");
  }
 
  //只写打开管道2
  fdw = open("gd2", O_WRONLY);
  if (-1 == fdw)
  {
  perror("open");
  }
  //读写
  while (1)
  {
  //读管道1
  memset(buf, 0, SIZE);
  ret = read(fdr, buf, SIZE);
  if (ret <= 0)
  {
  perror("read");
  break;
  }
                      printf("\033[31mbuf: %s\033[0m\n", buf);  //VT
 
  ///写管道2
  memset(buf, 0, SIZE);
  fgets(buf, SIZE, stdin);
  buf[strlen(buf) - 1] = 0;
  ret = write(fdw, buf, strlen(buf));
 
  }
 
  //关闭
  close(fdr);
  close(fdw);
  return 0;
 }
 

进程B

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
 #define SIZE 128
 int main()
 {
  //进程B
  int ret = -1;
  int fdr = -1;
  int fdw = -1;
  pid_t pid = -1;
  char buf[SIZE];
 
  //只读打开管道2 只写打开管道1
  fdw = open("gd1", O_WRONLY);
  if (-1 == fdr)
  {
  perror("open");
  }
 
  //只写打开管道2
  fdr= open("gd2", O_RDONLY);
  if (-1 == fdw)
  {
  perror("open");
  }
  //读写
  while (1)
  {
  //写管道1
  memset(buf, 0, SIZE);
  fgets(buf, SIZE, stdin);
                     
  buf[strlen(buf) - 1] = 0;
  ret = write(fdw, buf, strlen(buf));
  //读管道2
  memset(buf, 0, SIZE);
  ret = read(fdr, buf, SIZE);
   
  if (ret <= 0)
  {
  perror("read");
  break;
  }
        printf("\033[31mbuf: %s\033[0m\n", buf);  //VT
  ///写管道2
 
 
  }
 
  //关闭
  close(fdr);
  close(fdw);
  return 0;
 }
 

 

4.共享存储映射

1.共享存储映射概念

存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。

 

存储映射原理图

于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式, 因为进程可以直接读写内存,而不需要任何数据的拷贝。

2.存储映射函数 关联内存 取消内存
 #include <sys/mman.h>
 
 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
 功能:
    一个文件或者其它对象映射进内存
 参数:
    addr : 指定映射的起始地址, 通常设为NULL, 由系统指定
    length:映射到内存的文件长度
    prot: 映射区的保护方式, 最常用的 :
        a) 读:PROT_READ
        b) 写:PROT_WRITE
        c) 读写:PROT_READ | PROT_WRITE
    flags: 映射区的特性, 可以是
        a) MAP_SHARED : 写入映射区的数据会复制回文件, 且允许其他映射该文件的进程共享。
        b) MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
    fd:由open返回的文件描述符, 代表要映射的文件。
    offset:以文件开始处的偏移量, 必须是4k的整数倍, 通常为0, 表示从文件头开始映射
 返回值:
    成功:返回创建的映射区首地址
    失败:MAP_FAILED宏
 

关于mmap函数的使用总结:

  1. 第一个参数写成NULL

  2. 第二个参数要映射的文件大小 > 0

  3. 第三个参数:PROT_READ 、PROT_WRITE

  4. 第四个参数:MAP_SHARED 或者 MAP_PRIVATE

  5. 第五个参数:打开的文件对应的文件描述符

  6. 第六个参数:4k的整数倍,通常为0

 

(2) munmap函数

 
 #include <sys/mman.h>
 
 int munmap(void *addr, size_t length);
 功能:
     释放内存映射区
 参数:
     addr:使用mmap函数创建的映射区的首地址
     length:映射区的大小
 返回值:
     成功:0
     失败:-1
3.注意事项
  1. 创建映射区的过程中,隐含着一次对映射文件的读操作。

  2. 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制。

  3. 映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。

  4. 特别注意,当映射文件大小为0时,不能创建映射区。所以,用于映射的文件必须要有实际大小。mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。

  5. munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作。

  6. 如果文件偏移量必须为4K的整数倍。

  7. mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

4.共享映射实现父子进程通信
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <wait.h>
 
 int main()
 {
  //子写父读
  int fd = -1;
  int ret = -1;
  void* addr = NULL;
  pid_t pid = -1;
  //打开文件
  fd = open("txt.txt", O_RDWR);
  if (-1 == fd)
  {
  perror("open");
  return -1;
  }
  //关联文件
  addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  //开辟子进程
  pid = fork();
  if (-1 == pid)
  {
  perror("fork");
  return -1;
  }
  printf("映射完成");
 
  if (0 == pid)
  {
  //子写
  memccpy(addr, "ABCDEFG", 10);
  }
  else
  {
  wait(NULL);
  printf("addr %s\n", (char*)addr);
  }
 
 
 }

 

5.不同进程实现通信

一读一写

read

 h>
 #include <sys/mman.h>
 #include <wait.h>
 
 int main()
 {
  //子写父读
  int fd = -1;
  int ret = -1;
  void* addr = NULL;
  pid_t pid = -1;
  //打开文件
  fd = open("txt.txt", O_RDWR);
  if (-1 == fd)
  {
  perror("open");
  return -1;
  }
  //关联文件
  addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  //开辟子进程
 
  printf("映射完成\n");
 
 ///du
  printf("addr %s\n", (char*)addr);
 
 
 }

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <wait.h>
 
 int main()
 {
  //子写父读
  int fd = -1;
  int ret = -1;
  void* addr = NULL;
  pid_t pid = -1;
  //打开文件
  fd = open("txt.txt", O_RDWR);
  if (-1 == fd)
  {
  perror("open");
  return -1;
  }
  //关联文件
  addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  //开辟子进程
 
  printf("映射完成\n");
 
  //子写
  memcpy(addr, "hhhhhhh", 10);
 
 
 
 
6.匿名映射实现父子进程通信
     addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

 



posted @ 2023-07-03 23:10  大橘|博客  阅读(22)  评论(0)    收藏  举报