前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进程间通讯(六).semaphore and shared(1)

进程间通讯(六).semaphore and shared(1)

作者头像
franket
发布2021-09-15 20:08:32
7000
发布2021-09-15 20:08:32
举报
文章被收录于专栏:技术杂记技术杂记

前言

UNIX/Linux 是多任务的操作系统,通过多个进程分别处理不同事务来实现,如果多个进程要进行协同工作或者争用同一个资源时,互相之间的通讯就很有必要了

进程间通信,Inter process communication,简称 IPC,在 UNIX/Linux 下主要有以下几种方式:

  • 无名管道 ( pipe )
  • 有名管道 ( fifo )
  • 信号 ( signal )
  • 信号量 ( semaphore )
  • 消息队列 ( message queues )
  • 共享内存 ( shared memory )
  • 套接字 ( socket )

这里分享一下我在学习进程通讯过程中的笔记和心得


概要


信号量

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。

Tip: 引自 《Linux进程间通信——使用信号量》

信号量与已经介绍过的IPC机构(管道、FIFO以及消息列队)不同,它是一个计数器,用于为多个进程提供对共享数据对象的访问

为了获得共享资源,进程需要执行下列操作

  • 1.测试控制该资源的信号量
  • 2.若此信号量的值为正,则进程可以使用该资源,在这种情况下,进程会将信号量值减1,表示它使用了一个资源单位
  • 3.否则,若此信号量的值为0,则进程进入休眠状态,直至信号量大于0,进程被唤醒后它返回至步骤1

当进程不再使用由一个信号量控制的共享资源时,该信号量值增1,如果有进程正在休眠等待此信号量,则唤醒它们

为了正确地实现信号量,信号量值的测试及减1操作应该是原子操作,为此信号量通常是在内核中实现的

常用的信号量形式被称为 二元信号量(binary semaphore) ,它控制单个资源,其初始值为1,但是一般而言,信号量的初值可以是任意一个正值,该值表明有多少个共享资源单位可供共享应用。

Tip: 引自 《UNIX环境高级编程》


共享内存

顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。

Tip: 引自 《Linux进程间通信——使用共享内存》

共享存储允许两个或多个进程共享一个给定的存储区,因为数据不需要在客户进程和服务进程之间复制,所以这是最快的一种IPC,使用共享存储时要掌握的唯一窍门是,在多个进程之间同步访问一个给定的存储区时,若服务器进程正在将数据放入共享存储区,则它在做完这一操作之前,客户进程不应当去取这些数据,通常,信号量用于同步共享存储访问(也可以用记录锁或互斥量)

Tip: 引自 《UNIX环境高级编程》


系统限制

信号量限制

系统层面有一些内核参数限制了信号量的大小

代码语言:javascript
复制
root@ubuntu:~# ipcs -ls

------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767

root@ubuntu:~# sysctl -a 2>/dev/null | grep sem
kernel.sem = 250	32000	32	128
root@ubuntu:~#

表示系统信号量集的最大个数为128个

一个信号量集中最大可以有250个信号量

系统中最多总共有32000个信号量

semop系统调用允许的信号量最大个数为32个

共享内存限制

系统层面有一些内核参数限制了共享内存的大小

代码语言:javascript
复制
root@ubuntu:~# ipcs -lm

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1

root@ubuntu:~# sysctl -a 2>/dev/null | grep shm
kernel.shmmax = 33554432
kernel.shmall = 2097152
kernel.shmmni = 4096
vm.hugetlb_shm_group = 0
root@ubuntu:~#
  • SHMMAX参数定义共享内存段的最大尺寸(以字节为单位),默认值是32MB。
  • SHMALL参数控制着系统一次可以使用的共享内存总量(以页为单位),默认值2097152.该参数值至少应该大于等于SHMMAX/PAGE_SIZE。
  • SHMMNI 参数设置系统范围内共享内存段的最大数量,默认值是 4096。

Tip: /etc/sysctl.conf 中可以进行内核配置


代码示例

要求

  • A、B两个进程(非亲缘关系)
  • A进程往共享内存中写入任意字符串,B进程读取
  • 直到收到“quit”才退出
  • 写一次读一次,读一次写一次

注意:

  • 1)shmid要保持一致 IPC_CREAT|0666
  • 2)不能用sleep函数进行同步

提示:

  • 1)用信号量配合2个

代码示例

shmsemA.c

代码语言:javascript
复制
#include <stdio.h>
#include <sys/shm.h> //shmget,shmat,shmdt,shmctl 相关声明都在这里
#include <sys/sem.h> //sembuf,SEM_UNDO,SETALL 相关声明和宏定义都在这里
#include <string.h>  //memset,strcmp  相关函数声明都在这里

#define SHMSIZE 1024

typedef struct sembuf SB; // 将sembuf重命名

union semun  //定义semun共用体作为参数 
{ 
  int val;		 // value for SETVAL  设定一个值可以用
  struct semid_ds *buf;	 // buffer for IPC_STAT & IPC_SET  获取状态可以使用
  unsigned short *array; // array for GETALL & SETALL  设定所有值或获取所有值可以使用
  struct seminfo *__buf; // buffer for IPC_INFO 获取信息可以用
  void *__pad; //万能指针
};

int main() 
{
  int res=-1,shmid=0,semid=0;
  key_t  key=IPC_PRIVATE;
  char *shmaddr=NULL; //定义一个共享内存的指针
  SB sem_p0={0,-1,SEM_UNDO}, //构建对第一个信号量进行P操作的参数
     sem_v0={0,1,SEM_UNDO}, //构建对第一个信号量进行V操作的参数
     sem_v1={1,1,SEM_UNDO}; //构建对第二个信号量进行V操作的参数
  union semun sem_args;  
  unsigned short array[2]={0,0}; //构建数组

  sem_args.array=array; //构建出给两个信号量一起赋值的共用体参数
  

  if(-1 == (key=ftok("/",888))) //生成key
  {
    perror("ftok");
    return res;
  }
  if (0 > (shmid=shmget(key,SHMSIZE,IPC_CREAT|0600))) //使用shmget 创建或获取共享内存ID , 大小为1024个字节 , or use getpagesize() instead 
  {
    perror("shmget");
    return res;
  }
  else printf("created shared memory :%d\n",shmid); //显示出获取的共享内存ID
  
  if ((char *)0 > (shmaddr=shmat(shmid,0,0))) //通过ID获取地址,并且使用shmaddr进行指向
  {
    perror("shmat");
    return res;
  }
  else printf("attached shared memory:%p\n",shmaddr); //将内存地址打印出来
  
  if (0 > (semid=semget(key,2,IPC_CREAT|0600))) //通过key获取两个信号量的ID
  {
    perror("semget");
    return res;
  }
  else printf("created a sem set with two sems which id is :%d\n",semid); //将信号量ID打印出来

  if (0 > semctl(semid,0,SETALL,sem_args)) //将两个信号量一起赋值为0,设置值存于sem_args中
  {
    perror("semctl");
    return res;
  } 
  else printf("semset has been initialized\n");
  
  if (0 > semop(semid,&sem_v0,1)) //对第一个信号量进行V操作
  {
    perror("semop"); 
    return res;
  }

  memset(shmaddr,0,SHMSIZE); //将共享内存置0

  do
  {
    if (0 > semop(semid,&sem_p0,1)) //对第一个信号量进行P操作
    {
      perror("semop"); 
      return res;
    }
    puts("please enter the message to shm:\n('quit' to exit)");
    if(NULL == (fgets(shmaddr,SHMSIZE,stdin))) //将输入内容写到共享内存中
    {
      perror("fgets");
      return res;
    }
    if (0 > semop(semid,&sem_v1,1)) //对第二个信号量进行V操作
    {
      perror("semop"); 
      return res;
    }
  }while(strcmp(shmaddr,"quit\n"));   //如果输入为quit就退出

  if (0 > (shmdt(shmaddr))) //分离共享内存
  {
    perror("shmdt");
    return res;
  }
  else   printf("Deattach shared-memory\n"); 
  
  if (0 > semop(semid,&sem_p0,1)) //对第一个信号量进行P操作
  {
    perror("semop"); 
    return res;
  }

  if (0 > shmctl(shmid, IPC_RMID, NULL)) //删除和释放共享内存
  {
    perror("shmctl(IPC_RMID)\n");
    return res;
  }
  else printf("Delete shared-memory\n"); 
  res=0;
  return res;
}

本文系转载,前往查看

如有侵权,请联系?cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系?cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 概要
    • 信号量
      • 共享内存
        • 系统限制
          • 信号量限制
          • 共享内存限制
        • 代码示例
          • 要求
          • 代码示例
      相关产品与服务
      文件存储
      文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com