共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是我们必须用其它辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。
linux共享内存的API都定义在sys/shm.h中,包括4个系统调用:shmget , shmat , shmdt , shmctl 。
linux还提供了另一种利用mmap在无关进程之间共享内存的方式。这种方式无须任务文件的支持,但它需要先使用shm_open函数来创建或打开一个POSIX共享内存对象。
一、shm_open
name参数指定要创建或打开的共享内存对象。从可移植性的角度考虑,该参数应该使用“/name”的格式:以”/“开始,后接多个字符,并且这些字符不是“/”;以“\0”结尾,长度不超过NAME_MAX,通常是255。
oflag参数指定创建方式,可以是一个或多个按位或。
O_RDONLY 以只读方式
O_RDWR 可读,可写
O_CREAT 如果共享内存对象不存在,则创建之。此时mode参数的最低9位将指定该共享内存对象的访问权限。创建时初始长度为0。
O_EXCL 和 O_CREAT一起使用,如果由name指定的共享内存对象已经存在,则shm_open调用返回错误,否则就创建一个新的共享内存对象。
O_TRUNC 如果共享内存对象已存在,则把它截断,使其长度为0 。
该函数将name参数指定的共享内存对象标记为等待删除。当所有使用共享内存对象的进程都使用ummap将它从进程中分离后,系统将销毁这个共享内存对象所占据的资源。
三、 下面我们修改之前的聊天室服务端,改为多进程:一个子进程处理一个客户端,所有客户socket连接的读缓冲设计为一块共享内存,使用POSIX共享内存函数实现,编译时要指定链接选项 -lrt
linux共享内存的API都定义在sys/shm.h中,包括4个系统调用:shmget , shmat , shmdt , shmctl 。
linux还提供了另一种利用mmap在无关进程之间共享内存的方式。这种方式无须任务文件的支持,但它需要先使用shm_open函数来创建或打开一个POSIX共享内存对象。
一、shm_open
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char *name, int oflag, mode_t mode);
使用方法与open系统调用完全相同。
name参数指定要创建或打开的共享内存对象。从可移植性的角度考虑,该参数应该使用“/name”的格式:以”/“开始,后接多个字符,并且这些字符不是“/”;以“\0”结尾,长度不超过NAME_MAX,通常是255。
oflag参数指定创建方式,可以是一个或多个按位或。
O_RDONLY 以只读方式
O_RDWR 可读,可写
O_CREAT 如果共享内存对象不存在,则创建之。此时mode参数的最低9位将指定该共享内存对象的访问权限。创建时初始长度为0。
O_EXCL 和 O_CREAT一起使用,如果由name指定的共享内存对象已经存在,则shm_open调用返回错误,否则就创建一个新的共享内存对象。
O_TRUNC 如果共享内存对象已存在,则把它截断,使其长度为0 。
调用成功时返回一个文件描述符。该描述符可用于后续的mmap调用,从而将共享内存关联到调用进程。
失败返回-1并设置errno
二、shm_unlink
和打开文件最后需要关闭一样,由shm_open创建的共享内存地象用完之后也需要被删除。
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_unlink(const char *name);
该函数将name参数指定的共享内存对象标记为等待删除。当所有使用共享内存对象的进程都使用ummap将它从进程中分离后,系统将销毁这个共享内存对象所占据的资源。
三、 下面我们修改之前的聊天室服务端,改为多进程:一个子进程处理一个客户端,所有客户socket连接的读缓冲设计为一块共享内存,使用POSIX共享内存函数实现,编译时要指定链接选项 -lrt
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#define USER_LIMIT 5
#define BUFFER_SIZE 1024
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define PROCESS_LIMIT 65535
//客户端数据
struct client_data
{
sockaddr_in address; //客户端地址
int connfd; //客户端socket
pid_t pid; //处理此连接的子进程PID
int pipefd[2]; //和父进程通信的管道
};
static const char *shm_name = "/my_shm"; //共享内存对象
int sig_pipefd[2]; //处理信号的管道
int epollfd;
int listenfd;
int shmfd; //共享内存的文件描述符,用于mmap调用
char *share_mem = 0; //共享内存的指针
int *sub_process = 0; //子进程和客户连接的映射关系数组,用PID索引,值为客户连接编号
int user_count = 0; //当前客户连接数量
bool stop_child = false;
client_data * users = 0; //客户连接数组
//处理错误
int do_error(int *error, int fd = -1);
//设置非阻塞
int setnonblocking(int fd);
//添加描述符事件
void addfd(int epollfd, int fd);
//信号事件
void sig_handler(int sig);
//添加信号事件
void addsig(int sig, void(*handler)(int), bool restart = true);
//删除描述符,指针,数组
void del_resource();
//子进程的信号事件
void child_term_handler(int sig);
//子进程的执行函数
int run_child(int indx, client_dat