笔记一:进程间的通信
第一篇只是简单的介绍下一些相关知识,如果想详细了解可以看博客中的相关内容!!!
A与B虽然无法在用户空间通信,但是可以通过访问内核进行通信,访问内核中同一个对象(通过内核中的对象)
内核对象:
进程间的通信方式
2.有哪几种通信方式?
管道通信:无名管道、有名管道(文件系统中有名)
信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。
IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。
以上是单机模式下的进程通信(只有一个Linux内核)
Socket通信:
存在于一个网络中两个进程之间的通信(两个Linux内核)。
线程间通信:可以在用户空间就可以实现,可以通过全局变量通信。
一:进程通信概述
1.什么是进程间通信?
什么是线程间通信?
进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信
线程间通信:可以在用户空间就可以实现,可以通过全局变量通信。(
一个房子里面有两个房间,在房子里面有一个客厅,是两个房间所共有的)
2.有哪几种通信方式?
管道通信:无名管道、有名管道(文件系统中有名)
信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。
IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。
以上是单机模式下的进程通信(只有一个Linux内核)
Socket通信:存在于一个网络中两个进程之间的通信(两个Linux内核)。
3. 进程通信课程的学习思路:每一种通信方式都是基于文件IO的思想。
open:功能:创建或打开进程通信对象。函数形式不一样,有的是有多个函数完成。
write: 功能:向进程通信对象中写入内容。函数形式可能不一样。
read: 功能:从进程通信对象中读取内容。函数形式可能不一样。
close: 功能:关闭或删除进程通信对象。形式可能不一样。
二:无名管道
通信原理:
管道文件是一个特殊的文件,是由队列来实现的。
在文件IO中创建一个文件或打开一个文件是由open函数来实现的,它不能创建管道文件。只能用pipe函数来创建管道。
函数形式:int pipe(int fd[2])
功能:创建管道,为系统调用:unistd.h
参数:就是得到的文件描述符。可见有两个文件描述符:fd[0]和fd[1],管道有一个读端fd[0]用来读和一个写端fd[1]用来写,这个规定不能变。
返回值:成功是0,出错是-1;
例1:pipe函数使用。
注意:
管道中的东西,读完了就删除了;队列
如果管道中没有东西可读,则会读阻塞。
例2:验证读阻塞。
例3:验证写阻塞:可以计算出内核开辟的管道有多大。5456 5457
例4:实现进程通信。
无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信。
无名管道的缺点:只能实现父子进程(有亲缘关系进程)之间的通信。
正由于这无名管道的缺点,对无名管道进行改进:有名管道。
所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个inode号
而且这是一个特殊的文件类型:p管道类型。
1.创建这个文件节点,不可以通过open 函数,open 函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod,符号链接文件-ln –s,目录文件mkdir)
2.管道文件只有inode号,不占磁盘块空间,和套接字、字符设备文件、块设备文件一样。普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘块空间。
3. mkfifo 用来创建管道文件的节点,没有在内核中创建管道。
只有通过open 函数打开这个文件时才会在内核空间创建管道。
3.mkfifo
函数形式:int mkfifo(const char *filename,mode_t mode);
函数形式:int mkfifo(const char *filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。
返回值:创建成功返回0,创建失败返回-1。
例1:mkfifo的用法。
例子:通过管道实现无亲缘关系进程间通信。
三:信号
信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号。
内核可以发送多少种信号呢?
kill -l
命令:kill -9 pid
信号通信的框架
信号的发送(发送信号进程):kill raise alarm
信号的接收(接收信号进程) : pause() sleep while(1)
信号的处理(接收信号进程) : signal
1.信号的发送(发送信号进程)
kill :
所需头文件
#include <signal.h>
#include <sys/types.h>
函数原型 int kill(pid_t pid, int sig);
函数传入值 pid: 正数:要接收信号的进程的进程号
0:信号被发送到所有和pid进程在同一个进程组的进程
-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
sig:信号
0:信号被发送到所有和pid进程在同一个进程组的进程
-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
sig:信号
函数返回值 成功:0 出错:-1
kill命令的程序实现实例。
raise: 发信号给自己 == kill(getpid(), sig)
所需头文件
#include <signal.h>
#include <sys/types.h>
函数原型 int raise(int sig);
函数传入值 sig:信号
函数返回值 成功:0 出错:-1
alarm : 发送闹钟信号的函数:
alarm 与 raise 函数的比较:
相同点:让内核发送信号给当前进程
不同点:
1.alarm 只会发送SIGALARM信号
2.alarm 会让内核定时一段时间之后发送信号, raise会让内核立刻发信号
所需头文件 #include <unistd.h>
函数原型 unsigned int alarm(unsigned int seconds)
函数传入值 seconds:指定秒数
函数返回值 成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则
返回上一个闹钟时间的剩余时间,否则返回0。出错:-1
常见信号
信号名 含义 默认操作
SIGHUP 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。 终止
SIGINT 该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。 终止
SIGQUIT 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。 终止
SIGILL 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。 终止
SIGFPE 该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。 终止
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。 终止
SIGALRM 该信号当一个定时器到时的时候发出。 终止
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。 暂停进程
SIGTSTP 该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。 暂停进程
SIGCHLD 子进程改变状态时,父进程会收到这个信号 忽略
SIGABORT 该信号用于结束进程 终止
2. 信号的接收(接收信号进程)
接收信号的进程,要有什么条件:要想使接收的进程能收到信号,这个进程不能结束 :
sleep
pause:进程状态为S
while(1)
所需头文件 #include <unistd.h>
函数原型 int pause(void);
函数返回值 成功:0,出错:-1
3.信号的处理(接收信号进程)
收到信号的进程,应该怎样处理? 处理的方式:
(1)进程的默认处理方式(内核为用户进程设置的默认处理方式)
A:忽略
B:终止进程
C: 暂停
(2)自己的处理方式:
自己处理信号的方法告诉内核,这样你的进程收到了这个信号就会采用你自己的的处理方式
所需头文件 #include <signal.h>
函数原型 void (*signal(int signum, void (*handler)(int)))(int);
函数传入值 signum:指定信号
handler: SIG_IGN:忽略该信号。
SIG_DFL:采用系统默认方式处理信号。
自定义的信号处理函数指针
函数返回值 成功:设置之前的信号处理方式 出错:-1
signal 函数有二个参数,第一个参数是一个整形变量(信号值),第二个参数是一个函数指针,是我们自己写的处理函数;
这个函数的返回值是一个函数指针。
练习:有二个进程,一个server.c 一个client.c,要求server.c负责创建有名管道文件,即server要先运行,client后运行。
四:IPC通信
IPC通信(Inter-Process Communication)
三种: 共享内存、消息队列、信号灯
这个IPC对象,肯定是存在于内核中。而且用户空间的文件系统中有没有IPC的文件类型?没有。
有名管道为什么能实现无亲缘关系的进程之间的通信?
是因为用户空间有管道这种文件类型。
IPC是不是只能用于亲缘关系进程之间的通信呢?肯定不是
它是怎样实现无亲缘关系之间的通信呢?也即你是保证用户空间的二个进程对内核中的同一个IPC对象的操作。(ftok)
IPC对象的打开或创建: 类似于open的函数呢?
IPC和文件I/O函数的比较
文件I/O IPC
open Msg_get
Shm_get
Sem_get
read
write msgsnd msgrecv
shmat shmdt
semop
close msgctrl
shmctrl
semctrl
打开或创建一个共享内存对象,共享内核在内核是什么样子的?
一块缓存,变类似于用户空间的数组或malloc函数分配的空间一样
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数原型
int shmget(key_t key, int size, int shmflg);
函数参数 key:IPC_PRIVATE 或 ftok的返回值
size:共享内存区大小
shmflg:同open函数的权限位,也可以用8进制表示法
函数返回值 成功:共享内存段标识符---ID---文件描述符 出错:-1
查看IPC对象 ipcs –m -q -s
删除IPC对象 ipcrm -m id
返回值:共享内存段标识符 IPC的ID号