源地址:http://www.cnblogs.com/forstudy/archive/2012/03/26/2413724.html

Linux--进程间通信(信号量,共享内存)(转)

 

一. 信号量  

l信号量: 解决进程之间的同步与互斥的IPC机制

多个进程同时运行,之间存在关联
  •同步关系
  •互斥关系
互斥与同步关系存在的根源在于临界资源
  •临界资源是在同一个时刻只允许有限个(通常只有一个)进程可以访问(读)或修改(写)的资源
    –硬件资源(处理器、内存、存储器以及其他外围设备等)
    –软件资源(共享代码段,共享结构和变量等)
  •临界区,临界区本身也会成为临界资源
 
 
一个称为信号量的变量
  •信号量对应于某一种资源,取一个非负的整型值
  •信号量值指的是当前可用的该资源的数量,若它等于0则意味着目前没有可用的资源
在该信号量下等待资源的进程等待队列
对信号量进行的两个原子操作(PV操作)
  •P操作
  •V操作
 
最简单的信号量是只能取0 和1 两种值,叫做二维信号量
 
编程步骤:
  创建信号量或获得在系统已存在的信号量
    •调用semget()函数
    •不同进程使用同一个信号量键值来获得同一个信号量
  初始化信号量
    •使用semctl()函数的SETVAL操作
    •当使用二维信号量时,通常将信号量初始化为1
  进行信号量的PV操作
    •调用semop()函数
    •实现进程之间的同步和互斥的核心部分
  如果不需要信号量,则从系统中删除它
    •使用semclt()函数的IPC_RMID操作
    •在程序中不应该出现对已被删除的信号量的操作
 

 eg. 通过对信号量PV操作,消除父子进程间的竞争条件,使得其调用顺序可控。
 union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}; // 将信号量sem_id设置为init_value
int init_sem(int sem_id,int init_value) {
union semun sem_union;
sem_union.val=init_value;
if (semctl(sem_id,,SETVAL,sem_union)==-) {
perror("Sem init");
exit();
}
return ;
}
// 删除sem_id信号量
int del_sem(int sem_id) {
union semun sem_union;
if (semctl(sem_id,,IPC_RMID,sem_union)==-) {
perror("Sem delete");
exit();
}
return ;
}
// 对sem_id执行p操作
int sem_p(int sem_id) {
struct sembuf sem_buf;
sem_buf.sem_num=;//信号量编号
sem_buf.sem_op=-;//P操作
sem_buf.sem_flg=SEM_UNDO;//系统退出前未释放信号量,系统自动释放
if (semop(sem_id,&sem_buf,)==-) {
perror("Sem P operation");
exit();
}
return ;
}
// 对sem_id执行V操作
int sem_v(int sem_id) {
struct sembuf sem_buf;
sem_buf.sem_num=;
sem_buf.sem_op=;//V操作
sem_buf.sem_flg=SEM_UNDO;
if (semop(sem_id,&sem_buf,)==-) {
perror("Sem V operation");
exit();
}
return ;
}
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include "sem_com.c" #define DELAY_TIME 3 int main() {
pid_t pid;
// int sem_id;
// key_t sem_key; // sem_key=ftok(".",'a');
// 以0666且create mode创建一个信号量,返回给sem_id
// sem_id=semget(sem_key,1,0666|IPC_CREAT);
// 将sem_id设为1
// init_sem(sem_id,1); if ((pid=fork())<) {
perror("Fork error!\n");
exit();
} else if (pid==) {
// sem_p(sem_id); // P操作
printf("Child running...\n");
sleep(DELAY_TIME);
printf("Child %d,returned value:%d.\n",getpid(),pid);
// sem_v(sem_id); // V操作
exit();
} else {
// sem_p(sem_id); // P操作
printf("Parent running!\n");
sleep(DELAY_TIME);
printf("Parent %d,returned value:%d.\n",getpid(),pid);
// sem_v(sem_id); // V操作
// waitpid(pid,0,0);
// del_sem(sem_id);
exit();
} }

在以上程序注释//未去掉时,即没用信号量机制时,其结果为:

显然,此处存在竞争条件。

在以上程序注释//去掉后,即使用信号量机制,其结果为:

由于父子进程采用同一信号量且均执行各自PV操作,故必先等一个进程的V操作后,另一个进程才能工作。

二. 共享内存

 

最为高效的进程间通信方式
 
进程直接读写内存,不需要任何数据的拷贝
  •为了在多个进程间交换信息,内核专门留出了一块内存区
  •由需要访问的进程将其映射到自己私有地址空间
  •进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率
 
多个进程共享一段内存,需要依靠某种同步机制,如互斥锁和信号量等

l共享内存编程步骤:
  1. 创建共享内存
    •函数shmget()
    •从内存中获得一段共享内存区域
 
  2. 映射共享内存
    •把这段创建的共享内存映射到具体的进程空间中
    •函数shmat()
 
  3. 使用这段共享内存
    •可以使用不带缓冲的I/O读写命令对其进行操作
 
  4. 撤销映射操作: 函数shmdt()
 
  5. 删除共享内存: 函数shctl()
 

eg. 下面这个例子完成:父进程从stdin读取字符串并保存到共享内存中,子进程从共享内存中读出数据并输出到stdout

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h> #define BUFFER_SIZE 2048 int main() {
pid_t pid;
int shmid;
char *shm_addr;
char flag[]="Parent";
char buff[BUFFER_SIZE];
// 创建当前进程的私有共享内存
if ((shmid=shmget(IPC_PRIVATE,BUFFER_SIZE,))<) {
perror("shmget");
exit();
} else
printf("Create shared memory: %d.\n",shmid); // ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息
// -m 表示共享内存
printf("Created shared memory status:\n");
system("ipcs -m"); if((pid=fork())<) {
perror("fork");
exit();
}else if (pid==) {
// 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
if ((shm_addr=shmat(shmid,,))==(void*)-) {
perror("Child:shmat");
exit();
}else
printf("Child: Attach shared-memory: %p.\n",shm_addr); printf("Child Attach shared memory status:\n");
system("ipcs -m");
// 比较shm_addr,flag的长度为strlen(flag)的字符
// 当其内容相同时,返回0
// 否则返回(str1[n]-str2[n])
while (strncmp(shm_addr,flag,strlen(flag))) {
printf("Child: Waiting for data...\n");
sleep();
} strcpy(buff,shm_addr+strlen(flag));
printf("Child: Shared-memory: %s\n",buff);
// 删除子进程的共享内存映射地址
if (shmdt(shm_addr)<) {
perror("Child:shmdt");
exit();
}else
printf("Child: Deattach shared-memory.\n"); printf("Child Deattach shared memory status:\n");
system("ipcs -m"); }else{
sleep();
// 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
if ((shm_addr=shmat(shmid,,))==(void*)-) {
perror("Parent:shmat");
exit();
}else
printf("Parent: Attach shared-memory: %p.\n",shm_addr); printf("Parent Attach shared memory status:\n");
system("ipcs -m");
// shm_addr为flag+stdin
sleep();
printf("\nInput string:\n");
fgets(buff,BUFFER_SIZE-strlen(flag),stdin);
strncpy(shm_addr+strlen(flag),buff,strlen(buff));
strncpy(shm_addr,flag,strlen(flag));
// 删除父进程的共享内存映射地址
if (shmdt(shm_addr)<) {
perror("Parent:shmdt");
exit();
}else
printf("Parent: Deattach shared-memory.\n"); printf("Parent Deattach shared memory status:\n");
system("ipcs -m");
// 保证父进程在删除共享内存前,子进程能读到共享内存的内容
waitpid(pid,NULL,);
// 删除共享内存
if (shmctl(shmid,IPC_RMID,NULL)==-) {
perror("shmct:IPC_RMID");
exit();
}else
printf("Delete shared-memory.\n"); printf("Child Delete shared memory status:\n");
system("ipcs -m"); printf("Finished!\n");
} exit();
}

 
分类: Linux

转:Linux--进程间通信(信号量,共享内存)的更多相关文章

  1. Linux进程间通信—使用共享内存

    Linux进程间通信-使用共享内存 转自: https://blog.csdn.net/ljianhui/article/details/10253345 下面将讲解进程间通信的另一种方式,使用共享内 ...

  2. Linux进程间通信——使用共享内存

    一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存. ...

  3. Linux进程间通信——使用共享内存(转)

    一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存. ...

  4. Linux进程间通信(四) - 共享内存

    共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只 ...

  5. linux进程间通信之共享内存篇

    本文是对http://www.cnblogs.com/andtt/articles/2136279.html中共享内存(上)的进一步阐释说说明 1 共享内存的实现原理 共享内存是linux进程间通讯的 ...

  6. linux进程间通信之共享内存学习记录

    进程 狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed). 广义定义:进程是一个具有一定独立功能的 ...

  7. Linux进程间通信之共享内存

    一,共享内存  内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内存,被多个进程同时映射的物理内存,即共享内存.  映射物理内存叫挂接,用完以后解除映射叫脱接. 1,共享内存的特点 ...

  8. linux进程间通信同步-共享内存

    参考:https://www.cnblogs.com/charlesblc/p/6142868.html 使用有名信号量,sem_open().sem_close().sem_post().sem_w ...

  9. Linux进程间通信(消息队列/信号量+共享内存)

    写在前面 不得不说,Deadline果真是第一生产力.不过做出来的东西真的是不堪入目,于是又花了一早上重写代码. 实验内容 进程通信的邮箱方式由操作系统提供形如 send()和 receive()的系 ...

  10. Linux环境进程间通信(五): 共享内存(下)

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

随机推荐

  1. tomcat下文件路径

    第一种:复制要访问的文件a.txt至tomcat安装路径下的webapps/ROOT文件夹下: 访问路径为:localhost:8080/a.txt 或者在webapps文件夹下新建一个文件夹(tes ...

  2. csp-s模拟测试85

    csp-s模拟测试85 $T1$全场秒切没有什么区分度,$T2$全场成功转化题意但是我并不会打,$T3$暴力都没打很遗憾. 100 00:21:49 02:56:35 02:56:49 135 02: ...

  3. 二分查找总结及部分Lintcode题目分析 1

    进行二分查找课程回顾与总结,包括以下几个方面,二分法的模板总结和解题思路.应用. 二分法模板总结classical binary search: 1. 必须要做的排除极端情况,也就是数组(用A表示)不 ...

  4. 百度跨域搜索demo

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. SQL语句的四种连接

    SQL的四种连接查询 内连接 inner join 或者 join 外连接 左连接   left join 或者 left outer join 右连接  right join 或者 right ou ...

  6. 2018-8-10-win10-uwp-验证输入-自定义用户控件

    title author date CreateTime categories win10 uwp 验证输入 自定义用户控件 lindexi 2018-08-10 19:16:51 +0800 201 ...

  7. JS规则 较量较量(比较操作符) 两个操作数通过比较操作符进行比较,得到值为真(true)和假(false)。【>; <; >=; <=; !=;==】

    较量较量(比较操作符) 我们先来做道数学题,数学考试成绩中,小明考了90分,小红考了95分,问谁考的分数高? 答: 因为"95 > 90",所以小红考试成绩高. 其中大于号& ...

  8. Spring 框架中Http请求处理流程

    Spring Web http request请求流程: 首先介绍这边你需要知道的继承体系,DispacherServlet继承自FrameworkServlet,FrameworkServlet继承 ...

  9. Ubuntu Service说明与使用方法

    1 什么是Ubuntu的Service 网上很多资料说, service就是linux中随开机自启动的, 并且在后台运行的程序. 个人认为, 至少对于Ubuntu来说, 这个说法是不太准确的, 这只不 ...

  10. 自己整理的一个访问SQLite3数据库的C++类

    原文地址:自己整理的一个访问SQLite3数据库的C++类作者:vigra 近日,对SQLite3的使用进行了研究.真不愧是优秀的嵌入数据库,API接口也极其简捷.基本上只要使用以下几个接口就能完成数 ...