brophp框架做网站sem优化托管公司
每个进程的用户空间都是独立的,不能相互访问。
所有进程的内核空间(32位系统3G-4G)都是共享的
应用场景
- 作为缓冲区,处理速度不同的进程之间的数据传输
- 资源共享:多个进程之间共享同样的资源,一个进程对共享数据的修改,别的进程应该立刻看到
- 数据传输: 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
一、管道
缺点:缓存区在内核中大小受限,只能承载无格式字节流;单向通信方式,低效,不适合频繁交换数据
生命周期随着进程创建而建立,随着进程终止而消失
遵循先进先出原则,不支持 lseek 之类的文件定位操作
需要双方通信时,需要建立起两个管道
读端不读或者读的慢,写端要等待读端;
读端关闭,写端收到SIGPIPE信号直接终止;
写端不写或者写的慢,读端要等待写端;
写端关闭,读端读完pipe内部的数据然后再读,会读到0为止,表明读到文件结尾;
匿名
管道 pipe
特殊文件只存在于内存,没有存在于文件系统中
只能用于有亲缘关系的进程间
shell 命令中的竖线就是匿名管道
pipe() 和 fork() 搭配
有名
管道 fifo
mkfifo myfifo #在文件系统中创建一个类型为 p 的设备文件
int mkfifo(const char *pathname, mode_t mode); //默认在当前的路径下创建
mkfifo("myfifo", 0666);
open("myfifo", O_RDONLY);
open(MY_FIFO, O_WRONLY);
二、System V 进程间通信(操作系统层面上进程间通信方案)
消息队列
和共享内存
是以传输数据为目的
信号量
是为了保证进程间的同步与互斥而设计的
-
消息队列
是消息的链表,存放在内核中并由消息队列标识符标识,
缺点:通信不及时;有大小限制,在内核中一条消息最大长度MSGMAX 和一个队列的最大长度MSGMNB(以字节为单位),
不适合比较大数据的传输;用户态与内核态之间的数据拷贝开销 -
共享内存
–最快的IPC
映射一段能被其它进程访问的内存,这段共享内存由一个进程创建,多个进程均能访问
带来新的问题:多进程间竞争共享资源,常与信号量
配合使用
生命周期是随内核的,进程要主动删除其创建的共享内存:ipcrm -m {shmid}
共享内存块的大小都必须是系统页面大小的整数倍。系统页面大小指的是系统中单个内存页面包含的字节数。在 Linux 系统中,内存页面大小是4KB,不过您仍然应该通过调用 getpagesize 获取这个值(通过man 2 getpagesize查看 )
#include <sys/ipc.h>
#include <sys/shm.h> // shmget
#include <sys/types.h> // key_t
// key_t ftok(const char *pathname, int proj_id); //获取key值
// int shmget(key_t key, size_t size, int shmflg); //得到共享内存标识符,不存在时创建并返回
// void* shmat(int shmid, const void *shmaddr, int shmflg); //共享内存绑定到当前进程的地址空间中
// int shmdt(const void *shmaddr); //取消共享内存与进程地址空间之间的关联
// int shmctl(int shmid, int cmd, struct shmid_ds *buf); //cmd表示具体控制动作:IPC_STAT,IPC_SET改变状态,IPC_RMID 删除key_t key = ftok(PATH_NAME, PROJ_ID);//获取key值
shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL); //操作系统给用户返回的id// ipcs命令查看有关进程间通信设施的信息
// -q:列出消息队列相关信息。
// -m:列出共享内存相关信息。
// -s:列出信号量相关信息
信号量
一个整型的计数器表示资源的数量。两原子操作
P 操作: 把信号量 -1。相减后信号量 >= 0,表明还有资源可使用,进程可正常继续执行,否则进程需阻塞等待
V 操作: 把信号量 +1。相减后信号量 > 0,表明当前没有阻塞中的进程;否则表明当前有阻塞中的进程,将该进程唤醒运行
信号:用于通知接收进程某个事件已经发生,唯一的异步通信机制。缺点:传递信息少
信号事件来源主要: 硬件来源(如键盘 Cltr+C )和 软件来源(如 kill 命令)
可以在任何时候发送信号给某一进程,一旦有信号产生,进程就对信号处理(执行默认操作,定义一个信号处理函数,忽略信号)
# Ctrl+C 产生 SIGINT 信号,表示终止该进程;
# SIGTERM 终止该进程;通过 Core Dump 将当前进程的运行状态保存在文件里面,方便事后分析问题在哪里
# Ctrl+Z 产生 SIGTSTP 信号,表示停止该进程,但还未结束;
kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) 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
套接字
:3个属性:域、类型、协议。唯一的跨网络主机的通信机制
// domain: AF_INET 用于 IPV4、
// AF_INET6 用于 IPV6
// AF_LOCAL/AF_UNIX 二者等价,用于本机
// type: SOCK_STREAM 字节流,对应 TCP
// SOCK_DGRAM 数据报,对应 UDP
// SOCK_RAW 原始套接字
int socket(int domain, int type, int protocal);
TCP 服务端负责监听的 socket 叫作监听 socket ==> listen_fd
调用 accept 时,连接成功,会返回一个已完成连接的 socket,叫作已完成连接 socket ==> sock_fd,用来后续传输数据。
是「两个」 不同 socket
UDP 是没有连接的,所以不需要三次握手。不存在客户端和服务端的概念
每次通信时,调用 sendto 和 recvfrom,都要传入目标主机的 IP 地址和端口
本地 socket 的实现效率大大高于 IPv4 和 IPv6 的字节流、数据报 socket 实现
不像 TCP 和 UDP 要绑定 IP 地址和端口,而是绑定一个本地文件
三、POSIX 进程间通信:共享内存、消息队列、信号量、
参考:https://www.cnblogs.com/tongh/p/18002994
Linux内核v2.6.6和glibc2.3.4之后的版本支持POSIX消息队列 (ldd --version)
POSIX:可移植操作系统接口,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945
标准线上地址:注册后可以在线阅读或者下载。
IEEE和Open Group 的POSIX认证
相关页面
20世纪80年代中期,Unix厂商试图通过加入新的、往往不兼容的特性来使它们的程序与众不同。局面非常混乱,麻烦也就随之而来了。为了提高兼容性和应用程序的可移植性,阻止这种趋势, IEEE(电气和电子工程师协会)开始努力标准化Unix的开发,后来由 Richard Stallman命名为“Posix”。
这套标准涵盖了很多方面,比如Unix系统调用的C语言接口、shell程序和工具、线程及网络编程
有了这个规范,就可以调用通用的API
消息队列
-
优点
灵活,支持多种数据类型和优先级。
可靠,消息持久化且不会因发送者崩溃而丢失。
高效,支持大量数据的传输和并行处理 -
缺点
数据复制的开销。
需要管理访问权限以确保安全性。
-
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>mqd_t mq_open(const char *name, int oflag, ...); //打开一个已存在的消息队列,或创建一个新的消息队列
//发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);//接收消息
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout);//设置/获取消息队列的属性
int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat);int mq_close(mqd_t mqdes); //关闭
int mq_unlink(const char *name); //删除一个命名的消息队列。
共享内存
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */int shm_open(const char *name, int oflag, mode_t mode); //创建或打开一个POSIX共享内存对象
int ftruncate(int fd, off_t length); //设置大小
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); //映射到进程的地址空间
int munmap(void *addr, size_t length); //解除映射
int shm_unlink(const char *name); //删除共享内存对象
信号量
有名信号量:操作系统内核维护的具有全局唯一名字的信号量
无名信号量是一种不具有全局唯一名字的信号量,通常只能在相关的进程或线程之间进行共享
只有0和1两种取值的信号量叫做二进制信号量
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); //创建或打开有名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value); //初始化无名信号量int sem_getvalue(sem_t *sem, int *sval); //获取当前信号量的值
int sem_post(sem_t *sem); //V操作:信号量 +1
int sem_wait(sem_t *sem); //等待信号量减小。如果信号量的值大于0,则将其减小1并立即返回,否则会阻塞当前线程直到信号量变为大于0为止
int sem_trywait(sem_t *sem); //尝试等待信号量减小的非阻塞版本。如果信号量的值大于0,则将其减小1并立即返回,否则会立即返回,并且不会阻塞当前线程
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); //函数允许设置一个超时时间,如果在指定的时间内未能获得信号量,函数将返回一个特定的错误码int sem_close(sem_t *sem); //关闭有名信号量
int sem_destroy(sem_t *sem); //销毁无名信号量
int sem_unlink(const char *name); //从系统中删除有名信号量