进程间通信

🔥为什么无法直接通信?
每个进程都有自己的虚拟地址空间,访问的都是虚拟地址,进程间具有独立性,无法直接通信。

进程间通信的原理: 系统为进程提供公共传输媒介实现公共访问进而实现通信。
1.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
2.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4.资源共享:多个进程之间共享同样的资源。为了做到这一点,需要内核提供锁和同步机制
5.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

管道(Pipe)

原理:
1.管道是一种通过操作系统提供的内存缓冲区,在内核中实现的通信机制。
2.管道的读写操作通过文件描述符进行,数据从管道的写入端被写入内核缓冲区,然后由管道的读取端读取出来。

特性:
1.半双工通信(可选择性单向通信)。
2.提供字节流传输服务:有序的,基于连接的,可靠的传输方式。
3.自带同步与互斥。
4.生命周期随进程。

读写特性:
1.管道中无数据,read会阻塞;管道中数据已满,write会阻塞;
2.若管道的所有读端被关闭,则继续write就会触发异常,导致进程退出;
3.若管道的所有写端被关闭,read读完所有数据后,则不再阻塞,返回0;

同步: 是指散布在不同进程之间的若干程序片段,它们的运行必须严格按照一定的先后次序来运行,这种次序依赖于要完成的任务。 比如:数据的接收和发送,必须发送方发送了接收方才能接收。
互斥:是指散布在不同进程之间的若干程序片段,当某个进程执行其中的一个程序片段时,其他进程就不能运行它们之中的任一程序片段,只能等到该进程运行完之后才可以继续运行。

🔥同步与互斥的实质:
1.互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法控制对资源的访问顺序。
2.同步是指在互斥的基础上实现对资源的有序访问。

API:

//头文件
#include <unistd.h>
# 创建一个匿名管道。
int pipe(fd[2]);
参数:
fd:一个包含两个文件描述符的整数数组,其中 fd[0] 用于读取,fd[1] 用于写入。只能用于具有亲缘关系的进程间通信。
返回值:
成功:返回 0
失败:返回-1

匿名管道特点:
1.半双工
2.具有亲缘关系可建立
3.存于内存

//查看管道的命令:
ps -ef|grep pipe

匿名管道示例如下:

#include <iostream>
#include <unistd.h>
#include <cstring>

int main() {
    int pipe_fd[2];  // 用于存放管道的文件描述符

    // 创建管道
    if (pipe(pipe_fd) == -1) {
        std::cerr << "创建管道失败" << std::endl;
        return 1;
    }

    pid_t pid = fork();  // 创建子进程

    if (pid == -1) {
        std::cerr << "创建子进程失败" << std::endl;
        return 1;
    } else if (pid == 0) {  // 子进程
        close(pipe_fd[1]);  // 关闭写入端

        char buffer[100];
        read(pipe_fd[0], buffer, sizeof(buffer));  // 从管道读取数据
        std::cout << "子进程读取到数据: " << buffer << std::endl;

        close(pipe_fd[0]);  // 关闭读取端
    } else {  // 父进程
        close(pipe_fd[0]);  // 关闭读取端

        const char* message = "Hello, Pipe!";
        write(pipe_fd[1], message, strlen(message) + 1);  // 向管道写入数据

        close(pipe_fd[1]);  // 关闭写入端
    }

    return 0;
}
//头文件:#include <unistd.h>
# 创建一个命名管道。
int mkfifo(const char* path, mode_t mode);
参数:
path:管道的路径名。
mode:管道的权限。
返回值:
成功:返回0
失败:返回-1, 并设置错误

命名管道的特点:
1.无关进程之间的数据传输。
2.以路径名为关联,特殊的文件形式存于文件系统中。

共享内存

原理:开辟一块物理内存空间,各个进程将同一块物理内存空间映射在自己的虚拟地址空间中,通过虚拟地址进行访问,进而实现数据共享。

特性:
1.共享内存是一种允许两个或多个进程共享同一块内存区域的机制。
2.共享内存是最快的进程间通信方式之一,因为进程可以直接访问共享内存中的数据无需进行数据的复制,提高效率。
3.信号量与共享内存通常结合使用。

  • 虚拟地址空间映射后,直接通过虚拟地址访问物理内存,相较于其他方式少了两步数据拷贝操作,效率较高。
  • 生命周期随内核。
  • 各个进程对共享内存的操作,都是不安全的操作。

API:

//头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
# 创建共享内存段,获得共享内存的标识符。
int shmget(ket_t key, size_t size, int shmflg);
key:共享内存的键值。
size:共享内存的大小。
shmflg:标志位,用于指定共享内存的权限和行为。
返回值:
成功:返回共享内存id
失败:返回-1

# 将共享内存连接到进程的地址空间。
int shmat(int shmid,const void* shmaddr, int shmflg);
shmid:共享内存的标识符。
shmaddr:指定连接的地址,通常为 NULL。
shmflg:标志位,通常为 0。
返回值:
成功:返回指向共享内存的指针
失败:返回-1

# 将共享内存从进程的地址空间分离。(断开连接)
int shmdt(const void* shmaddr);
shmaddr:共享内存连接的地址。
返回值:
成功:返回 0
失败:返回-1

# 共享内存控制操作。
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
shmid:共享内存的标识符。
cmd:控制命令。
buf:结构体指针,用于传递或接收信息。
返回值:
成功:返回 0
失败:返回-1

一个共享内存+信号量使用的示例:

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <cstring>

union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};

// 等待信号量
void semaphore_wait(int sem_id) {
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = -1;
    semaphore.sem_flg = SEM_UNDO;
    semop(sem_id, &semaphore, 1);
}

// 发送信号量
void semaphore_signal(int sem_id) {
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = 1;
    semaphore.sem_flg = SEM_UNDO;
    semop(sem_id, &semaphore, 1);
}

int main() {
    key_t sem_key = ftok("semaphore_key", 65);
    key_t shm_key = ftok("shared_memory_key", 66);

    // 创建信号量
    int sem_id = semget(sem_key, 1, 0666 | IPC_CREAT);
    union semun arg;
    arg.val = 1;
    semctl(sem_id, 0, SETVAL, arg);

    // 创建共享内存
    int shmid = shmget(shm_key, 1024, 0666|IPC_CREAT);
    char* shared_memory = (char*) shmat(shmid, (void*)0, 0);

    pid_t pid = fork();

    if (pid == 0) {  // 子进程
        semaphore_wait(sem_id);  // 等待信号量
        std::cout << "子进程读取到数据: " << shared_memory << std::endl;
        semaphore_signal(sem_id);  // 发送信号量
    } else {  // 父进程
        semaphore_wait(sem_id);  // 等待信号量
        strcpy(shared_memory, "Hello, Shared Memory!");  // 写入共享内存
        std::cout << "父进程写入数据: " << shared_memory << std::endl;
        semaphore_signal(sem_id);  // 发送信号量
        wait(NULL);  // 等待子进程执行完毕
        shmdt(shared_memory);  // 分离共享内存
        shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存
        semctl(sem_id, 0, IPC_RMID);  // 删除信号量
    }

    return 0;
}

消息队列

原理:是进程间通信(IPC)机制之一,用于在不同的进程之间传输数据。它是一个存储在内核中的消息链表,用于暂存进程间传输的消息。消息队列是一个先进先出(FIFO)的数据结构。

特点:
1.面向记录,消息具有特定格式和优先级
2.独立于收,发进程。进程终止,消息队列内容不会被删除
3.实现消息的随机查询,不一定按顺序,也可以是按类型查询

API:

//头文件
#include <sys/ipc.h>
#include <sys/msg.h>
# 获取消息队列的标识符。
int msgget(ket_t key, int msgflg);
key:消息队列的键值。
msgflg:标志位,用于指定消息队列的权限和行为。
返回值:
成功:返回队列id  
失败:返回-1

# 向消息队列发送消息。
int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg);
msqid:消息队列的标识符。
msgp:指向消息的指针。
msgsz:消息的大小。
msgflg:标志位,用于指定发送消息的行为。
返回值:
成功:返回 0
失败:返回-1

# 从消息队列接收消息。
int msgrcv(int msqid, void* msgp, size_tmsgsz, long msgtyp, int msgflg);
返回值:
msqid:消息队列的标识符。
msgp:指向接收消息的缓冲区。
msgsz:缓冲区的大小。
msgtyp:消息类型,通常为 0,表示接收队列中的第一条消息。
msgflg:标志位,用于指定接收消息的行为。
成功:返回消息数据长度
失败:返回-1

信号量

原理: 是一种经典的进程间同步和互斥机制,用于解决多个进程或线程在访问共享资源时的并发控制问题。信号量维护了一个计数器和一个等待队列,其中计数器的值代表可用资源的数量。

特点:
1.原子操作:信号量的操作是原子的,确保了进程间的互斥访问。
2.计数器:信号量维护一个计数器,代表可用的资源数量。
3.等待队列:当信号量的值为0时,进入等待状态的进程或线程会被阻塞,直到信号量的值变为大于0。
4.P/V操作:通过semop()对信号量进行P/V操作,如增加或减少计数值。信号量可以用于解决进程间的竞争条件和临界区问题,保证共享资源的正确访问顺序。

API:

# 获取信号量集合的标识符。
int semget(key_t key, int nsems, int semflg);
key:信号量集合的键值。
nsems:信号量集合中信号量的数量。
semflg:标志位,用于指定信号量集合的权限和行为。
返回值:
成功:返回一个新的信号量集合的标识符。
失败:返回-1,并设置errno以指示错误类型。

# 对信号量进行操作。
int semop(int semid, void * sops, size_t nsops);
semid:信号量集合的标识符。
sops:指向包含操作的结构体数组的指针。
nsops:操作的数量。
返回值:
成功:返回 0。
失败:返回-1,并设置errno以指示错误类型。

# 信号量控制操作。
int semctl(int semid, int semnum, int cmd, arg);
semid:信号量集合的标识符。
semnum:信号量的索引。
cmd:控制命令。
arg:结构体指针,用于传递或接收信息。
返回值:
成功:
GETVAL:返回信号量的当前值。
GETPID:返回最后执行semop()操作的进程ID。
GETNCNT:返回等待该信号量值增加的进程数量。
GETZCNT:返回等待该信号量值变为0的进程数量。
SETVAL、SETALL:返回 0。
IPC_RMID、IPC_SET、IPC_STAT:返回 0。
失败:返回-1,并设置errno以指示错误类型。

好了,欢迎大家来评论,共同进步!

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值