进程间通讯(管道,信号量,共享内存,消息队列)

命令 ipcs可以查看所有的管道,共享内存和消息队列

管道

管道中的数据存储在系统的内存中,而不是存储在磁盘上,因此我们在查看管道文件大小时,大小永远是0.

有名管道

有名管道可以在任意两个进程间通讯。

创建有名管道:mkfifo+管道名

例如a程序给有名管道fifo写入数据,再b程序从有名管道fifo中读取数据:

代码:

a.c:

b.c:

运行结果:

无名管道

无名管道只能在父子进程间通讯(先创建管道再fork,这样子进程就有和父进程一样的文件描述符)

创建无名管道:int pipe(int pipefd[2])

其中 pipefd[0]:只能读的文件描述符

pipefd[1]:只能写的文件描述符

举例:父进程给无名管道写入数据,子进程通过无名管道读数据:

结果:

信号量

就像生活中的红绿灯,当一方为绿灯时此方可以通行,而另一方需要等待,等红灯变成绿灯方可通过。信号量是一种特殊的变量,对信号量的操作都是原子操作,信号量-1意味着使用资源(p操作),信号量+1意味着释放资源(v操作)。

举例:两个进程访问同一块资源(A,B程序使用同一个打印机)

解决方法:使用信号量(当前程序访问信号量为1时,才可以进行打印;为0时需要等待)

使用信号量同步进程

所需头文件:

#include<sys/sem.h>

所需接口函数:

int semget( key_t key,int nsems,int semflg ):用于创建信号量,或者获取别人已经创建好的信号量

key:需要创建或者已有信号量的密码key值,例如(key_t)1234等

nsems:需要创建信号量的个数

semflg:标志位

IPC_CREAT | IPC_EXCL | 0600: 全新创建一个信号量(可读可写),如果此信号量已存在,就失败返回-1

返回值:信号量的id

semop( int semid,struct sembuf* sops,int nsops ):用于对信号量进行原子操作

semid:信号量id

sops:结构体指针

struct sembuf

{

unsigned short sem_num; //对哪个信号量进行操作(如果只有一个信号量就写0,也就是信号的下标)

short sem_op; //设置为-1时,进行p操作;为1时,进行v操作

short sem_flg; //标志位 SEM_UNDO

};

nsops:操作信号量的个数

semctl( int semid,int semnum,int cmd, ... ):对信号量的状态进行设置(初始化,移除信号量等操作)

semid:信号量id

semnum:对哪个信号量进行操作(如果只有一个信号量,就写0,也就是信号量的下标)

cmd:设置信号量

SETVAL:设置信号量

IPC_RMID:移除整个信号量集合

最后还需要自己定义一个联合体作为参数传入

union semun

{

int val; //信号量的初始值

};

代码实现:

sem.h:

#include<sys/sem.h>
#include<unistd.h>

union semun
{
    int val; //信号量初始值
};

void sem_init();   //初始化信号量

void sem_p();      //p操作

void sem_v();      //v操作

void sem_destory();  //销毁信号量

sem.c:

#include"sem.h"

static int semid = -1; //信号量id

void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT | IPC_EXCL | 0600);
    if(semid == -1)   //信号量已经存在
    {
        semid = semget((key_t)1234,1,0600);
        if(semid == -1)  //创建失败
        {
            perror("semget err\n");
        }
    }
    else  //全新创建的信号量
    {
        union semun a;
        a.val = 1;
        if(semctl(semid,0,SETVAL,a) == -1)
        {
            perror("semctl err\n");
        }
    }
}
void sem_p()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1) == -1)
    {
        perror("p err\n");
    }
}

void sem_v()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = SEM_UNDO;
    if(semop(semid,&buf,1) == -1)
    {
        perror("v err\n");
     }
}

void sem_destory()
{
    if(semctl(semid,0,IPC_RMID))
    {
        perror("destory err\n");
    }
}

A.c:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include"sem.h"
int main()
{
    sem_init();   //创建信号量
    int i = 0;
    for(i;i<5;i++)
    {
        sem_p();      //p操作

        printf("A");
       fflush(stdout);
        int n = rand()%3;
        sleep(n);
        printf("A");
        fflush(stdout);

        sem_v();     //v操作
        n = rand()%3;
        sleep(n);
    }
    sleep(10);    //不知道哪个进程先结束,因此让此进程后结束再销毁信号量
    sem_destory(); //销毁信号量
    return 0;
}

B.c:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include"sem.h"

int main()
{
    sem_init();   //创建信号量
    int i = 0;
    for(i;i<5;i++)
    {
        sem_p();      //p操作

        printf("B");
        fflush(stdout);
        int n = rand()%3;
        sleep(n);
        printf("B");
        fflush(stdout);

        sem_v();     //v操作
        n = rand()%3;
        sleep(n);
    }
    return 0;
}

运行结果:

共享内存

概念:

将物理内存的一块空间映射到不同进程的逻辑地址空间中,此时A进程向共享内存中写入数据,进程B可以直接使用这个数据。

所需头文件:

#include<sys/shm.h>

所需接口函数:

int shmget( key_t key,size_t size,int shmflg ): 创建或获取已有的共享内存

key:不同进程可以使用相同key(例如(key_t)1234)值获取同一块共享内存

size:申请共享内存的大小

shmflg:标志位

IPC_CREAT | 0600:创建共享内存(可读可写)

返回值:共享内存id

void* shmat( int shmid,const void* shmaddr,int shmflg ):将共享内存映射到进程的逻辑地址空间

shmid:共享内存id

shmaddr:一般给NULL

shmflg:一般给0(可读可写)

返回值:共享内存的地址

int shmdt( const void* shmaddr ):断开当前进程与共享内存的映射

shmaddr:共享内存的地址

举例:a程序向共享内存中写入数据hello,b程序从共享内存中读出数据

代码实现:

a.c:

b.c:

运行结果:

消息队列

所需头文件:

#include<sys/msg.h>

所需接口函数:

int msgget( key_t key,int msgflg ):创建或者获取已有的消息队列id

key:多个进程可以通过相同的key值(例如(key_t)1234)访问同一消息队列

msgflg:标志位。

IPC_CREAT | 0600:创建消息队列(可读可写)

返回值:消息队列id

int msgsnd( int msgid,const void* msqp,size_t size,int msgflg ):向消息队列中发送数据

msgid:消息队列id

msqp:结构体指针

struct msgbuf //需要自己定义

{

long mtype; //第一个元素必须是long类型

char mtext[]; //第二个元素是存放数据的类型(char*,int*,结构体...)

}

msgflg:标志位,一般写0

int msgrcv( int msgid,void* msqp,size_t size,long msqtyp,int msgflg ):从消息队列中接收数据

msgid:消息队列id

msqp:存放信息的结构体指针

size:结构体中存放信息的大小(不是结构体的大小)

msgtyp:消息类型(写0的话就是可以接收所有类型的数据)

msgflg:标志位,一般写0

举例:进程A向消息队列中发送不同类型数据,进程B从消息队列中接收不同类型数据

代码实现:

A.c:

B.c:

运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦的白鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值