加入收藏 | 设为首页 | 会员中心 | 我要投稿 衡阳站长网 (https://www.0734zz.cn/)- 数据集成、设备管理、备份、数据加密、智能搜索!
当前位置: 首页 > 服务器 > 搭建环境 > Windows > 正文

Linux下的进程间通信:共享存储

发布时间:2019-05-10 23:18:40 所属栏目:Windows 来源:Marty Kalin
导读:学习在 Linux 中进程是如何与其他进程进行同步的。 本篇是 Linux 下进程间通信(IPC)系列的第一篇文章。这个系列将使用 C 语言代码示例来阐明以下 IPC 机制: 共享文件 共享内存(使用信号量) 管道(命名的或非命名的管道) 消息队列 套接字 信号 在聚焦
副标题[/!--empirenews.page--]

 Linux下的进程间通信:共享存储

学习在 Linux 中进程是如何与其他进程进行同步的。

本篇是 Linux 下进程间通信(IPC)系列的第一篇文章。这个系列将使用 C 语言代码示例来阐明以下 IPC 机制:

  • 共享文件
  • 共享内存(使用信号量)
  • 管道(命名的或非命名的管道)
  • 消息队列
  • 套接字
  • 信号

在聚焦上面提到的共享文件和共享内存这两个机制之前,这篇文章将带你回顾一些核心的概念。

核心概念

进程是运行着的程序,每个进程都有着它自己的地址空间,这些空间由进程被允许访问的内存地址组成。进程有一个或多个执行线程,而线程是一系列执行指令的集合:单线程进程就只有一个线程,而多线程的进程则有多个线程。一个进程中的线程共享各种资源,特别是地址空间。另外,一个进程中的线程可以直接通过共享内存来进行通信,尽管某些现代语言(例如 Go)鼓励一种更有序的方式,例如使用线程安全的通道。当然对于不同的进程,默认情况下,它们不能共享内存。

有多种方法启动之后要进行通信的进程,下面所举的例子中主要使用了下面的两种方法:

  • 一个终端被用来启动一个进程,另外一个不同的终端被用来启动另一个。
  • 在一个进程(父进程)中调用系统函数 fork,以此生发另一个进程(子进程)。

第一个例子采用了上面使用终端的方法。这些代码示例的 ZIP 压缩包可以从我的网站下载到。

共享文件

程序员对文件访问应该都已经很熟识了,包括许多坑(不存在的文件、文件权限损坏等等),这些问题困扰着程序对文件的使用。尽管如此,共享文件可能是最为基础的 IPC 机制了。考虑一下下面这样一个相对简单的例子,其中一个进程(生产者 producer)创建和写入一个文件,然后另一个进程(消费者 consumer)从这个相同的文件中进行读取:

  1. writes +-----------+ reads
  2. producer-------->| disk file |<-------consumer
  3. +-----------+

在使用这个 IPC 机制时最明显的挑战是竞争条件可能会发生:生产者和消费者可能恰好在同一时间访问该文件,从而使得输出结果不确定。为了避免竞争条件的发生,该文件在处于状态时必须以某种方式处于被锁状态,从而阻止在操作执行时和其他操作的冲突。在标准系统库中与锁相关的 API 可以被总结如下:

  • 生产者应该在写入文件时获得一个文件的排斥锁。一个排斥锁最多被一个进程所拥有。这样就可以排除掉竞争条件的发生,因为在锁被释放之前没有其他的进程可以访问这个文件。
  • 消费者应该在从文件中读取内容时得到至少一个共享锁。多个读取者可以同时保有一个共享锁,但是没有写入者可以获取到文件内容,甚至在当只有一个读取者保有一个共享锁时。

共享锁可以提升效率。假如一个进程只是读入一个文件的内容,而不去改变它的内容,就没有什么原因阻止其他进程来做同样的事。但如果需要写入内容,则很显然需要文件有排斥锁。

标准的 I/O 库中包含一个名为 fcntl 的实用函数,它可以被用来检查或者操作一个文件上的排斥锁和共享锁。该函数通过一个文件描述符(一个在进程中的非负整数值)来标记一个文件(在不同的进程中不同的文件描述符可能标记同一个物理文件)。对于文件的锁定, Linux 提供了名为 flock 的库函数,它是 fcntl 的一个精简包装。第一个例子中使用 fcntl 函数来暴露这些 API 细节。

示例 1. 生产者程序

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5.  
  6. #define FileName "data.dat"
  7.  
  8. void report_and_exit(const char* msg) {
  9. [perror][4](msg);
  10. [exit][5](-1); /* EXIT_FAILURE */
  11. }
  12.  
  13. int main() {
  14. struct flock lock;
  15. lock.l_type = F_WRLCK; /* read/write (exclusive) lock */
  16. lock.l_whence = SEEK_SET; /* base for seek offsets */
  17. lock.l_start = 0; /* 1st byte in file */
  18. lock.l_len = 0; /* 0 here means 'until EOF' */
  19. lock.l_pid = getpid(); /* process id */
  20.  
  21. int fd; /* file descriptor to identify a file within a process */
  22. if ((fd = open(FileName, O_RDONLY)) < 0) /* -1 signals an error */
  23. report_and_exit("open to read failed...");
  24.  
  25. /* If the file is write-locked, we can't continue. */
  26. fcntl(fd, F_GETLK, &lock); /* sets lock.l_type to F_UNLCK if no write lock */
  27. if (lock.l_type != F_UNLCK)
  28. report_and_exit("file is still write locked...");
  29.  
  30. lock.l_type = F_RDLCK; /* prevents any writing during the reading */
  31. if (fcntl(fd, F_SETLK, &lock) < 0)
  32. report_and_exit("can't get a read-only lock...");
  33.  
  34. /* Read the bytes (they happen to be ASCII codes) one at a time. */
  35. int c; /* buffer for read bytes */
  36. while (read(fd, &c, 1) > 0) /* 0 signals EOF */
  37. write(STDOUT_FILENO, &c, 1); /* write one byte to the standard output */
  38.  
  39. /* Release the lock explicitly. */
  40. lock.l_type = F_UNLCK;
  41. if (fcntl(fd, F_SETLK, &lock) < 0)
  42. report_and_exit("explicit unlocking failed...");
  43.  
  44. close(fd);
  45. return 0;
  46. }

上面生产者程序的主要步骤可以总结如下:

  • (编辑:衡阳站长网)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读