源地址: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. VS2010-MFC(常用控件:组合框控件Combo Box)

    转自:http://www.jizhuomi.com/software/189.html 上一节讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常见,例如, ...

  2. hexo next中遇到的bug,引发出的关于jquery中click()函数和on("click",function())的区别

    个人博客:https://mmmmmm.me 源码:https://github.com/dataiyangu/dataiyangu.github.io 背景: 本人在维护博客的时候加入了aplaye ...

  3. js 仿微信投诉—引入vue.js,拆分组件为单个js

    效果 页面目录 index.html <!DOCTYPE html > <html> <head> <meta charset="UTF-8&quo ...

  4. Caffe系列1——网络文件和求解分析

    1. 首先,我们先看一个完整的文件:lenet_train_test.prototxt name: "LeNet" #整个网络的名称 layer { #数据层——训练数据 name ...

  5. 【转】IOS获取屏窗高度踩坑之window.outerHeight

    近日本人在直接使用window.outerHeight获取屏窗高度时 在iphone 6中出现等于0的情况,从而导致页面发生错误 后找遍代码,测试无数,终于让我逮住了这个该死兼容 window.out ...

  6. 廖雪峰Java11多线程编程-3高级concurrent包-3Condition

    Condition实现等待和唤醒线程 java.util.locks.ReentrantLock用于替代synchronized加锁 synchronized可以使用wait和notify实现在条件不 ...

  7. JOIN方法也是连贯操作方法之一

    JOIN方法也是连贯操作方法之一,用于根据两个或多个表中的列之间的关系,从这些表中查询数据. 大理石平台规格 join通常有下面几种类型,不同类型的join操作会影响返回的数据结果. INNER JO ...

  8. 0906NOIP模拟测试赛后总结

    我进前十辣.然而有10个大佬去学LCT了于是没有考试. Dybala神和exzkt神分-rank1,奶风神和林哥分-rank3,wc.miemeng和DuanYue神140分-rank5. 我.ooo ...

  9. string、char* 、int数据类型相互转换

    string类型转换成char*类型,这里一般有以下三种方法: 1.c_str()方法 string name="Qian"; char *str=(char*)name.c_st ...

  10. AtCoder F - Exhausted?

    传送门 sxy题解: //Achen #include<algorithm> #include<iostream> #include<cstring> #inclu ...