进程间通信方式汇总

作者:华清远见讲师

对于进程来说,进程的本质就是程序的执行过程,进程是独立运行的单位,所以不同的程序的执行就产生不同的进程,进程和进程之间,运行空间时相互独立的,以平常的方法无法实现两者之间的通信。

这里给出几种进程之间通信的方法可供参考学习: 管道、信号、IPC通信共享内存、消息队列、信号量)、套接口(socket)。

刚刚接触进程的学者,我们可以给以上的方法分类,同时分析每种通信方式的实现过程:

第一类:原始通信方式:最早的通信方式,理解起来简单、易懂。

(1)管道通信:实质是管道文件操作,分为有名管道和 无名管道两种。

无名管道 : 用在有亲缘关系进程之间通信,例如父子进程之间。通信方向单一,有固定的读端口fd[0](只能read()),固定的写端口fd[1](只能write()),如下图所示,构成一个半双工通道。


图1.无名管道

创建无名管道的方法:pipe()

包含头文件:#include

函数原型 : int pipe(int fd[2]);

fd: 整型的数组名数组长度2

返回值 : 成功 0 : fd[0] 读文件描述符 fd[1] 写文件描述符

失败 -1

举例:

#include

#include

#include

int main()

{

pid_t id;

int fd[2] = {0};

if(pipe(fd) == -1) // 注意1, pipe() 执行次数1次 放在 fork()前面

{

perror("pipe");

return -1;

}

id = fork(); // 创建一个子进程

if(id == 0) // 子进程

{

write(fd[1],"hello world!",13); // 写管道

printf("send OK\n");

}

else if(id > 0) // 父进程

{

char buf[100] = {0};

read(fd[0], buf, 100); // 读管道

printf("rcv : %s\n", buf);

}

else

{

perror("fork");

}

return 0;

}

有名管道: 用在任意两个进程之间通信,实质是两个进程同时访问一个管道文件,所有操作都属于文件IO。

创建有名管道方法:mkfifo()

头文件 :#include #include #include

函数原型 : int mkfifo(const char *filename, mode_t mode);

filename :管道文件的路径 相对路径 绝对路径

mode : 文件的访问权限

返回值 :成功 0

失败 -1 errno perror()

举例:

///读进程

#include

#include

#include

#include

#define FIFO "MYFIFO"

int main()

{

int r;

int fd;

char buf[100] = {0};

r = access(FIFO, F_OK); // 判断文件是否存在

if(r == -1) // 文件不存在

{

r = mkfifo(FIFO, 0666); // 创建管道文件

if(r == 0)

{

printf("create FIFO\n");

}

else //创建失败

{

perror("mkfifo MYFIFO");

return 0;

}

}

else //文件已存在

{

printf("%s 文件存在\n", FIFO);

}

fd = open(FIFO, O_RDONLY); //打开文件

if(fd != -1)

{

r = read(fd, buf, 100); //阻塞读取信息

printf("recv %d : %s\n", r, buf);

close(fd); // 关闭文件

}

return 0;

}

写进程

#include

#include

#include

#include

#include

#define FIFO "MYFIFO"

int main()

{

int fd;

fd = open(FIFO, O_WRONLY); //打开文件

if(fd != -1)

{ //写入信息

write(fd, "hello", strlen("hello")+1);

close(fd);

}

}

注意事项:管道通信, 两端(两个进程)通信, 要求 只有一端read() 方法, 另一端write()方法才有意义;read() 阻塞读取管道信息,write() 阻塞写入信息。

(2)信号:信号通信是唯一的异步通信,所有进程 默认接收所有信号

处理信号 : signal() 忽略信号 默认处理 捕捉信号(自定义处理方法)

发送信号 : 发送给目标进程 kill() 给自己 raise()

常见信号:

信号编号 信号名 默认意义 目的

2 SIGINT 按键 Ctrl+c 给终端上正在的进程 终止进程

3 SIGQUIT 按键 Ctrl+\ 给终端上正在的进程 终止进程

9 SIGKILL 杀死一个进程 (不能被忽略) 杀死进程

20 SIGTSTP 按键 Ctrl+z 终止进程

处理信号的函数:signal()

函数原型: #include

typedef void (*sighandler_t)(int); // 函数指针类型的重命名

函数原型 : sighandler_t signal(int signo, sighandler_t handler);

signo : 信号的编号

handler : SIG_IGN 忽略信号

SIG_DFL 默认处理

fun 自定义处理函数的首地址

返回值: 成功 首地址

失败 -1

发送信号的函数:kill() raise()

函数原型 : #include

#include

int kill(pid_t id, int signo);

id : 进程号 > 0 向PID== id 进程发送信号

0 向同组的进程发送信号

-1 向进程表中所有进程(除去 PID最大 )发送信号

< -1 向进程组号 == |id|发送信号

signo : 信号的编号 或信号名

返回值: 成功 0

失败 -1

int raise(int signo);

signo : 信号编号

返回值: 成功 0

失败 -1

举例:

接收信号的进程

#include

#include

void handler(int signo) // 自定义信号处理函数

{

if(signo == SIGQUIT) // 判断信号 是不是 SIGQUIT

{

printf("input ctrl+\\ \n");

}

else if(signo == SIGINT) // 判断信号 是不是 ctrl+c发送的

{

printf("Ctrl +c\n");

exit(0); //进程退出

}

}

int main()

{

signal(SIGINT, handler); // 设置信号处理方法

signal(SIGQUIT, handler);

while(1)

{

printf("PID = %d\n", getpid());

sleep(1);

}

发送信号的进程

#include

#include

#include

int main(int argc, char *argv[])

{

if(argc < 3)

{

printf("input: %s PID signo\n", argv[0]);

return 0;

}

pid_t id = atoi(argv[1]); // 获取参数 ---> 转成整型

int signo = atoi(argv[2]); // 信号编号 ---> 转成整型

kill(id, signo);

}

第二类:IPC通信方式, 通信的特点, 通过一个IPC对象,获取对应的ID从而实现通信

图2. IPC通信

获取IPC对象: 有亲缘进程之间, 例如 父子进程, IPC对象key 值 为IPC_PRIVATE

无关进程之间, key值 通过ftok() 获取:

方法: 函数原型: key_t ftok(const char *pathname, int idno);

pathname : 目录的路径 相对路径

绝对路径

idno : 取值 0 --- 255之间的数值

返回值:成功 键值 失败 -1

(1)共享内存

创建/打开共享内存-----> 映射------> 通信------> 解除映射 ----->删除共享内存

头文件:#include

#include

#include

创建/打开共享内存:

函数原型: int shmget(key_t key, int size, int shmflg);

key : 有亲缘关系进程 IPC_PRIVATE

无关进程 ftok() 获取键值

size : 共享内存的大小 字节数

shmflg : 共享内存的权限 和open() 的参数位相同

例子: key PIC_PRIVATE 0666 或者 0777 ....

ftok() IPC_CREAT|0666

返回值:成功 共享内存的段标识符

失败 -1

映射函数原型:

void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid: 共享内存的段标志符

shmaddr : 计划映射的位置 == 首地址

NULL == 0 系统自动映射,找首地址

shmflg : 映射后空间的访问权限

SHM_RDONLY 只读

0 可读可写

返回值 :成功 映射区域的首地址

失败 (void *)-1

共享内存通信方法: 已知映射的首地址p 不阻塞读写------>效率高

读共享内存: printf("%s\n", p);

char buf[100]; strcpy(buf, p);

写共享内存: scanf("%s", p);

char buf[] = "jxkdadjlaj"; strcpy(p, buf);

解除映射 函数原型:

int shmdt(void *shmaddr);

shmaddr: 映射区域首地址

返回值: 成功 0

失败 -1

删除共享: int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid : 共享内存的段标志符

cmd: IPC_RMID 删除对象 第3个参数 NULL

IPC_SET 设置共享内存的属性 第3个参数 新属性存放位置

IPC_STAT 获取共享内存的属性 第3个参数 属性存放位置

buf : 属性存放的首地址

返回值: 成功 0

失败 -1

举例:

#include

#include

#include

#include

#include

#include

#include

int main()

{

int shmid;

pid_t id;

char *p;

shmid = shmget(IPC_PRIVATE, 100, 0666); // 创建/打开共享内存

if(shmid == -1)

{

perror("shmget");

}

else

{

printf("shmget OK\n");

system("ipcs -m"); // 查看共享内存信息

id = fork(); // 创建 子进程

if(id == 0) // 在 子进程中 写入信息

{

p = shmat(shmid, 0, 0); // 映射

if(p == (char *)-1)

{

perror("c: shmat");

exit(0);

}

printf("c: shmat OK\n");

strcpy(p, "hello"); // 写共享内存

shmdt(p); // 解除映射

}

else if(id > 0) // 在 父进程 中 读信息

{

p = shmat(shmid, 0, 0); // 映射

if(p == (char *)-1)

{

perror("f: shmat");

exit(0);

}

printf("f: shmat OK\n");

sleep(1); // 先 写入 再读取信息

printf("f : recv %s\n", p); // 读共享内存

shmdt(p); // 解除映射

shmctl(shmid, IPC_RMID, NULL); // 删除共享内存

system("ipcs -m");

}

}

}

(2)消息队列

创建/打开消息队列 ---> 添加消息/取出消息 ----> 删除消息队列

头文件:#include

#include

#include

函数原型: int msgget(key_t key, int flag);

key : 键值 有亲缘关系进程 IPC_PRIVATE

无关 ftok()

flag : open()权限位 相同

0666 ....

IPC_CREAT | 0666

返回值:成功 消息队列ID

出错 -1

向消息队列中添加消息:msgsnd()

函数原型 : int smgsnd(int msgid, const void *buf, size_t size, int flag);

msgid : 消息队列ID

buf : 添加消息的存放位置

自定义结构体---->存放消息类型 + 正文

struct msgbuf

{

long type; // 消息的类型, 使用者给定的含义

正文数据类型 text[N]; // 根据具体情况改变的

}

size : 正文的字节数

flag : 0 阻塞形式添加消息

IPC_NOWAIT 不阻塞添加

返回值: 成功 0

失败 -1

从消息队列中取出消息:msgrcv() 过滤

函数原型 int msgrcv(int smgid, void *buf, size_t size, long msgtype, int flag);

msgid : 消息队列ID

buf : 接收到消息的存放位置

size : 正文字节数

msgtype : 接收的消息类型 过滤条件

0 没有过滤,直接接收队列的第一个消息

> 0 只接收 消息队列中,第一个消息类型 == msgtype 的消息

< 0 只接收 消息类型 不大于 |msgtype| 并且最小的

flag : 0 阻塞接收

IPC_NOWAIT 不阻塞接收

返回值: 成功 接收信息的实际字节数

失败 -1

删除消息队列: msgctl

int msgctl(int msgid, int cmd, struct msgid_ds *buf);

msgid : 消息队列ID

cmd : IPC_RMID 删除对象 第3个参数 NULL

IPC_SET 设置属性 第3个 buf 属性存放位置

IPC_STAT 获取属性 第3个 buf 属性存放位置

buf : 属性存放首地址

返回值: 成功 0 ; 失败 -1

举例:

#include

#include

#include

#include

#include

#include

#include

typedef struct msgbuf // 自定义消息的结构体类型

{

long type; // 消息类型

char text[100]; // 正文

}MSG_c;

int main()

{

pid_t id;

int msgid,r;

msgid = msgget(IPC_PRIVATE, 0666); // 创建/打开消息队列

if(msgid == -1)

{

perror("msgget");

return -1;

}

printf("msgget OK %d\n",msgid);

id = fork(); // 创建子进程

if(id == 0) // 子进程中 添加消息

{

MSG_c a;

a.type = 1; // 消息的具体内容

strcpy(a.text, "hello world!");

r = msgsnd(msgid, &a, sizeof(a.text), 0); // 阻塞形式添加到消息队列中

if(r == -1)

{

perror("msgsnd ");

exit(0);

}

printf("msgsnd OK r = %d\n",r);

}

else if(id > 0) // 父进程

{

MSG_c b;

r = msgrcv(msgid, &b, sizeof(b.text),1,0); // 取出第一个 消息类型== 1的消息

if(r == -1)

{

perror("msgrcv");

exit(0);

}

printf("r = %d,type = %ld %s\n", r, b.type,b.text); // 打印 消息信息

msgctl(msgid, IPC_RMID, NULL); // 删除消息队列

}

}

(3)信号量 :保护临界资源----> 进程之间实现互斥, 信号量常常修饰共享内存

创建/打开信号量---> 初始化信号量(执行 1 次)----> P操作 ----> V操作 --->删除

头文件:#include

#include

#include

创建一个信号量: semget()

函数原型: int semget(key_t key, int num, int semflg);

key : 键值 亲缘关系进程 IPC_PRIVATE

无关进程 ftok()

num : 信号量的个数 一般 1

semflg : 权限 亲缘进程 0666 ....

无关进程 IPC_CREAT

IPC_EXCL 唯一的信号量, 如果已经存在,则返回错误

返回值: 成功 信号量ID

失败 -1

函数原型: semctl()

int semctl(int semid, int semnum, int cmd, union semun arg);

semid : 信号量ID

semnum : 信号量的编号 一般 0 : 第一个信号量

cmd : IPC_STAT 获取这个semnum编号信号量的结构

IPC_RMID 删除信号量对象

IPC_SETVAL 设置信号量的值 val , 初始化 SETVAL

IPC_GETVAL 获取信号量的值

arg : 信号量相关结构 变量 , union 复用

自定义复用类型: union semun

{

int val;

struct semid_ds *buf;

unsigned short *arry;

};

返回值: 成功 0; 失败 -1

函数原型:semop()

int semop(int semid, struct sembuf *sp, size_t ns);

semid : 信号量ID

sp : 结构体变量的地址

系统定义好的结构体

struct sembuf

{

short sem_num; 信号量的编号 第一个信号量 0

short sem_op ; -1 P操作 ; 1 V操作

short sem_flg: 一般 SEM_UNDO

};

ns : 操作个数 一般 1

返回值: 成功 0 ; 失败 -1

一般开发者会自定义信号量的相关函数,如下:

a) 初始化信号量 : 自定义一个初始化函数 init_sem()

int init_sem(int semid, int no, int value)

{

union semun a; // 复用变量

a.val = value; // 初值

if(semctl(semid, no, SETVAL, a) == -1) // 设置信号量的值

{

return -1;

}

else

{

return 0;

}

}

调用: init_sem(semid, 0, 1);

b) P操作: 自定义一个P操作函数

int p_sem(int semid, int num)

{

struct sembuf mybuf;

mybuf.sem_num = num;

mybuf.sem_op = -1;

mybuf.sem_flg = SEM_UNDO;

if(semop(semid, &mybuf, 1) < 0)

{

perror("semop");

exit(-1);

}

return 0;

}

c) V操作: 自定义一个V操作函数

int v_sem(int semid, int num)

{

struct sembuf mybuf;

mybuf.sem_num = num;

mybuf.sem_op = 1;

mybuf.sem_flg = SEM_UNDO;

if(semop(semid, &mybuf, 1) < 0)

{

perror("semop");

exit(-1);

}

return 0;

}

d) 删除信号量: 自定义一个删除信号量函数

int delete_sem(int semid,int no)

{

union semun a; // 复用变量

if(semctl(semid, no, IPC_RMID, a) == -1)

return -1;

else

return 0;

}

举例:

#include

#include

#include"mysem.h"

int main()

{

int semid;

pid_t id;

semid = semget(IPC_PRIVATE, 1, 0666); // 创建信号量

if(semid != -1)

{

printf("semget OK\n");

}

if(init_sem(semid, 0, 1) != -1) // 初始化信号量

{

printf("init_sem OK\n");

}

id = fork(); // 创建子进程

if(id == 0)

{

p_sem(semid, 0); // P操作

printf("child running\n");

sleep(2);

v_sem(semid, 0); // V操作

}

else if(id > 0)

{

sleep(1);

p_sem(semid, 0); //P操作

printf("father running\n");

v_sem(semid, 0); // V操作

delete_sem(semid, 0); // 删除信号量

}

}

第三类:socket套接口通信方式, 对于socket常用来实现网络中不同主机之间的进程间通信。这是另一个知识点,对于初学者只要掌握前5中通信方式,就能实现本机进程之间的通信。

文章源自华清远见嵌入式学院:http://www.embedu.org/
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
各种安全相关思维导图整理收集。渗透步骤,web安全,CTF,业务安全,人工智能,区块链安全,数据安全,安全开发,无线安全,社会工程学,二进制安全,移动安全,红蓝对抗,运维安全,风控安全,linux安全.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值