C | 进程间通信的方式
C | 进程间通信的方式
1.无名管道
无名管道是实现亲缘间进程通信的一种方式,属于半双工通信。
无名管道的实现是队列,不能使用lseek对读写指针偏移。
无名管道有两个端:数据流入端和数据流出端,也叫写端和读端。它们是两个固定的端口,遵循先入先出,数据读走消失的原则。
如果无名管道中没有数据,read读取时会阻塞等待数据。
无名管道的长度有限,64K。管道写满之后,再次写入会阻塞。
如果关闭了写入端口,管道机制会认为写端关闭,将管道中的数据读出后,read不会再阻塞。不会影响程序运行。
如果关闭读端关闭,写入将不再有意义,再次写入时会发生“管道破裂”。
无名管道不在文件系统上体现,数据存储在内存上,进程结束,数据丢失。
创建无名管道的函数接口 : 头文件:#include <unistd.h>
原型: int pipe(int pipefd[2])
功能: 创建一个无名管道,读写端两个文件描述符分别封装到fd[0]和fd[1](fd[0]---r;fd[1]---w)
参数 : 自己定义的数组
返回值:成功返回 0 ;失败返回 -1;
使用无名管道实现亲缘间进程通信 :
因为fork函数创建完子进程后,文件描述符也会被拷贝过去,相当于父子进程共用文件描述符去操作同一个管道。
双方通信需要两个管道。
无名管道实现进程间通信的原理:
2.有名管道
有名管道是建立在无名管道的基础上的一种进程间通信方式,目的是为了解决无名管道只能用于亲缘间进程通信的缺点。
有名管道在文件系统中属于特殊的管道文件,虽然在文件系统上有所体现,但数据并不是存放在磁盘上,而是存放在内存上,进程结束,数据丢失。
有名管道通过文件实现进程间通信,所以需要打开这个文件,进程需要分别以读、写权限打开,如果打开方式不足这两个权限,open会阻塞等待权限。
创建有名管道 :方式一 :linux命令mkfifo 有名管道的名字方式二 :函数接口头文件:#include <sys/types.h>#include <sys/stat.h>原型: int nkfifo(const char *pathname,mode_t mode)功能: 创建一个有名管道参数: pathname:目标路径mode : 权限返回值:成功返回0;失败返回-1
两个进程间通信需要两个有名管道文件
有名管道实现进程间通信的原理:
3.信号
信号是软件层对硬件层中断的一种模拟,是一种异步通信的方式,通过信号传递信息,信息量较小。
在进程创建初期,会创建一个信号函数表,每一个信号对应一个指向信号处理函数的功能函数指针。
信号的处理方式:
1.忽略:指收到信号后,不采取任何措施。
例如:SIGCHLD :子进程结束后给父进程发送的一个信号
2.默认:指收到信号后,执行进程创建初期构建的信号函数表中默认的信号处理函数。
3.捕获:指将信号函数表中信号对应的默认函数指针指向自己定义的信号处理函数,收到信号后,执行按自己的想法执行。
SIGKILL和SIGSTOP不能被忽略和捕捉。
信号相关的函数接口:1)signal
头文件:#include <signal.h>
原型: typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler)
功能: 注册一个信号处理函数
参数: signum:信号handler:信号处理方式忽略:SIG_IGN 默认:SIG_DFL 捕捉:自定义的函数指针
返回值:成功返回一个指向上一次执行的信号处理函数的函数指针;失败返回SIG_ERR2)kill
头文件: #include <sys/types.h>#include <signal.h>
原型: int kill(pid_t pid, int sig)
功能: 给指定的一个进程发送一个信号
参数: pid :目标进程号sig :信号号
返回值:成功返回0;失败返回-13)pause
头文件:#include <unistd.h>
原型: int pause(void)
功能: 将一个进程挂起,直到收到信号改变状态注意:只监控信号,但是并不消耗信号
返回值:成功返回接收到的信号号;失败返回 -14)raise
头文件: #include <signal.h>
原型: int raise(int sig)
功能: 给自己发送一个信号
参数: sig:信号号
返回值: 成功返回0 ;失败返回非0值5)alarm
头文件:#include <unistd.h>
原型: unsigned int alarm(unsigned int seconds)
功能: 给自己发送一个闹钟信号---SIGALRM 默认终止进程
参数: seconds:定时时间,单位秒注意:如果调用alarm后再次调用alarm 函数会刷新定时器的时间,打断了上一次alarm定时,上一次的alarm不会再发送闹钟信号了,会将上一次的alarm剩余的秒数返回回来
返回值:成功返回上一次alarm剩余的秒数 ,0 代表的是定时器时间已到
4.消息队列
消息队列是一种全双工的通行方式,利用内核空间完成通信,实质上是管道在内核空间上的集合。
消息队列的函数接口:1)ftok
头文件:#include <sys/types.h>#include <sys/ipc.h>
原型: key_t ftok(const char *pathname,int proj_id)
功能: 根据pathname和id这两个参数生成一个key_t类型的数据
参数: pathname:存在的文件路径即可proj_id:int数据类型的低8位
返回值:成功返回生成好的键值;失败返回-12)msgget
头文件:#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>
原型: int msgget(key_t key,int msgflg)
功能: 创建或者打开一个消息队列
参数: key:通过键值创建一个消息队列IPC_PRIVATE:直接用于亲缘间进程自定义key的值(ftok)msgflg:打开消息队列的方式IPC_CREAT IPC_EXCL
返回值:成功返回一个消息队列的ID号(非负整数);失败-13)msgsnd
头文件:同上
原型: int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg)
功能: 向消息队列放入数据
参数: msqid:目标消息队列的ID号msgp:要发送消息的地址,必须是一个结构体类型,结构体第一个成员必须是消息的类型(long)的数据,其余成员可以修改。struct msgbuf {long type;char text[1024];}msgsz:发送消息中正文的大小msgflg:发送消息的方式阻塞:0 非阻塞:IPC_NOWAIT
返回值:成功返回0;失败返回-1 4)msgrcv
头文件:同上
原型: ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg)
功能: 从消息队列中获取数据
参数: msgid:消息队列的ID号msgp:存放数据的地址,结构体要求同msgsndmsgsz:消息的正文的大小msgtyp:消息的类型 如果写0,从消息队列头开始读取msgflg:获取数据的方式 (同msgsnd) 返回值:成功返回接收的正文的大小;失败返回-15)msgctl
头文件:同上
原型: int msgctl(int msgid,int cmd,struct msgqid_ds *buf)
功能: 控制消息队列
参数: msgid:目标消息队列cmd:控制方式IPC_STAT:获取消息队列的所有信息IPC_SET:设置消息队列里的信息(设置msg_perm结构体)IPC_RMID:删除消息队列,第三个参数写NULLbuf:信息结构体
返回值:成功返回0,失败返回-1
消息队列实现进程间通信的原理:
5.共享内存
共享内存是利用地址映射的方式实现进程间通信,实质上是在内核空间建立一个共享的区域,然后将这片区域映射到用户空间,此时操作用户空间就相当于直接操作内核空间。
共享内存是进程间通信中效率最高、速度最快的一种通信方式。但是共享内存中的数据读走不会消失,每次写入数据时会覆盖之前的数据。
共享内存一般用于数据实时采集上传,本身不具备进程同步的功能。
共享内存的函数接口:1)shmget
头文件:#include <sys/ipc.h>#include <sys/shm.h>
原型: int shmget(key_t key,size_t size,int shmflg)
功能: 创建或者打开共享内存
参数: key:创建或者打开共享内存的方式,同消息队列size:创建共享内存的大小,如果已经存在则无效size一般都设置为4K的倍数,如果不是4K的倍数,真实的共享内存会近似4K的倍数,但ipcs查看时不会显示出来shmflg:打开的方式,同消息队列
返回值:成功返回共享内存的ID号,失败返回-12)shmat
头文件:同上
原型: void *shmat(int shmid,const void *shmaddr,int shmflg)
功能: 地址映射,将shmid所对应的共享内存的首地址映射到用户空间
参数: shmid:目标共享内存shmaddr:映射到用户空间的地址写NULL操作系统会自动分配地址,通过返回值返回给用户空间shmflg:映射出来的地址的操作权限0:读写权限 SHM_RDONLY:只读权限
返回值: 成功返回用户空间映射的地址;失败返回(void *)-1 3)shmdt
头文件:同上
原型: int shmdt(const void *shmaddr)
功能: 取消地址映射
参数: shmaddr:用户空间的首地址
返回值:成功返回0;失败返回-14)shmctl
头文件:同上
原型: int shmctl(int shmid,int cmd,struct shmid_ds *buf)
功能: 控制共享内存
参数: shmid:共享内存ID号cmd:控制方式IPC_STAT:获取共享内存的属性IPC_SET:设置共享内存的属性IPC_RMID:删除共享内存如果有进程正在使用,会等进程断开连接后删除buf:消息结构体地址
返回值:IPC_STAT控制方式,成功返回ID号;其他情况成功返回0;失败返回-1
使用的时候要进行地址映射,不再使用要取消地址映射。
共享内存实现进程间通信的原理:
6.信号灯集
信号灯集实质上是内核空间信号灯的集合。
信号灯的函数接口:1)semget
头文件:#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>
原型: int semget(key_t key,int nsems,int semflg)
功能: 创建或者打开一个信号灯集
参数: key:信号灯集的键值(同消息队列)nsems:创建的信号灯集中信号灯的个数semflg:打开方式(同消息队列)
返回值:成功返回一个信号灯集的ID号;失败返回-12)semctl
头文件:同上
原型: int semctl(int semid,int semnum,int cmd,...)
功能: 控制信号灯集
参数: semid:要控制的信号灯集的ID号semnum:信号灯的编号cmd:控制方式IPC_RMID:删除信号灯集,不考虑第二个参数GETVAL:获取信号灯的值SETVAL:设置信号灯的值...:可变参数,一个联合体union semun {int val;struct semid_ds *buf;}
返回值:成功返回0;控制方式设置为GETVAL成功返回一个信号灯的值;失败返回-1
函数需要自己封装
//信号灯集示例:
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>//定义一个初始化使用的联合体
union semun { int val;
};
//初始化函数
//参数:semid:信号灯集ID
// semnum:信号灯编号
// val:要设置的初始值
//返回值:成功返回0;失败返回-1
int sem_init_val(int semid,int semnum,int val)
{union semun myval;myval.val = val;if (-1 == semctl(semid,semnum,SETVAL,myval)) {printf("初始化信号灯%d失败\n",semnum);return -1;}return 0;
}
// p 操作
//参数:semid:信号灯集ID
// semnum:信号灯编号
//返回值:成功返回0;失败返回-1
int sem_p(int semid,int semnum)
{struct sembuf mybuf; mybuf.sem_num = semnum;//信号灯编号mybuf.sem_op = -1;//申请资源mybuf.sem_flg = 0;//阻塞方式if (-1 == semop(semid,&mybuf,1)) {printf("p操作失败\n");return -1;}return 0;
}
// v操作
//参数:semid:信号灯集ID
// semnum:信号灯编号
//返回值:成功返回0;失败返回-1
int sem_v(int semid,int semnum)
{struct sembuf mybuf; mybuf.sem_num = semnum;//信号灯编号mybuf.sem_op = 1;//申请资源mybuf.sem_flg = 0;//阻塞方式if (-1 == semop(semid,&mybuf,1)) {printf("v操作失败\n");return -1;}return 0;
}int main(int argc, const char *argv[])
{//生成一个共享内存使用的key值key_t mykey = ftok("/home/linux/class/",'a');if (-1 == mykey) {printf("生成建值失败\n");return -1;}key_t mykey1 = ftok("/home/linux/class/TCP/",'a');if (-1 == mykey1) {printf("生成建值失败\n");return -1;}//创建或者打开信号灯集int semid = semget(mykey1,2,IPC_CREAT|0664);if (-1 == semid) {perror("semget");return -1;}//初始化信号灯集sem_init_val(semid,0,1);sem_init_val(semid,1,0);while (1) {sem_p(semid,0); //p操作printf("请输入\n");scanf("%s",buf);sem_v(semid,1);}return 0;
}************************************************************
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>union semun {int val;
};int sem_init_val(int semid,int semnum,int val)
{union semun myval;myval.val = val;if (-1 == semctl(semid,semnum,SETVAL,myval)) {printf("初始化信号灯%d失败\n",semnum);return -1;}return 0;
}int sem_p(int semid,int semnum)
{struct sembuf mybuf; mybuf.sem_num = semnum;mybuf.sem_op = -1;mybuf.sem_flg = 0;if (-1 == semop(semid,&mybuf,1)) {printf("p操作失败\n");return -1;}return 0;
}int sem_v(int semid,int semnum)
{struct sembuf mybuf; mybuf.sem_num = semnum;mybuf.sem_op = 1;mybuf.sem_flg = 0;if (-1 == semop(semid,&mybuf,1)) {printf("v操作失败\n");return -1;}return 0;
}int main(int argc, const char *argv[])
{key_t mykey = ftok("/home/linux/class/",'a');if (-1 == mykey) {printf("生成建值失败\n");return -1;}key_t mykey1 = ftok("/home/linux/class/TCP/",'a');if (-1 == mykey1) {printf("生成建值失败\n");return -1;}int semid = semget(mykey1,2,IPC_CREAT|0664);if (-1 == semid) {perror("semget");return -1;}while(1){sem_p(semid,1); //p操作printf("buf = %s\n",buf);sem_v(semid,0);}return 0;
}
7.有名信号量
有名信号量一般用于两个进程间通信。
信号灯的编号从0开始。
操作方式与无名信号量一致,经典的PV操作,来实现进程间通信。
当多个进程间通信时不适用,因为要申请大量的信号灯。
有名信号量函数接口:1)sem_open
头文件:#include <fcntl.h>#include <sys/stat.h>#include <semaphore.h>
原型: sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value)
功能: 创建或者打开一个有名信号灯
参数: name:信号灯的名字oflag:打开方式0:直接打开(不需要后两个参数) O_CREAT O_EXCLmode:权限value:信号灯的初值
返回值:成功返回创建或打开的信号灯的地址;失败返回SEM_FAILED2)sem_close
头文件:同上
原型: int sem_close(sem_t *sem)
功能: 关闭有名信号灯
参数: sem:有名信号灯的地址
返回值:成功返回0;失败返回-1 3)sem_unlink
头文件:同上
原型: int sem_unlink(const char *name)
功能: 删除有名信号灯
参数: name:有名信号灯的名字
返回值:成功返回0;失败返回-14)sem_post
头文件:同上
原型: int sem_post(sem_t *sem)
功能: 释放资源(V操作)
参数: sem:目标信号的
返回值:成功返回0;失败返回-15)sem_wait
头文件:同上
原型: int sem_wait(sem_t *sem)
功能: 申请资源(P操作)
参数: sem:目标信号灯
返回值:成功返回0;失败返回-1
C | 进程间通信的方式相关推荐
- 一种网络进程间通信的方式—— 管道
一种网络进程间通信的方式-- 管道 摘要: 文章主要介绍了计算机网络进程间通信的必要性以及进程间通信所采用的几种方式,重点说明了管道通信的原理及命名管道的实现方法. 关键词:管道 命名管道 进程 一. ...
- linux实现单机qq_Linux后台服务器开发——Linux下进程间通信的方式有哪些?
Linux下进程间通信的方式有: 管道 消息队列 信号 信号量 共享存储 套接字 一.管道 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道 PIPE无名管道 详情参阅:ht ...
- 进程间通信的方式及原理
# 进程间通信的方式 文章目录 # 进程间通信的方式 消息队列 使用步骤 管道 消息队列 信号 信号量 socket 消息队列 首先消息队列就是内核维护的一块链表区域,只要是有足够权限的进程都可以向队 ...
- 进程间通信的方式——信号、管道、消息队列、共享内存
进程间通信的方式--信号.管道.消息队列.共享内存 多进程: 首先,先来讲一下fork之后,发生了什么事情. 由fork创建的新进程被称为子进程(child process).该函数被调用一次,但返回 ...
- 进程间通信的方式(附代码分析)
进程间通信的方式 1. 进程间通信的几种方式 管道 比如 ls | grep 1;也就是将 进程 ls 拿到的结果作为 grep 1 这个进程的输入.实现了进程间的通信. 消息队列 消息队列就是我们的 ...
- 进程和线程的区别 及 进程间通信的方式
1.进程和线程的区别 (1)进程和线程对比: 进程是对运行时程序的封装,是系统资源调度和分配的基本单位,进程可以看做是操作系统的并行机制. 线程是进程的子任务,cpu ...
- Linux多线程间通信和多进程间通信的方式
文章目录 一.进程间的通信方式 1. 进程间通信的概念 1.1. 进程通信的目的: 1.2. Linux 进程间通信(IPC)的发展 2. 进程间通信的7种方式 2.1. 管道/匿名管道(pipe) ...
- 进程间通信的方式(四):信号量
信号量(semaphore)是一种用于提供不同进程之间或者一个给定的不同线程间同步手段的原语.信号量多用于进程间的同步与互斥,简单的说一下同步和互斥的意思: 同步:处理竞争就是同步,安排进程执行的先后 ...
- 进程间通信的方式总结
进程间通信就是在不同进程之间传播或交换信息. 进程间通信的目的如下: ①数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间. ②共享数据:多个进程想要操作共享数据, ...
最新文章
- JDK5.0新特性系列---目录
- python开源报表系统_流程设计器、表单设计器和简单报表管理开源OA系统smart-web...
- C++实现 找出10000以内的完数
- python百分号和斜杠_Python中正反斜杠(‘/’和‘\’)的意义与用法
- mysql数据库入门教程(15):流程控制结构
- 防止多重启动之调用Api [收集2005090201]
- 计算机教师自媒体方向,教师和自媒体,我该选择哪个深耕?
- 数字计算机的分类依据,数字计算机模拟计算机分类依据
- 计算机专业英语词库mdx,Mdict词库合集(22本实用词典)
- ARM资源免费下载——超级全面,力荐
- 2017网络安全方向学习总览(转载供本人查阅而已)
- 2021最新文本综述:从浅层到深度学习(附PDF下载)
- vim-airline use patched fonts
- C++ VS项目属性的一些配置项的总结
- 苹果开发者账号(三)
- java mye_JAVA环境搭建之MyEclipse10+jdk1.8+tomcat8环境搭建详解
- 坦然的面对一切都已经发生的事实
- 从立项到在steam平台上发布游戏,所要经历的流程。【esyi杂谈】
- 电脑msvcr120.dll文件丢失(一键修复方法)
- python郑州就业怎么样_目前学什么技术好
热门文章
- jack server 常见错误解决方法
- 操作系统之虚拟化CPU(一)介绍
- 大型建筑公司如何实现数字化转型
- 【信号分析之心电信号处理】
- VCSA Exception in invoking authentication handler User password expired
- Win10安装nodejs 错误代码2502,2503
- Java——将一个正整数分解质因数
- C++ 强制类型转换操作符(static_cast、dynamic_cast、const_cast和reinterpret_cast)
- 基于jupyter notebook的python编程-----MNIST数据集的的定义及相关处理学习
- 在互联网大厂的程序员多久能挣够100万?