操作系统学习提升篇——进程同步

进程的线程共享进程资源,进程共享计算机资源,因此进程和线程一样都需要信息同步。

共享内存

在某种程度上,多进程是共同使用物理内存的;
由于操作系统的进程管理,进程间的内存空间是独立的;
进程默认是不能访问进程空间之外的内存空间的(一个进程不能访问另一个进程的内存空间)。共享内存可以打破限制。

共享存储允许不相关的进程访问同一片物理内存。实现原理:把这一片物理内存分别映射到不同进程的页表里面去,使得不同进程都能通过页表来访问同一个物理内存。

共享内存是两个进程之间共享和传递数据最快的方式。
共享内存未提供同步机制,需要借助其他机制管理访问。
共享内存是高性能后台开发中最常用的进程同步方式。

实现共享内存的四个步骤:
在这里插入图片描述

共享内存的例子:实现客户端和服务端的通信
在这里插入图片描述

//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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44776894/article/details/107393393