什么是多进程?
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的(静态的),进程是活的(动态的)。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;用户进程就不必我多讲了吧,所有由你启动的进程都是用户进程。进程是操作系统进行资源分配的单位。
在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。
常见的通信方式
管道pipe
管道允许在进程之间按先进先出的方式传送数据,是进程间通信的一种常见方式。
匿名管道特点:
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。
- 匿名管道只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
- 管道是基于字节流来通信的。
- 依赖于文件系统,它的生命周期随进程的结束结束(随进程)。
- 其本身自带同步互斥效果。
匿名管道实现:
int pipe(int pipefd[2]);
pid_t fork(void);
命名管道FIFO
上述管道虽然实现了进程间通信,但是它具有一定的局限性:首先,这个管道只能是具有血缘关系的进程之间通信;第二,它只能实现一个进程写另一个进程读,而如果需要两者同时进行时,就得重新打开一个管道。为了使任意两个进程之间能够通信,就提出了命名管道(named pipe 或 FIFO)。
命名管道特点:
与管道的区别是它提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。
命名管道实现:
#include <sys/stat.h>
int mknod(const char* path, mode_t mod, dev_t dev);
int mkfifo(const char* path, mode_t mod);
注释:这两个函数都能创建一个FIFO文件,该文件是真实存在于文件系统中的。函数mknod中参数path为创建命名管道的全路径;mod 为创建命名管道的模式,指的是其存取权限;dev为设备值,改值取决于文件创建的种类,它只在创建设备文件是才会用到。
返回值:这两个函数都是成功返回0,失败返回-1。
消息队列MessageQueue
消息队列的特点(与管道对比):
- 匿名管道是跟随进程的,消息队列是跟随内核的,也就是说进程结束之后,匿名管道就死了,但是消息队列还会存在(除非显示调用函数销毁)。
- 管道是文件,存放在磁盘上,访问速度慢,消息队列是数据结构,存放在内存,访问速度快。
- 管道是数据流式存取,消息队列是数据块式存取。
进程AB利用消息队列通信,A创建消息队列;B用同样的key创建消息队列;然后它们就可以利用消息队列进行通信了。
创建消息队列:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
共享存储SharedMemory
为什么用共享存储区进行通信?因为快!管道是文件,操作慢,消息队列创建操作都有消耗所以慢,共享内存是要创建好两个进程都可以直接对这块内存进行操作,互相都是可见的。
为什么用共享存储区编写程序?因为接口简单!操作绝对比消息队列简单好多。
信号量Semaphore
信号量多用于进程间的同步和互斥。
信号量的工作机制,它可以直接理解成计数器,信号量会有初值(>0),每当有进程申请使用信号量,通过一个P操作来对信号量进行-1操作,当计数器减到0的时候就说明没有资源了,其他进程要想访问就必须等待(比如忙等待或者睡眠),当该进程执行完这段工作(我们称之为临界区)之后,就会执行V操作来对信号量进行+1操作。
进程AB利用信号量通信,A创建信号量、初始化信号量;B用同样的key创建信号量;然后它们就可以利用信号量进行通信了。
信号量的创建:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
信号量的初始化:
int semget(key_t key, int nsems, int semflg);
信号量操作:P、V操作通过一个函数实现
int semop(int semid, struct sembuf *sops, unsigned nsops);
信号Signal
信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行并传递给它为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
Linux提供了几十种信号,分别代表着不同的意义。信号之间依靠他们的值来区分,但是通常在程序中使用信号的名字来表示一个信号。通常程序中直接包含<signal.h>就好。
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式,信号可以在用户空间进程和内核之间直接交互。内核也可以利用信号来通知用户空间的进程。
信号的来源:
- 硬件来源,例如按下了cltr+C,通常产生中断信号sigint。
- 软件来源,例如使用系统调用或者命令发出信号。最常用的发送信号的系统函数是kill,raise,setitimer,sigation,sigqueue函数。软件来源还包括一些非法运算等操作。
一旦有信号产生,用户进程对信号产生的相应有三种方式:
- 执行默认操作,linux对每种信号都规定了默认操作。
- 捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。
- 忽略信号,当不希望接收到的信号对进程的执行产生影响,而让进程继续执行时,可以忽略该信号,即不对信号进程作任何处理。
- 但是有两个信号SIGKILL和SEGSTOP是应用进程无法捕捉和忽略的,这是为了使系统管理员能在任何时候中断或结束某一特定的进程。
套接字Socket
网络编程。