Linux下进程间通信(2)

进程间通信(2)


共享内存

共享内存是指多个进程看到同一块内存。
当然这块内存由操作系统维护。
需要通信的进程通过操作系统看到这块内存。

简单示意图

这里写图片描述

共享内存相关函数

shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
说明:
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

使用共享内存实现进程通信

//Makefile

.PHONY:all 
all: server client
server: server.c comm.c
    gcc -o $@ $^
client: client.c comm.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f server client
//comm.h
#ifndef _COMM_H_
#define _COMM_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#define PATH "./"
#define FTOK_ID 0x333
int createshm(int size);
int getshm(int size);

int destroyshm(int shmid);
#endif 
//comm.c
#include "comm.h"

static int comm(int size,int flag)
{
    key_t key = ftok(PATH, FTOK_ID);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    int shmid = shmget(key, size , flag);
    if(shmid < 0)
    {
        perror("shmid");
        return -2;
    }
    return shmid;
}
int createshm(int size)
{
    return comm(size, IPC_CREAT|IPC_EXCL);
}
int getshm(int size)
{
    return comm(size, IPC_CREAT);

}
int destroyshm(int shmid)
{
   return shmctl(shmid, IPC_RMID, NULL);
}
//server.c
#include "comm.h"

int main()
{
   int shmid = createshm(4096);

   char* addr = shmat(shmid, NULL, 0); 
   if(addr == NULL)
   {   
        perror("shmat");
        return 1;
   }   
   int i = 0;
   while(i < 30) 
   {   
        printf("%s\n", addr);
        i++;
        sleep(1);
   }   
   shmdt(addr);
   destroyshm(shmid);
   return 0;
}
//client.c
#include "comm.h"

int main()
{
   int shmid = getshm(4096);

   char* addr = shmat(shmid, NULL, 0); 
   if(addr == NULL)
   {   
        perror("shmat");
        return 1;
   }   
   int i = 0;
   while(i < 30) 
   {   
        addr[i++] = 'A'; 
        addr[i] = '\0';
        sleep(1);
   }   
   shmdt(addr);
   return 0;
}

运行结果
这里写图片描述
查看/删除共享内存的命令
ipcs -m / ipcrm -m id
这里写图片描述


共享内存的特点:
它是最快的进程间通信方式,但没有同步互斥机制
生命周期随内核,与消息队列一致

信号量

信号量机制是一种进程同步工具

信号量本质上是一个计数器

信号量结构体伪代码
struct semaphore
{
int value;
pointer_PCB queue;
}
value 表示的是资源数目
queue 表示的是需要获取资源的进程队列

p操作
将资源数目减1
v操作
将资源数目加1

P原语

P(s)
{
s.value = s.value--;
if (s.value < 0)
{
该进程状态置为等待状状态
将该进程的PCB插入相应的等待队列s.queue末尾
}
}

V原语

V(s)
{
s.value = s.value++;
if (s.value < =0)
{
唤醒相应等待队列s.queue中等待的一个进程
改变其状态为就绪态
并将其插入就绪队列
}
}

信号量集函数
semget函数
功能:用来创建和访问一个信号量集
原型
int semget(key_t key, int nsems, int semflg);
参数
key: 信号集的名字
nsems:信号集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1

shmctl函数
功能:用于控制信号量集
原型
int semctl(int semid, int semnum, int cmd, …);
参数
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号
cmd:将要采取的动作(有三个可取值)
最后一个参数根据命令不同而不同
返回值:成功返回0;失败返回-1

semop函数
功能:用来创建和访问一个信号量集
原型
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数
semid:是该信号量的标识码,也就是semget函数的返回值
sops:是个指向一个结构数值的指针
nsops:信号量的个数Copyright 2017 比特科技
比特科技
返回值:成功返回0;失败返回-1
说明:
sembuf结构体:
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
};
sem_num是信号量的编号。
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值:
一个是“-1”,也就是P操作,等待信号量变得可用;
另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用
sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO

使用二元信号量可以保护临界资源

//Makefile
.PHONY:sem clean
sem:test_sem.c comm.c
    gcc -o $@ $^ 
clean:
    rm -f sem
//comm.h
#ifndef __COMM_H_
#define __COMM_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


#define PATH "./"
#define SEM_ID 0x3333
union semun
{
    int                 val;
    struct semid_ds    *buf;
    unsigned short     *array;
    struct seminfo     *__buf;
};

int createsem(int nsems);
int getsem(int nsems);
int destroysem(int semid);
int initsem(int semid, int nu, int value);
int P(int semid, int nu);
int V(int semid, int nu);



#endif
//comm.c
#include "comm.h"

static int comm(int nsems, int flag)
{
    key_t key = ftok(PATH, SEM_ID);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }
    int semid = semget(key, nsems, flag);
    if(semid < 0)
    {
        perror("semget");
        return -2;
    }
    return semid;
}
int createsem(int nsems)
{
    return comm(nsems, IPC_CREAT|IPC_EXCL);
}
int getsem(int nsems)
{
    return comm(nsems, IPC_CREAT);
}
int initsem(int semid, int nu, int value)
{
    union semun _un;
    _un.val = value;
    if(semctl(semid, nu, SETVAL, _un) < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}

static int commPV(int semid, int nu, int op)
{
    struct sembuf _s;
    _s.sem_num = nu;
    _s.sem_op = op;
    _s.sem_flg = 0;
    if(semop(semid, &_s, 1) < 0)
    {
        perror("semop");
        return -1;
    }
    return 0;
}
int P(int semid, int nu)
{
    return commPV(semid, nu, -1);
}
int V(int semid, int nu)
{
    return commPV(semid, nu, 1);
}
int destroysem(int semid)
{
    if(semctl(semid, 0, IPC_RMID) < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}
//sem.c

#include "comm.h"


int main()
{
    int semid = createsem(1);
    initsem(semid, 0, 1);
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        return 1;
    }
    else if(pid == 0)
    {
      //child
      int _semid = getsem(1);
      int i = 0;
      while(i++ < 20)
      {
        P(_semid, 0);
        printf("A");
        fflush(stdout);
        usleep(218723);
        printf("A ");
        fflush(stdout);
        usleep(322109);
        V(_semid, 0);
      }

    }
    else
    {
     //father
      int i = 0;
      while(i++ < 20)
      {
        P(semid, 0);
        printf("B");
        fflush(stdout);
        usleep(218732);
        printf("B ");
        fflush(stdout);
        usleep(321312);
        V(semid ,0);
      }
    wait(NULL);
    destroysem(semid);
    }

    return 0;
}

这里写图片描述
此时屏幕成为临界资源,可以看到AA BB 成对出现 如果去掉 P、V呢?

#include "comm.h"


int main()
{

    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        return 1;
    }
    else if(pid == 0)
    {
      //child
      int i = 0;
      while(i++ < 20)
      {
        printf("A");
        fflush(stdout);
        usleep(218723);
        printf("A ");
        fflush(stdout);
        usleep(322109);
      }

    }
    else
    {
     //father
      int i = 0;
      while(i++ < 20)
      {
        printf("B");
        fflush(stdout);
        usleep(218732);
        printf("B ");
        fflush(stdout);
        usleep(321312);
      }
    wait(NULL);
    }

    return 0;
}

这里写图片描述
看到AA, BB 不会成对出现
即 二元信号量可以保护临界资源
生命周期也是随内核.

查看/删除信号量的指令ipsc -s/ipcrm -m
这里写图片描述
进程间通信(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值