详解共享内存以及所有进程间通信的特点

一、什么是共享内存?

共享内存:使得多个进程可以访问同一块内存空间

共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。

二、共享内存的特点

(1)、共享内存是以传输数据为目的
(2)、共享内存无同步无互斥
(3)、共享内存是所有进程间通信速度最快的。
(4)、共享内存的生命周期随内核

三、共享内存的优缺点

1、优点:使用共享内存进行进程间的通信非常方便,而且函数的接口也简单,数据的共享使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

四、实现共享内存

在学习共享内存的代码前我们先来看几个关于共享内存的函数。

1、创建共享内存——shmget函数

函数原型为:

      int shmget(key_t key,size_t size,int shmflg);

key:让两个不同进程可以访问到同一资源。
size:表示共享内存的大小。
shmflg:代表标志位,分别是IPC_CREAT,IPC_EXCL。IPC_CREAT单独使用时表示 若存在共享内存,打开返回,若不存在,创建之。IPC_EXCL一般不单独使用。若两个标志位同时使用表示若存在共享内存,出错返回,若不存在,则创建之。

2、销毁共享内存——shmctl函数

函数原型为:

       int shmctl(int shmid,int cmd,struct shmid_ds *buf);


shm_id:是shmget函数返回的共享内存标识符。

cmd:command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段


buf:是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:

struct shmid_ds
 {
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t shm_segsz;   /* Size of segment (bytes) */
    time_t shm_atime;   /* Last attach time */
    time_t shm_dtime;   /* Last detach time */
    time_t shm_ctime;   /* Last change time */
    pid_t  shm_cpid;    /* PID of creator */
    pid_t  shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t shm_nattch;  /* No. of current attaches */
               ...
 };

接下来,让我们来看看如何显示共享内存的相关代码吧!
Makefile:

   .PHONY:all
all:client server

client:client.c comm.c
    gcc -o $@ $^
server:server.c comm.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f server client

comm.h:

#define _COMM_H_

#define PATHNAME "."
#define PROJ_ID 0x6666

#include<stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>


int creatShm(int size);
int getShm(int size);
int destoryShm(int shmid);

#endif //_COMM_H_

comm.c:

#include "comm.h"

static int commShm(int size,int flags)
{
   key_t _key=ftok(PATHNAME,PROJ_ID);
   if(_key<0)
  {
     perror("ftok");
     return -1;
  }
  int shmid=shmget(_key,size,IPC_CREAT|IPC_EXCL);
  if(shmid<0)
  {
     perror("shmget");
      return -2;
  }
   return shmid;
}
int createShm(int size)
{
    return commShm(size,IPC_CREAT|IPC_EXCL);
}

int getShm(int size)
 {
      return commShm(size,IPC_CREAT);
 }

int destoryShm(int shmid)
{
   if((shmctl(shmid,IPC_RMID,NULL))!=0)
   {
       perror("shmctl");
       return -1;
   }
    return 0;
}

client.c:

include "comm.h"
int main()
{
    printf("hello,bit!\n");
    return 0;
}

server.c :

#include "comm.h"
int main()
{
    int shmid =createShm(4097);//创建共享内存
    sleep(1);
    printf("hello,ShareMemory\n");
    sleep(4);
    destoryShm(shmid);//销毁共享内存
    return 0;
}

程序的运行结果为:创建内存成功后1s后输出hello,ShareMemory然后4s后共享内存被销毁!(如图所示)
没创建共享内存前:

创建后:

这里写图片描述

这里写图片描述

4s后,共享内存被销毁。

这里写图片描述

关于共享内存我们就说到这里,接下来让我们看看关于进程间通信的相关知识吧!

五、进程间通信的特点

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC)。

IPC的方式通常有管道(包括匿名管道和命名管道)、消息队列、信号量、共享内存等。

接下来我们来看看它们各自有什么特点!

管道:

(1)匿名管道:

它的数据只能在一个方向上流动,具有固定的读端和写端。

它只能用于具有亲缘关系的进程之间的通信(例如父子进程或者兄弟进程之间)。

它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

(2)命名管道(FIFO):

与匿名管道不同。FIFO可以在无关的进程之间交换数据,与无名管道不同。

FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

消息队列:

消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

消息队列的生命周期随内核

信号量:

信号量是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作,保证信号量的原子性。

每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

支持信号量组,拥有了信号量,就可以拥有使用临界资源。

共享内存:

共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

共享内存是以传输数据为目的,故无同步与互斥。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
进程间通信中,信号是一种非常重要的机制。它可以让一个进程向另一个进程发送一个异步通知,让接收进程执行特定的操作。 信号的基本概念是:当一个进程需要通知另一个进程发生了某个事件时,它可以向另一个进程发送一个信号。接收进程在接收到信号后,可以选择忽略该信号或者执行相应的处理程序。 信号可以由如下几种方式发送: 1. kill命令:可以使用kill命令向指定进程发送信号。 2. 信号函数:可以使用信号函数向指定进程发送信号。 3. 软件异常:当一个进程执行某些非法操作时,操作系统会向该进程发送一个信号。 常见的信号有: 1. SIGINT:中断进程。通常由Ctrl+C键触发。 2. SIGTERM:请求进程终止。通常由kill命令发送。 3. SIGKILL:强制进程终止。通常由kill命令发送。 4. SIGSTOP:暂停进程。通常由kill命令发送。 5. SIGCONT:恢复进程。通常由kill命令发送。 6. SIGUSR1和SIGUSR2:用户自定义信号。 在Linux系统中,可以使用kill命令向指定进程发送信号。例如: ``` kill -SIGINT pid ``` 这个命令会向指定pid的进程发送SIGINT信号,让该进程接收到信号后执行相应的处理程序。 在编写程序时,可以使用信号函数向指定进程发送信号。例如,下面的代码向指定进程发送SIGUSR1信号: ``` #include <signal.h> #include <unistd.h> int main() { int pid = 12345; // 指定进程的pid kill(pid, SIGUSR1); // 向指定进程发送SIGUSR1信号 return 0; } ``` 当接收进程接收到信号后,可以执行相应的处理程序。例如,下面的代码定义了一个信号处理程序,当接收到SIGUSR1信号时会执行该处理程序: ``` #include <signal.h> void handle_signal(int signum) { // 处理SIGUSR1信号 } int main() { signal(SIGUSR1, handle_signal); // 注册信号处理程序 while(1) {} // 循环等待信号 return 0; } ``` 上面的代码中,signal函数用于注册信号处理程序,当接收到SIGUSR1信号时会执行handle_signal函数。循环部分是为了等待信号的到来。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值