进程的线程共享进程资源,进程共享计算机资源,因此进程和线程一样都需要信息同步。
共享内存
在某种程度上,多进程是共同使用物理内存的;
由于操作系统的进程管理,进程间的内存空间是独立的;
进程默认是不能访问进程空间之外的内存空间的(一个进程不能访问另一个进程的内存空间)。共享内存可以打破限制。
共享存储允许不相关的进程访问同一片物理内存。实现原理:把这一片物理内存分别映射到不同进程的页表里面去,使得不同进程都能通过页表来访问同一个物理内存。
共享内存是两个进程之间共享和传递数据最快的方式。
共享内存未提供同步机制,需要借助其他机制管理访问。
共享内存是高性能后台开发中最常用的进程同步方式。
实现共享内存的四个步骤:
共享内存的例子:实现客户端和服务端的通信
//common.h文件
#ifndef __COMMON_H__
#define __COMMON_H__
#define TEXT_LEN 2048
//共享内存的数据结构
struct ShmEntry{
//是否可以读取共享内存,用于进程间同步
bool can_read;
//共享内存信息
char msg[2048];
};
#endif
//server.cpp文件
#include "common.h"
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
int main()
{
// 共享内存的结构体
struct ShmEntry *entry;
// 1. 申请共享内存
int shmid = shmget((key_t)1111, sizeof(struct ShmEntry), 0666|IPC_CREAT);
if (shmid == -1){
std::cout << "Create share memory error!" << std::endl;
return -1;
}
// 2. 连接到当前进程空间/使用共享内存
entry = (ShmEntry*)shmat(shmid, 0, 0);
entry->can_read = 0;
while (true){
if (entry->can_read == 1){
std::cout << "Received message: " << entry->msg << std::endl;
entry->can_read = 0;
}else{
std::cout << "Entry can not read. Sleep 1s." << std::endl;
sleep(1);
}
}
// 3. 脱离进程空间
shmdt(entry);
// 4. 删除共享内存
shmctl(shmid, IPC_RMID, 0);
return 0;
}
//client.cpp文件
#include "common.h"
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
int main()
{
struct ShmEntry *entry;
// 1. 申请共享内存
int shmid = shmget((key_t)1111, sizeof(struct ShmEntry), 0666|IPC_CREAT);
if (shmid == -1){
std::cout << "Create share memory error!" << std::endl;
return -1;
}
// 2. 连接到当前进程空间/使用共享内存
entry = (ShmEntry*)shmat(shmid, 0, 0);
entry->can_read = 0;
char buffer[TEXT_LEN];
while (true){
if (entry->can_read == 0){
std::cout << "Input message>>> ";
fgets(buffer, TEXT_LEN, stdin);
strncpy(entry->msg, buffer, TEXT_LEN);
std::cout << "Send message: " << entry->msg << std::endl;
entry->can_read = 1;
}
}
// 3. 脱离进程空间
shmdt(entry);
// 4. 删除共享内存
shmctl(shmid, IPC_RMID, 0);
return 0;
}
操作步骤:
打开虚拟机,用make命令编译两个cpp文件,然后分别在两个虚拟机中运行。在client文件中输入发送的消息,那么在server中可以接收到。
Unix域套接字
域套接字是一种高级的进程间通信的方法。
Unix域套接字可以用于同一机器进程间通信。
套接字(socket)原是网络通信中使用的术语。
Unix系统提供的域套接字提供了网络套接字类似的功能。
Unix域套接字不需要借助额外的同步机制管理访问。
提供了单机简单可靠的进程通信同步服务。
只能在单机使用,不能跨机器使用。
实现Unix域套接字的步骤:
服务端:
客户端:
Unix域套接字的例子:
//server.cpp文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
// 域套接字,定义域套接字路径,会自动在该路径下创建一个文件用于信息传递
#define SOCKET_PATH "./domainsocket"
#define MSG_SIZE 2048
int main()
{
int socket_fd, accept_fd;
int ret = 0;
socklen_t addr_len;
char msg[MSG_SIZE];
struct sockaddr_un server_addr;
// 1. 创建域套接字
socket_fd = socket(PF_UNIX,SOCK_STREAM,0);
if(-1 == socket_fd){
std::cout << "Socket create failed!" << std::endl;
return -1;
}
// 移除已有域套接字路径
remove(SOCKET_PATH);
// 内存区域置0
bzero(&server_addr,sizeof(server_addr));
server_addr.sun_family = PF_UNIX;
strcpy(server_addr.sun_path, SOCKET_PATH);
// 2. 绑定域套接字
std::cout << "Binding socket..." << std::endl;
ret = bind(socket_fd,(sockaddr *)&server_addr,sizeof(server_addr));
if(0 > ret){
std::cout << "Bind socket failed." << std::endl;
return -1;
}
// 3. 监听套接字
std::cout << "Listening socket..." << std::endl;
ret = listen(socket_fd, 10);
if(-1 == ret){
std::cout << "Listen failed" << std::endl;
return -1;
}
std::cout << "Waiting for new requests." << std::endl;
accept_fd = accept(socket_fd, NULL, NULL);
bzero(msg,MSG_SIZE);
while(true){
// 4. 接收&处理信息
recv(accept_fd, msg, MSG_SIZE, 0);
std::cout << "Received message from remote: " << msg <<std::endl;
}
close(accept_fd);
close(socket_fd);
return 0;
}
//client.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#define SOCKET_PATH "./domainsocket"
#define MSG_SIZE 2048
int main()
{
int socket_fd;
int ret = 0;
char msg[MSG_SIZE];
struct sockaddr_un server_addr;
// 1. 创建域套接字
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(-1 == socket_fd){
std::cout << "Socket create failed!" << std::endl;
return -1;
}
// 内存区域置0
bzero(&server_addr,sizeof(server_addr));
server_addr.sun_family = PF_UNIX;
strcpy(server_addr.sun_path, SOCKET_PATH);
// 2. 连接域套接字
ret = connect(socket_fd, (sockaddr *)&server_addr, sizeof(server_addr));
if(-1 == ret){
std::cout << "Connect socket failed" << std::endl;
return -1;
}
while(true){
std::cout << "Input message>>> ";
fgets(msg, MSG_SIZE, stdin);
// 3. 发送信息
ret = send(socket_fd, msg, MSG_SIZE, 0);
}
close(socket_fd);
return 0;
}