Linux进程间通信

本文深入探讨了进程间通信(IPC)的多种方式,包括管道、消息队列、信号量和共享内存的具体实现与应用。详细讲解了如何使用这些机制进行进程间的数据交换和同步控制,以及它们在实际编程中的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、pipe/msgqueue/sems/shm相关代码的实现及总结 

管道:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>    // 用于创建匿名管道


int main(){
    int fds[2];
    char buf[100];
    int len;

    // 创建匿名管道
    if(pipe(fds) == -1){
        printf("创建失败!\n");
        perror("make pipe");
        exit(1);
    }

    // 从标准输入中读取数据
    while(fgets(buf,100,stdin)) {
        len = strlen(buf);

        // fd[0]  表示读端   fd[1]     表示写端
        // 将数据写入到管道中
        if( write(fds[1], buf ,len ) != len) {
            perror("write to pipe");
            break;
        }
        memset(buf, 0x00, sizeof(buf));

        // 从管道中读取数据
        if((len = read(fds[0], buf, 100)) == -1) {
            perror("read from pipe");
            break;
        }

        // 往标准输出写数据
        
        if(write(1, buf, len) != len) {
            perror("write to stdout");
            break;
        }

    }
}

消息队列(msgqueue):一种带有数据标识的特殊管道,就像多条并存的管道

使用方法:

    发送者:获取消息队列ID—>将数据放入一个带有标识符的结构体中,发送消息给消息队列。

    接收者:获取消息队列ID—>将制定标识的消息读出

相关代码:

/**********************************************************
 * Author        : WangWei
 * Email         : 872408568@qq.com
 * Last modified : 2019-05-05 21:17:56
 * Filename      : fifo_1.c
 * Description   : fifo_1  通过FIFO从键盘接收数据,并发送
 * *******************************************************/
#include "fifo.h"



int main()

{
    //  int access(const char *filenpath, int mode);  
    //   确定文件或文件夹的访问权限。即,检查某个文件的存取方式,比如说是只读方式、只写方式等.
    //   如果指定的存取方式有效,则函数返回0,否则函数返回-1。

    if(access(FIFO,F_OK)) {

        mkfifo(FIFO, 0644);
    }

    int fifo = open(FIFO, O_WRONLY);

    char buf[20];
    bzero(buf, 20);

    fgets(buf, 20, stdin);
    int n = write(fifo, buf, strlen(buf));   // 将数据写入FIFO

    printf("已发送:%d 字节\n",n);
    return 0;
}

/**********************************************************
 * Author        : WangWei
 * Email         : 872408568@qq.com
 * Last modified : 2019-05-05 21:34:52
 * Filename      : fifo_2.c
 * Description   :  通过FIFO从fifo_1中接收数据并打印出来
 * *******************************************************/
#include "fifo.h"

int main()
{
    if(access(FIFO, F_OK)) {
        mkfifo(FIFO, 0644);
    }

    int fifo = open(FIFO, O_RDONLY);   // 以制度方式打开管道

    char buf[20];
    bzero(buf, 20);

    read(fifo, buf, 20);

    printf("新消息: %s \n", buf);
    return 0;
}

信号量(sems):用来卸掉各进程或线程间工作的。

临界资源:多个进程或现场可能同时访问的资源,访问这些资源的代码称为临界代码,临界代码所在区域被称为临界区域。

共享内存(shm):最快的进程间通信方式(少了两步用户态与内核态之间的数据拷贝过程)
    原理:开辟一块物理内存空间,将这块内存映射到进程的虚拟地址空间进行操作,若多个进程映射连接同一块物理内存,则通过这块物理内存实现进程间通信。
    
    操作步骤;
    创建共享内存(shmget)->将共享内存映射到虚拟地址空间(shmat)——>直接通过虚拟地址进行内存操作(memcpy)——>解除映射关系(shmdt)——>删除共享内存(shmctl)
    共享内存生命周期随内核

 

相关代码:

发送端:

#include "comm.h"
#include <signal.h>

int shmid;

void rmid(int sig) {

    // 信号来了就删除SHM
    shmctl(shmid, IPC_RMID, NULL);   // shmctl 获取/设置共享内存相关属性

}


int main()
{
    signal(SIGINT, rmid);

    key_t key = ftok(PATHNAME, PROJ_ID);
    shmid = shmget(key, SHMSZ, IPC_CREAT|0666);

    char *p = shmat(shmid, NULL, 0);
    bzero(p, SHMSZ);

    pid_t pid = getpid();    // server将自身PID放入shm的钱4个字节中
    memcpy(p, &pid, sizeof(pid_t));

    fgets(p + sizeof(pid_t), SHMSZ, stdin);   // 从键盘将数据填入SHM中
    pause();      // 等待client的信号去删除shm

    return 0;
}

接收端:

#include "comm.h"
#include <signal.h>

int shmid;

void rmid(int sig) {

    // 信号来了就删除SHM
    shmctl(shmid, IPC_RMID, NULL);   // shmctl 获取/设置共享内存相关属性

}


int main()
{
    signal(SIGINT, rmid);

    key_t key = ftok(PATHNAME, PROJ_ID);
    shmid = shmget(key, SHMSZ, IPC_CREAT|0666);

    char *p = shmat(shmid, NULL, 0);
    bzero(p, SHMSZ);

    pid_t pid = getpid();    // server将自身PID放入shm的钱4个字节中
    memcpy(p, &pid, sizeof(pid_t));

    fgets(p + sizeof(pid_t), SHMSZ, stdin);   // 从键盘将数据填入SHM中
    pause();      // 等待client的信号去删除shm

    return 0;
}

[root@Centos7 shm]# cat client.c
#include "comm.h"

int main(int argc, char **argv) {
    key_t key = ftok(PATHNAME, PROJ_ID);     // 将路径名和项目标识符转换为shm id
    int shmid = shmget(key, SHMSZ, 0666);

    char *p = shmat(shmid, NULL, 0);
    printf("from SHM: %s", p+sizeof(pid_t));

    kill(*((pid_t *) p), SIGINT);
    shmdt(p);

    return 0;
}


 2、ipcs -q/m/s与ipcrm -q/m/s的使用及总结 

ipcs命令

ipcs -a :显示全部可以显示的信息
ipcs -q:显示活动的消息队列
ipcs -m:显示活动的共享内存信息
ipcs -s:显示活动的信号量信息
ipcrm命令:

ipcrm -m id:删除共享内存标识
ipcrm -M key:删除由关键字创建的共享内存标识
ipcrm -q id :删除消息队列标识 id和其相关的消息队列和数据结构
ipcrm -Q key:删除由关键字key创建的消息队列和其相关的消息队列和数据结构
ipcs -s id:删除信号标识符id和其相关的信号量集及数据结构
ipcs -S key:删除由关键字key创建的信号量标识及其相关的信号量集及数据结构


 3、将二元信号量P/V操作,封装成动态/静态库,并分别使用并测试 

P操作:程序进入临界区申请资源的动作。

V 操作:程序离开临界区释放资源的动作。

动态库: 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

静态库:静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

生成静态库:
① gcc -c  xxx.c -o xxx.o
② ar -rc libxxx.a xxx.o xxx.o
③ gcc main.c libxxx.a
//gcc main.c -lxxx
(标准版测试运行:gcc main.c -lxxx -L .)

动态库 (共享库):
生成动态库:
gcc  -fPIC -shared -o libxxx.so xxx.c
(其中 -fPIC 是未知无关)。


 4、同步与互斥概念原理,生产者消费者原理。 

进程同步

    进程同步也是进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作。

    比如说进程A需要从缓冲区读取进程B产生的信息,当缓冲区为空时,进程B因为读取不到信息而被阻塞。而当进程A产生信息放入缓冲区时,进程B才会被唤醒。概念如图所示。

    1

    

 

进程互斥

    进程互斥是进程之间的间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待。只有当使用临界资源的进程退出临界区后,这个进程才会解除阻塞状态。

    比如进程B需要访问打印机,但此时进程A占有了打印机,进程B会被阻塞,直到进程A释放了打印机资源,进程B才可以继续执行。概念如图所示。

    3

    

生产者消费者原理:

 

 问题描述:生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。本作业要求设计在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来

 

    这里生产者和消费者是既同步又互斥的关系,首先只有生产者生产了,消费着才能消费,这里是同步的关系。但他们对于临界区的访问又是互斥的关系。因此需要三个信号量empty和full用于同步缓冲区,而mut变量用于在访问缓冲区时是互斥的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值