凉了!张三同学没答好「进程间通信」,被面试官挂了....
Hollis
共 7339字,需浏览 15分钟
·
2020-08-11 21:22
开场小故事
正文
管道
|
」这个竖线。$ ps auxf | grep mysql
|
」竖线就是一个管道,它的功能是将前一个命令(ps auxf
)的输出,作为后一个命令(grep mysql
)的输入,从这功能描述,可以看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行。|
」表示的管道称为匿名管道,用完了就销毁。FIFO
,因为数据是先进先出的传输方式。mkfifo
命令来创建,并且指定管道名字:$ mkfifo myPipe
$ ls -l
prw-r--r--. 1 root root 0 Jul 17 02:45 myPipe
$ echo "hello" > myPipe // 将数据写进管道
// 停住了 ...
$ cat < myPipe // 读取管道里的数据
hello
那管道如何创建呢,背后原理是什么?
int pipe(int fd[2])
fd[0]
,另一个是管道的写入端描述符 fd[1]
。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。fork
创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0]
与 fd[1]
」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。父进程关闭读取的 fd[0],只保留写入的 fd[1]; 子进程关闭写入的 fd[1],只保留读取的 fd[0];
A | B
命令的时候,A 进程和 B 进程都是 shell 创建出来的子进程,A 和 B 之间不存在父子关系,它俩的父进程都是 shell。|
」匿名管道将多个命令连接在一起,实际上也就是创建了多个子进程,那么在我们编写 shell 脚本时,能使用一个管道搞定的事情,就不要多用一个管道,这样可以减少创建子进程的系统开销。消息队列
MSGMAX
和 MSGMNB
,它们以字节为单位,分别定义了一条消息的最大长度和一个队列的最大长度。共享内存
信号量
一个是 P 操作,这个操作会把信号量减去 -1,相减后如果信号量 < 0,则表明资源已被占用,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使用,进程可正常继续执行。 另一个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;
1
。进程 A 在访问共享内存前,先执行了 P 操作,由于信号量的初始值为 1,故在进程 A 执行 P 操作后信号量变为 0,表示共享资源可用,于是进程 A 就可以访问共享内存。 若此时,进程 B 也想访问共享内存,执行了 P 操作,结果信号量变为了 -1,这就意味着临界资源已被占用,因此进程 B 被阻塞。 直到进程 A 访问完共享内存,才会执行 V 操作,使得信号量恢复为 0,接着就会唤醒阻塞中的线程 B,使得进程 B 可以访问共享内存,最后完成共享内存的访问后,执行 V 操作,使信号量恢复到初始值 1。
1
,就代表着是互斥信号量,它可以保证共享内存在任何时刻只有一个进程在访问,这就很好的保护了共享内存。0
。如果进程 B 比进程 A 先执行了,那么执行到 P 操作时,由于信号量初始值为 0,故信号量会变为 -1,表示进程 A 还没生产数据,于是进程 B 就阻塞等待; 接着,当进程 A 生产完数据后,执行了 V 操作,就会使得信号量变为 0,于是就会唤醒阻塞在 P 操作的进程 B; 最后,进程 B 被唤醒后,意味着进程 A 已经生产了数据,于是进程 B 就可以正常读取数据了。
0
,就代表着是同步信号量,它可以保证进程 A 应在进程 B 之前执行。信号
kill -l
命令,查看所有的信号:$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
Ctrl+C 产生 SIGINT
信号,表示终止该进程;Ctrl+Z 产生 SIGTSTP
信号,表示停止该进程,但还未结束;
kill
命令的方式给进程发送信号,但前提需要知道运行中的进程 PID 号,例如:kill -9 1050 ,表示给 PID 为 1050 的进程发送 SIGKILL
信号,用来立即结束该进程;
SIGKILL
和 SEGSTOP
,它们用于在任何时候中断或结束某一进程。Socket
int socket(int domain, int type, int protocal)
domain 参数用来指定协议族,比如 AF_INET 用于 IPV4、AF_INET6 用于 IPV6、AF_LOCAL/AF_UNIX 用于本机; type 参数用来指定通信特性,比如 SOCK_STREAM 表示的是字节流,对应 TCP、SOCK_DGRAM 表示的是数据报,对应 UDP、SOCK_RAW 表示的是原始套接字; protocal 参数原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成,protocol 目前一般写成 0 即可;
实现 TCP 字节流通信:socket 类型是 AF_INET 和 SOCK_STREAM; 实现 UDP 数据报通信:socket 类型是 AF_INET 和 SOCK_DGRAM; 实现本地进程间通信:「本地字节流 socket 」类型是 AF_LOCAL 和 SOCK_STREAM,「本地数据报 socket 」类型是 AF_LOCAL 和 SOCK_DGRAM。另外,AF_UNIX 和 AF_LOCAL 是等价的,所以 AF_UNIX 也属于本地 socket;
针对 TCP 协议通信的 socket 编程模型
服务端和客户端初始化 socket
,得到文件描述符;服务端调用 bind
,将绑定在 IP 地址和端口;服务端调用 listen
,进行监听;服务端调用 accept
,等待客户端连接;客户端调用 connect
,向服务器端的地址和端口发起连接请求;服务端 accept
返回用于传输的socket
的文件描述符;客户端调用 write
写入数据;服务端调用read
读取数据;客户端断开连接时,会调用 close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
accept
时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。针对 UDP 协议通信的 socket 编程模型
针对本地进程间通信的 socket 编程模型
本地 socket 的编程接口和 IPv4 、IPv6 套接字编程接口是一致的,可以支持「字节流」和「数据报」两种协议; 本地 socket 的实现效率大大高于 IPv4 和 IPv6 的字节流、数据报 socket 实现;
总结
|
」竖线就是匿名管道,通信的数据是无格式的流并且大小受限,通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能用于存在父子关系的进程间通信,匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。SIGKILL
和 SEGSTOP
,这是为了方便我们能在任何时候结束或停止某个进程。互斥的方式,可保证任意时刻只有一个线程访问共享资源; 同步的方式,可保证线程 A 应在线程 B 之前执行;
评论