跳转至

Lecture 8. Unix 管道与套接字(16 页)

普通管道

  • 管道(pipe) 是一种在所有 Unix 版本中都提供的进程间通信机制。现在先考虑匿名管道(普通管道)。
  • 对于运行在同一设备上的进程,可以使用管道来实现高速的信息传递。
  • 管道是 FIFO 的,像一个隧道。由系统内核以有限大小的 缓冲区实现
  • 匿名管道必须是两个相关的进程才可以访问。比如父进程和子进程。要让子进程拥有权限,必须先 创建管道,再创建子进程
  • 一个进程在管道的一端写入数据,另一个进程从管道的另一边读数据。因此,匿名管道是单向 的。
  • 匿名管道的生命周期随进程,命名管道的生命周期随系统。也就是说当没有进程访问一个匿名管道的时候,这个匿名管道就消失了。但命名管道会像文件一样持续存在。

使用 C 语言操作匿名管道

首先,创建

int fdptr[2];
// blabla
pipe(fdptr);
// 执行成功后,fdptr[0] 与 fdptr[1] 是两个文件描述符,分别描述管道的两端(0 读,1 写)

stdinstdoutstderr 分别是 0、1、2,因此 fdptr[0]fdptr[1] 最小值是 3。

接着,进行读写

char strbuff[5];
write(fdptr[1], "welcome", 7);  // 向管道写入数据
int n = read(fdptr[0], strbuff, 5);  // n 是实际读到的字节数

关于 read()

  • 注意如果管道里面啥都没有,那么 read() 就会被阻塞
  • 如果管道里面有东西但是不够第三个参数那么多,那么不会被阻塞,返回实际读到的字节数

命名管道 named pipe

对于两个没有直接关联的进程,如果对于同一个 file name space 具有权限,那么可以使用命名管道进行通信。

命名管道也是 FIFO 的,只不过是以类似于文件的形式存在的。甚至对于使用同样文件系统的不同设备,可以把这个命名管道用 U 盘啊之类的东西拷过去然后再进行跨设备的进程间通信。

使用 C 语言操作命名管道

首先,创建

mknod("mypipe", 010777, 0);

参数解释:

  • mknod 用于创建文件(命名管道),名字叫 mypipe
  • 010777 是八进制的表示。10 表示这个文件比较特殊,777 表示 owner、group、world 三者对此文件的读、写、执行的权限
  • 0 不用管

接着,进行读写

namedpipe = open("mypipe", O_WRONLY);
namedpipe = open("mypipe", O_RDONLY);  // 只读
  • 如果没有进程写,那么读的就会阻塞
  • 如果没有进程读,那么写的就会阻塞

网络套接字

套接字是独立进程之间通信的更加常见的形式。无论是系统内核的数据结构还是文件,都可以通过套接字传输。

管道与命名管道的通信具有局限性:管道通信必须要求两个进程之间有关联,而命名管道需要使用公共文件系统。对于既不存在关联、也不共享文件系统的进程,要实现通信,就要考虑 TCP/IP 等机制。

套接字是由进程创建的,由 IP 地址与端口号组成。每一对进程需要使用一对(也就是俩)套接字进行通信。

通信服务(比如 TCP/IP),可以为不同进程之间套接字的连接提供方案。

套接字连接之后,数据(字节)从本地套接字发送到远程套接字。然后远程设备通过监听套接字上的数据变化,来接收数据。

TCP 套接字是 面向连接(connection-oriented) 的,UDP 套接字是 无连接(connectionless) 的。面向连接就会稳定一些但是牺牲一些速度,vice versa。

像 FTP 服务器使用 21 号端口,HTTP 服务器使用 80 号端口,这些小于 1024 的端口都是众所周知(well known)的。

TCP 套接字连接过程示意图

  • bind() 是指把特定端口和 IP 地址结合起来
  • listen() 就是开始监听
  • connect() 过程涉及到了 三次握手,图示如下(SYN 表示搞个序列号希望连接,ACK 表示确认)

三次握手

  • A: 嗨,我想跟你初始化(SYN)一个连接
  • B:好,我知道了(ACK),我跟你初始化这个连接(SYN)
  • A:OK,我们的连接搞好了(ACK)

四次挥手

  • A:嗨,我想跟你说的话都说完了(FIN)
  • B:哦我知道了(ACK)
  • (此时 B 还可以给 A 继续说话)
  • B:嗨,我跟你想说的也说完了(FIN)
  • A:哦我知道了(ACK)