C | 进程间通信的方式

article/2025/10/9 13:17:16

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 ;失败返回非05)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

http://chatgpt.dhexx.cn/article/022pFFdf.shtml

相关文章

面试题:进程间通信的方式

liunx六大进程间通信方式 管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号量&#xff0c;socket&#xff0c;信号&#xff0c;文件锁 1&#xff0c;管道 1&#xff0c;匿名管道&#xff1a; 概念&#xff1a;在内核中申请一块固定大小的缓冲区&…

进程间通信的几种方式浅谈(上)

程序员必须让拥有依赖关系的进程集协调&#xff0c;这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信&#xff08;interprocess communication&#xff09;。第二种技术是同步&#xff0c;…

进程间通信的几种方式

一、管道 在Linux 中&#xff0c;管道是一种使用非常频繁的通信机制。从本质上说&#xff0c;管道也是一种文件&#xff0c;但它又和一般的文件有所不同&#xff0c;管道可以克服使用文件进行通信的两个问题&#xff0c;具体表现如下所述。 • 限制管道的大小。实际上&#x…

【进程间通信】进程间通信方式汇总

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、管道模型1.1 匿名管道1.2 命名管道 2、消息队列2.1 创建消息队列2.2 发送消息2.3 接收消息 3、共…

android中进程间通信的几种方式

进程间通信&#xff08;IPC&#xff09;方式 使用Bundle使用文件共享使用Messenger使用AIDL使用COntentProvider使用Socket 一、使用Bundle 我们都知道Android中三大组件Activity&#xff0c;Service&#xff0c;Receiver都支持在Intent中传递Bundle数据&#xff0c;而Bundle…

操作系统——进程间通信

文章目录 其他文章管道消息队列共享内存信号量信号Socket总结 个人博客网站&#xff1a; https://xingkongdiyiren.github.io/myblog/,完整的Java知识体系&#xff0c;包括408&#xff0c;架构&#xff0c;业务&#xff0c;产品&#xff0c;软技能等 其他文章 操作系统——概…

进程间的通信方式(六种)

进程之间的通信 参考文章&#xff1a;https://blog.csdn.net/qq_34827674/article/details/107678226 前提知识&#xff1a;每个进程都有自己的用户空间&#xff0c;而内核空间是每个进程共享的。因此进程之间想要进行通信&#xff0c;就需要通过内核来实现。 管道&#xff1…

【操作系统】进程间通信的五种方式

引言1.进程对白&#xff1a;管道、记名管道、套接字1.管道2.虫洞&#xff1a;套接字3.信号 4.信号旗语&#xff1a;信号量5.进程拥抱&#xff1a;共享内存 引言 进程作为人类的发明&#xff0c;自然免不了脱离人类的习性&#xff0c;也有通信需求。如果进程之间不进行任何通信…

进程之间的通信方式

进程之间的通信方式包括管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号&#xff0c;信号量&#xff0c;socket六种方式&#xff0c;下面来对这6种方式分别进行介绍。 一、管道 管道的结构示意图如上所示&#xff0c;管道包含一个输入端和一个输出端&#xff0…

进程间通信的六种常见方式

目录 进程间通信&#xff08;IPC&#xff09;&#xff1a; 一、管道 二、FIFO 三、消息队列 四、共享内存 五、信号 六、信号量 七、进程间通信方式总结&#xff1a; 进程间通信&#xff08;IPC&#xff09;&#xff1a; 进程间通信的方式有很多&#xff0c;这里主要…

idea数据库管理工具配置连接数据库

idea数据库管理工具配置连接数据库 —————————————————————————————————————————————————————— 在cmd中操作数据库太麻烦了&#xff0c;还好idea为我们提供了很方便的数据库管理工具&#xff0c;下面看看如何用idea连接…

idea连接数据库失败解决办法

一.IDEA连接Mysql报错&#xff1a; 未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver‘.  Change driver class 报错详细内容&#xff1a;未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver’. Change driver class 报错原因&#xff1a;Mysql版本为5.0&#xff0c;找不到com.mysql.…

IDEA中如何连接数据库并显示数据库信息。

我的相关博客&#xff1a; java代码程序中连接mysql数据库的方法及代码 mysql数据库并发上锁问题&#xff0c;java代码 关于IDEA中怎么连接mysql数据库 相信部分朋友在使用IDEA操作数据库的时候会出现有关数据库信息的报错。 显示没有此表&#xff0c;或者无数据库等错误信息…

idea连接数据库失败原因及解决方案

这是因为安装mysql的时候时区设置的不正确 mysql默认的是美国的时区&#xff0c;而我们中国大陆要比他们迟8小时&#xff0c;采用8:00格式。使用的数据库是MySQL&#xff0c;在你没有指定MySQL驱动版本的情况下它自动依赖的驱动是8.0.12很高的版本&#xff0c;这是由于数据库和…

IDEA如何连接数据库 / IDEA连接数据库 新手,图解

如果在下面操作中遇到了问题&#xff0c;可以查看我的这篇笔记 https://blog.csdn.net/qq_44627608/article/details/115442815 1. 打开IDEA的数据库设置 2. 新建数据库链接 左上角的便是你链接的数据库了&#xff0c;第一次打开时左上角应该是空的&#xff0c;需要你从下面的…

IDEA使用Database连接数据库

一&#xff0c;连接数据库 1.点击右侧Database后,点击左上角按钮&#xff0c;然后选中Data Source &#xff0c;无论使用的是MariaDB还是MySQL都选中MySQL 2.点击非常隐蔽的 … 按钮 3.选中你需要使用的数据库 4.填写数据的账号&#xff0c;密码&#xff0c;数据库名称&…

idea代码连接mysql数据库操作

此文章仅为作者学习上的问题记录&#xff0c;如有错误&#xff0c;欢迎指正。 首先是准备工作 先创建一个Module 之后在此Module下创建一个lib包 然后将下载的连接包复制到lib包下&#xff0c;连接包下载地址&#xff1a; https://cdn.mysql.com//Downloads/Connector-J/mysq…

IDEA中配置数据库连接

1.点击IDEA右边框的 Database &#xff0c;在展开的界面点击 选择 Data Source &#xff0c;再选择 MySQL 2.在弹出的界面进行基本信息的填写 3.填写完后&#xff0c;点击Test Connection 测试一下 这样就是填写的没问题&#xff0c;如果是第一次点击这个&#xff0c;需要下载…

Idea连接MySQL数据库教程 (简单明了)

使用Idea连接数据库 具体步骤&#xff1a;点击右侧DataBase → 点击号 → 点击Data Source 选择MySQL → 输入用户名、密码、连接的数据库名称&#xff08;连接路径会自动生成&#xff09; → 可点击下面的Test Connection来测试连接 注意事项一&#xff1a; 第一次连接需要下…

IntelliJ IDEA配置连接MySQL数据库

如图&#xff1a; 1、点击主界面右侧边栏Database 2、点击""号 3、点击Data Source 4、点击MySQL 如图填写数据库名&#xff0c;用户名和密码&#xff0c;之后点击下方Test Connection测试 连接成功会显示上图字样 这时发现已经可以查看到数据库信息&#xff0c;说…