《操作系统概念》第三章笔记 2
进程间通信方式
进程间通信有两种模型,一种是共享内存,另一种是消息传递,两种模型在操作系统中都十分常见。
POSIX 共享内存
什么是 POSIX
POSIX(Portable Operating System Interface for Computing Systems)是由IEEE 和ISO/IEC 开发的一簇标准。该标准是基于现有的UNIX 实践和经验,描述了操作系统的调用服务接口,用于保证编制的应用程序可以在源代码一级上在多种操作系统上移植运行。所以从某种程度上 POSIX 就是一个标准接口。为了便于程序员们在不同的操作系统上移植代码。
关于 IX 的来源
由于 GNU 不是 Unix,而它的目的是取代 Unix,我不希望人们把 GNU 称为 “Unix 系统”。因此,我提出了一个人们可能真正使用的简洁的名字。在没有特别灵感的情况下,我用了一种很笨的方式取了一个名字。我取了 “可移植操作系统” 的首字母并加上 “ix”。IEEE 马上就采用了这个名字。——Richard Stallman
函数解释
shm_open()
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
参数:name
:第一个参数指定共享内存对象的名称,访问此内存时需要通过这个名称。O_CREAT | O_RDWR
: 这个参数是负责创建内存以及内存读写的。O_CREAT是负责没有内存需要创建(create),O_RDWR是负责打开对象以便读写(read or write0666
:负责设定共享对象的权限, 感觉和 chmod -R 755
有异曲同工之妙。
ftruncate()
ftruncate(shm_fd, 4096)
把内存对象设置为 4096(字节)
mmap()
memory map,字面意思翻译过来就是内存映射,函数干的工作也是内存映射。
作用是将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,省去了系统函数 read
write
函数原型如下:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
prot
:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
PROT_EXEC
:页内容可以被执行PROT_READ
:页内容可以被读取PROT_WRITE
:页可以被写入PROT_NONE
:页不可访问flags
:指定映射对象的类型,映射选项和映射页是否可以共享。offset
:被映射对象内容的起点
更多内容请查看: mmap(2) - Linux manual page (man7.org)例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int main()
{
const int SIZE = 4096;
const char *name = "OS";
const char *message0= "Studying ";
const char *message1= "Operating Systems ";
const char *message2= "Is Fun!";
int shm_fd;
void *ptr;
/* create the shared memory segment */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory segment */
ftruncate(shm_fd,SIZE);
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
return -1;
}
/**
* Now write to the shared memory region.
*
* Note we must increment the value of ptr after each write.
*/
sprintf(ptr,"%s",message0);
ptr += strlen(message0);
sprintf(ptr,"%s",message1);
ptr += strlen(message1);
sprintf(ptr,"%s",message2);
ptr += strlen(message2);
return 0;
}客户机 / 服务机通信
套接字
主要还是在计算机网络里讲,这里书上提供了一个Java编写的 TCP 协议交互程序。没啥好说的,都一样,主要原理还是在计算机网络里讲。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import java.net.*;
import java.io.*;
public class DateServer
{
public static void main(String[] args) {
try {
ServerSocket sock = new ServerSocket(6013);
// now listen for connections
while (true) {
Socket client = sock.accept();
// we have a connection
PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
// write the Date to the socket
pout.println(new java.util.Date().toString());
// close the socket and resume listening for more connections
client.close();
}
}
catch (IOException ioe) {
System.err.println(ioe);
}
}
}管道
管道是一种特殊类型的文件,允许两个进程进行通信。普通管道
普通管道是单向的,意味着通信只能从管道的写入端写,读取端读。如果需要双向管道,就需建立两个管道。
在 Unix 和 Windows 系统中,采用管道交流的进程通信需要有父子关系,只可以用于同一机器的进程间通信,进程通信完成且终止,则管道消失。命名管道
当建立一个命名管道时,多个进程可以使用一个管道通信,且通信可以是双向的,通信结束之后,命名管道仍然存在。
命名管道虽然通信可以是双向的,但是在 Unix 下它是半双工的管道,因此想要实现全双工的管道还是需要建立两条管道。然而在 Windows 下是全双工的管道。
参考
Operating System Concepts - 10th edition (os-book.com)
What is POSIX? Richard Stallman explains | Opensource.com
System v 和Posix - 知乎 (zhihu.com)
POSIX shared-memory API - GeeksforGeeks
mmap(2) - Linux manual page (man7.org)