20155339 《信息安全系统设计》第十周课下作业-IPC

共享内存

  • 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间中。

  • 使用共享内存,不同进程可以对同一块内存进行读写。

  • 优点:由于所有进程对共享内存的访问就和访问自己的内存空间一样,而不需要进行额外系统调用或内核操作,同时还避免了多余的内存拷贝,所以,这种方式是效率最高、速度最快的进程间通信方式。

  • 缺点:内核并不提供任何对共享内存访问的同步机制,即引发读写问题。

  • Linux下一般一个page大小是4k。

  • 创建共享内存空间后,内核将不同进程虚拟地址的映射到同一个页面:所以在不同进程中,对共享内存所在的内存地址的访问最终都被映射到同一页面。

  • 共享内存的使用过程可分为 创建->连接->使用->分离->销毁 这几步。

  • 下图展示了共享内存的工作机制:

  • 使用帮助文档,查看共享内存的创建函数,如下:

  • 由上图可知:分配共享内存使用shmget函数:

使用方法如下:int shmget(key_t key, size_t size, int shmflg)

  • 由于帮助文档不怎么看得懂,又找到了下图进行学习:

  • 创建后,为了使共享内存可以被当前进程使用,必须紧接着进行连接操作。使用函数shmat(SHared Memory ATtach),参数传入通过shmget返回的共享内存id。

  • shamt函数:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg)。返回附加好的共享内存地址。

  • shmat函数:断开共享内存连接。

  • shmctl函数:共享内存管理。

  • 代码练习(读写数据):


#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
typedef struct{
char name[8];
int age;
} people;
int main(int argc, char** argv)
{
int shm_id,i;
key_t key;
char temp[8];
people *p_map;
char pathname[30] ; strcpy(pathname,"/tmp") ;
key = ftok(pathname,0x03);
if(key==-1)
{
perror("ftok error");
return -1;
}
printf("key=%d\n",key) ;
shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600);
if(shm_id==-1)
{
perror("shmget error");
return -1;
}
printf("shm_id=%d\n", shm_id) ;
p_map=(people*)shmat(shm_id,NULL,0);
memset(temp, 0x00, sizeof(temp)) ;
strcpy(temp,"test") ;
temp[4]='0';
for(i = 0;i<3;i++)
{
temp[4]+=1;
strncpy((p_map+i)->name,temp,5);
(p_map+i)->age=0+i;
}
shmdt(p_map) ;
return 0 ;
}

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
char name[8];
int age;
} people;
int main(int argc, char** argv)
{
int shm_id,i;
key_t key;
people *p_map;
char pathname[30] ; strcpy(pathname,"/tmp") ;
key = ftok(pathname,0x03);
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key=%d\n", key) ;
shm_id = shmget(key,0, 0);
if(shm_id == -1)
{
perror("shmget error");
return -1;
}
printf("shm_id=%d\n", shm_id) ;
p_map = (people*)shmat(shm_id,NULL,0);
for(i = 0;i<3;i++)
{
printf( "name:%s\n",(*(p_map+i)).name );
printf( "age %d\n",(*(p_map+i)).age );
}
if(shmdt(p_map) == -1)
{ perror("detach error");
return -1;
}
return 0 ;
}
  • 运行结果

  • 可看出读和写两段代码都是在同一段地址上进行的。

管道

  • 管道实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。

  • 数据由缓冲区的尾部写入,头部读出。先进先出原则。

  • PIPE函数:

  • 代码练习

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<wait.h>
#include<sys/types.h>
int main(int argc ,char *argv[])
{
int pipefd[2],result;
char buf[1024];
int flag=0;
pid_t pid;
result= pipe(pipefd);//创建一个管道
if(result==-1){
perror("pipe error:");
exit(EXIT_FAILURE);
}
pid=fork();//创建一个子进程
if(pid==-1)
{
perror("fork error:");
exit(EXIT_FAILURE);
}
else if(pid==0){
if((close(pipefd[1]))==-1)//close write only read
{
perror("close write error:");
exit(EXIT_FAILURE);
}
while(1){ //循环读取数据
read(pipefd[0],buf,1024);//最多读取1024个字节
printf("read from pipe : %s\n",buf);
if(strcmp(buf,"quit")==0){// if 读取到的字符串是exit 这是
//父进程会接受到一个终止进程的信号,父进程会回收子进程的资 // 源等
exit(EXIT_SUCCESS);
}
}
}else{
//close read only write
if((close(pipefd[0]))==-1){
perror("close read error:");
exit(EXIT_FAILURE);
}
while(1)//循环写入内容
{
waitpid(pid,NULL,WNOHANG);//等待子进程退出
if(flag==1)
exit(0);
scanf("%s",buf);
write(pipefd[1],buf,strlen(buf)+1);//具体写多少个字节
if(strcmp(buf,"quit")==0){
flag=1;
sleep(1);//让子进程完全退出。
}
}
}
return 1;
}

FIFO(命名管道)

  • 我觉得是管道的升级版,因为命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信。
  • 操作方法只要创建了一个命名管道然后就可以使用open、read、write等系统调用来操作。
  • 代码练习:

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h> int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
const int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[PIPE_BUF + 1]; if(access(fifo_name, F_OK) == -1)
{
//管道文件不存在
//创建命名管道
res = mkfifo(fifo_name, 0777);
if(res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
} printf("Process %d opening FIFO O_WRONLY\n", getpid());
//以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
pipe_fd = open(fifo_name, open_mode);
data_fd = open("Data.txt", O_RDONLY);
printf("Process %d result %d\n", getpid(), pipe_fd); if(pipe_fd != -1)
{
int bytes_read = 0;
//向数据文件读取数据
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while(bytes_read > 0)
{
//向FIFO文件写数据
res = write(pipe_fd, buffer, bytes_read);
if(res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
//累加写的字节数,并继续读取数据
bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
}
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE); printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h> int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
//清空缓冲数组
memset(buffer, '\0', sizeof(buffer)); printf("Process %d opening FIFO O_RDONLY\n", getpid());
//以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
pipe_fd = open(fifo_name, open_mode);
//以只写方式创建保存数据的文件
data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
printf("Process %d result %d\n",getpid(), pipe_fd); if(pipe_fd != -1)
{
do
{
//读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}while(res > 0);
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE); printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
  • 运行结果:

  • 管道和命名管道有什么区别

  • 1.在命名管道中,管道可以是事先已经创建好的,而在管道中,管道已经在主进程里创建好了,然后在fork时直接复制相关数据或者是用exec创建的新进程时把管道的文件描述符当参数传递进去。

    2.一般来说FIFO和PIPE一样总是处于阻塞状态。如果不希望命名管道操作的时候发生阻塞,可以在open的时候使用O_NONBLOCK标志,以关闭默认的阻塞操作。

信号

  • 一个信号的生命周期中有两个阶段:生成和传送。
  • 内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:

    1.异常:进程运行过程中出现异常;

    2.其它进程:一个进程可以向另一个或一组进程发送信号;

    3.终端中断:Ctrl-C,Ctrl-/等;

    4.作业控制:前台、后台进程的管理;

    5.分配额:CPU超时或文件大小突破限制;

    6.通知:通知进程某事件发生,如I/O就绪等;

    7.报警:计时器到期。
  • 代码练习:

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h> int main()
{
char buffer[100]; struct sigaction act; if(sigaction(SIGINT,&act, NULL) == -1)
{
printf("sigaction error exit now\n");
exit(0);
} while(1)
{
fgets(buffer,sizeof(buffer),stdin);
printf("%s\n",buffer);
} return 0;
}

消息队列

  • 消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。
  • 内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。
  • 每个消息队列中的消息,又构成一个独立的链表。
  • Linux的消息队列(queue)实质上是一个链表,它有消息队列标识符(queue ID)。
  • msgget创建一个新队列或打开一个存在的队列;
  • msgsnd向队列末端添加一条新消息;
  • msgrcv从队列中取消息,取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息。
  • 代码练习:

#include<stdio.h>
#include<stdlib.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>
#define MAX_TEXT 1024
#define MSG_SIZE 512
struct msg_st{
long int msg_type; //消息类型
char text[MAX_TEXT];//消息内容
};
int main()
{
struct msg_st data;
char buf[MSG_SIZE];
int msgid=msgget((key_t)2456,0666|IPC_CREAT);
if(msgid==-1){
perror("msgget");
exit(1);
}
while(1){
printf("接收:");
if(msgrcv(msgid,(void*)&data,MAX_TEXT,1,0)==-1){
perror("msgrcv");
exit(2);
}
printf("%s\n",data.text);
}
return 0;
}

#include<stdio.h>
#include<sys/ipc.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<unistd.h>
#include<string.h>
#define MAX_TEXT 512
#define MSG_SIZE 512
struct msg_st
{
long int msg_type; //消息类型
char text[MAX_TEXT];//消息内容
};
int main()
{
struct msg_st data;
char buf[MSG_SIZE];
//创建消息队列
int msgid=msgget((key_t)2456,0666|IPC_CREAT);
if(msgid==-1)
{
perror("msgget");
exit(1);
}
printf("消息队列创建成功\n");
//发送消息
while(1)
{
//从键盘输入发送的消息
printf("发送:");
fgets(buf,MSG_SIZE,stdin);
data.msg_type=1;
strcpy(data.text,buf);
//将消息发送到消息队列
if(msgsnd(msgid,(void*)&data,MAX_TEXT,0)==-1){
perror("msgsnd");
exit(1);
}
}
return 0;
}
  • 运行截图

参考链接

20155339 《信息安全系统设计》第十周课下作业-IPC的更多相关文章

  1. 20155326 第十周课下作业-IPC

    20155326 第十周课下作业-IPC 学习题目: 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 学习过程 -IPC ...

  2. 第十周课下作业-IPC

    第十周课下作业-IPC 题目:研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 共享内存 共享内存允许两个或多个进程进程共 ...

  3. 2017-2018-1 20155320第十周课下作业-IPC

    2017-2018-1 20155320第十周课下作业-IPC 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 共享内存 ...

  4. 20155322 2017-2018-1《信息安全系统设计》第十周 课下作业-IPC

    20155322 2017-2018-1<信息安全系统设计>课下作业-IPC 作业内容 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接. 共享内存 管 ...

  5. 20155219 第十周课下作业-IPC

    题目:研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 1.共享内存 共享内存就是允许两个不相关的进程访问同一个逻辑内存. ...

  6. 20165234 《Java程序设计》第十周课下作业

    相关知识点的总结 泛型 Java 泛型的主要目的是可以建立具有类型安全的集合框架,如链表.散列映射等数据结构. 可以使用“class 名称<泛型列表>”声明一个类,为了和普通的类有所区别, ...

  7. #学号 20175201张驰 《Java程序设计》第10周课下作业MyList

    参见附件,补充MyList.java的内容,提交运行结果截图(全屏) 课下推送代码到码云 public class MyList { public static void main(String [] ...

  8. 20165234 《Java程序设计》第二周课下作业

    1. 教材代码完成情况测试P14 把100改为自己的后四位学号,编译运行Kernighan.java 代码的功能是从给定一个数字,实现从1依次加到此数的和. 如下是我用命令行实现代码的编译与运行. 2 ...

  9. 2017-2018-1 20155330 《信息安全系统设计基础》第10周课堂测试&课下作业

    2017-2018-1 20155330 <信息安全系统设计基础>第10周课堂测试&课下作业 stat命令的实现-mysate 学习使用stat(1),并用C语言实现 提交学习st ...

随机推荐

  1. 转: Dubbo远程调用服务框架原理与示例

    Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和  Spring 框架无缝集成. 主要核心部件: Remoting:  网络通 ...

  2. 自己搭建anki服务器

    目录 centos端 电脑客户端 安卓端 centos端 # 安装服务 yum -y install python-setuptools easy_install Ankiserver mkdir - ...

  3. ORACLE数据泵还原(IMPDP命令)

    Oracle数据库还原IMPDP命令是相对于EXPDP命令的,方向是反向的.即对于数据库备份进行还原操作.一.知晓IMPDP命令 C:\>impdp -help Import: Release ...

  4. 非定制UIImagePickerController的使用

    非定制UIImagePickerController的使用 效果: 源码: // // ViewController.m // ImagePic // // Created by XianMingYo ...

  5. Linux 系统常见命令功能大全_【all】

    Linux常见快捷键(6个) ctrl + u:剪贴光标前面 ctrl + k:剪贴光标后面 ctrl + y:粘贴 ctrl + r:查找命令 ctrl + insert:复制 shift+ ins ...

  6. Linux which/whereis/locate命令详解

    which 查看可执行文件的位置,从全局环境变量PATH里面查找对应的路径,默认是找 bash内所规范的目录 whereis 查看文件的位置,配合参数-b,用于程序名的搜索,从linux数据库查找. ...

  7. python2.7与3.5共存windows平台安装

    文:铁乐与猫 2018-3-18 周日 01.首先是安装python2.7: 官网下载 https://www.python.org 点击安装包进行安装 可以选择自定义的路径 将默认打x的[add p ...

  8. LinkedHashSet 元素唯一,存储取出有序

      package cn.itcast_04; import java.util.LinkedHashSet; /* * LinkedHashSet:底层数据结构由哈希表和链表组成. * 哈希表保证元 ...

  9. 一、JavaScript概述 二、JavaScript的语法 三、JavaScript的内置对象

    一.JavaScript的概述###<1>JavaScript的概念 又称ECMAScript,和java没有任何关系 嵌入在HTML元素中的 被浏览器解释运行的 一种脚本语言. ###& ...

  10. #001 WebStrom SVN使用技巧

    WebStrom中SVN 的一些使用技巧 2016-03-23 17:11:52 星期三 使用SVN的目录,是为了来管理代码的版本. 服务端语言 都有比较完善的IDE,前端JS代码,由于之前一直都用 ...