前言
1、通常来说进程间的通信方式共有六种:
(1)管道通信 ——无名管道 (pipe)、有名管道 (fifo)
(2)信号通信 (sinal)
(3)信号量 (Semaphore)
(4)共享内存 (SharedMemory)
(5)消息队列 (MessageQueue)
(6)套接字 (Socket)
但是本文只介绍前五种,你肯定疑惑了,第六种为什么不说?
那原因也很简单,我觉得文章太长了!
一、管道通信
1、无名管道
1、无名管道的特点:
(1)只能用于具有亲缘关系的进程之间的通信
(2)是一个单工的通信模式,具有固定的读端和写端
(3)管道也可以看成一种特殊文件,对他的读写可以使用read(),write()函数
2、原理示意图:
3、pipe函数
功能:创建无名管道
头文件:
#include<unistd.h>
函数原型:
int pipe(int fd[]);
参数:
fd[]:包含两个元素的整型数组,存放读端和写端的文件描述符
例子:
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd[2];
pipe(fd); //传出两个文件描述符 fd[0] fd[1]
int pid = fork();
if(pid == 0)//子进程
{
close(fd[0]); //子进程关闭读端
write(fd[1], "hello", sizeof("hello"));
}
else if(pid > 0)//父进程
{
char buf[100] = { 0 };
close(fd[1]);//父进程关闭写端
wait(NULL);//等待子进程结束
read(fd[0], buf, sizeof(buf));//读出数据
printf("read is %s\n", buf);//打印
}
}
2、有名管道
1、有名管道的特点:
(1)可以使两个不相关的进程实现彼此通讯
(2)该管道可以通过路径名指出,在文件系统中可见
(3)严格遵守先进先出的规则,在开始处读数据,末尾处写数据
2、原理示意图:
3、mkfifo函数
功能:创建有名管道
头文件:
#include<sys/types.h>
#include<sys/state.h>
函数原型:
int mkfifo(const char *filename,mode_t mode);
参数:
filename:要创建的管道名
mode:管道的访问权限
例子:
//读端
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
mkfifo("myfifo", 0666);
int fd = open("myfifo", O_RDWR);
if(fd > 0)
{
char buf[100] = { 0 };
read(fd, buf, sizeof(buf));
printf("read is %s\n", buf);
close(fd);
}
}
//写端
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open("myfifo", O_RDWR);
if(fd > 0)
{
write(fd, "hello", sizeof("hello"));
close(fd);
}
}
二、信号通信
1、信号是进程间通信机制中唯一的异步通讯机制
2、进程可以通过三种方式来响应信号
(1)忽略信号:对信号不做如何处理
(2)捕捉信号:定义信号处理函数,信号发生时,执行相应的处理函数
(3)执行默认操作:常见的信号如下
· SIGHUP:从终端上发出的结束信号。
· SIGINT:来自键盘的中断信号,即Ctrl+c。
· SIGKILL:该信号结束接收信号的进程。
· SIGCHLD:标识子进程停止或结束的信号。
· SIGSTOP:来自键盘Ctrl+z,或调试程序的停止执行信号。
· SIGQUIT:来自键盘的退出信号,即Ctrl+\。
· SIGALARM:进程的定时器到期,发送该信号。
3、kill函数
功能:发送信号给进程
头文件:
#include<signal.h>
#include<sys/types.h>
函数原型:
int kill(pid_t pid,int sig);
参数:
pid:发送信号给进程号为pid的进程
sig:信号类型
例子:
#include <signal.h>
int main()
{
kill(11111, SIGINT);
}
4、signal函数
功能:定义一个函数,当产生某个信号时,自动执行此函数
头文件:
#include<signal.h>
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
参数:
signum:指定信号代码
handler:SIG_IGN 忽略该信号
SIG_DFL 采用系统默认方式处理信号
自定义的信号处理函数
例子:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void *fun(int n) //n 当产生某个信号时,信号值
{
printf("recv a signal CTRL+C %d\n", n);
exit(0);
}
int main()
{
signal(SIGINT, fun);
while(1); //不让程序结束
}
5、alarm函数
功能:在进程中设定一个定时器,当定时器时间到了,发送一个SIGALARM信号给进程
头文件:
#include<unistd.h>
函数原型:
unsigned int alarm(unsigned int seconds);
参数:
seconds:指定秒数
例子:
//发送信号
#include <stdio.h>
int main()
{
alarm(2);
while(1)
{
printf("hello world\n");
sleep(1);
}
}
//接收信号
#include <stdio.h>
#include <signal.h>
void fun(int n)
{
printf("recv a signal SIGALRM\n");
}
int main()
{
alarm(1); //1秒钟之后产生SIGALRM信号
signal(SIGALARM, fun); //捕获SIGALRM信号,执行fun
while(1); //不让程序暂停
}
三、信号量
那个,这个暂时先放一放。
四、共享内存
1、共享内存是一种最为高效的进程间通信方式:因为进程可以直接读写内存,不需要任何数据的复制
2、原理图:
3、shmget函数
功能:创建共享内存
头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
int shmget(key_t key,int size,int shmflg);
参数:
key:共享内存的键值
size:共享内存区的大小
shmflg:共享内存的权限,可以使用八进制表示法
返回值:
共享内存的标识符
例子:
#include <sys/shm.h>
#include <stdio.h>
int main()
{
int shmid = shmget(55555, 10 * sizeof(int), 0666 | IPC_CREAT);//IPC_CREAT不存在就新建
printf("shmid is %d\n", shmid);
}
4、shmat函数
功能:映射共享内存
头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
char *shmat(int shmid,const void *shmaddr,int shmflg);
参数:
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为0,表示系统自动分配地址,并把该段共享内存映射到调用进程的地址空间)
shmflg:共享内存的权限,可以使用八进制表示法
返回值:
被映射的段地址
例子:
int *p = shmat(shmid, NULL, 0);
5、shmdt函数
功能:撤销映射
头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
int shmdt(const void *shmaddr);
参数:
shmaddr:被映射的共享内存段地址
例子:
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int shmid = shmget(IPC_PRIVATE, 10 * sizeof(int), 0644);
printf("shmid %d\n", shmid);
int i;
int pid = fork(); //创建子进程
if(pid == 0) //子进程在运行
{
//写共享内存
int *p = shmat(shmid, NULL, 0); //映射
for(i = 0; i < 10; i++)
{
p[i] = i; //写入共享内存
}
shmdt(p); //解除映射
exit(0); //子进程结束
}
else if(pid > 0)
{
//读共享内存
wait(NULL); //等子进程结束
int *p = shmat(shmid, NULL, 0); //映射
for(i = 0; i < 10; i++)
{
printf("%d\n", p[i]); //读出
}
shmdt(p); //解除映射
}
}
五、消息队列
1、消息队列就是一些消息的列表,可以实现消息的随机查询,这些消息存在于内核中,有队列ID来标识。
2、msgget函数
功能:
头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgget(key_t key,int msgflg);
参数:
key:消息队列的键值
msgflg:权限标志位
返回值:
消息队列ID
3、msgsnd函数
功能:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
参数:
msqid:消息队列的队列ID
msgp:指向消息结构的指针
struct msgbuf
{
long mtype;//消息类型
char mtext[100];//消息正文
}
msgsz:消息正文的字节数
msgflg:IPC_NOWAIT 消息无法发送函数立即返回
0 调用阻塞直到发送成功
4、msgrcv函数
功能:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgrcv(int msqid,const void *msgp,size_t msgsz,long int msgtyp,int msgflg);
参数:
msqid:消息队列的队列ID
msgp:消息缓冲区
struct msgbuf
{
long mtype;//消息类型
char mtext[100];//消息正文
}
msgsz:消息正文的字节数
msgtyp:0 接收消息队列第一个消息
大于0 接收消息队列第一个类型为msgtyp的消息
小于0 接收消息队列中第一个类型值不小于msgtyp绝对值且类型值又最小的消息
msgflg:IPC_NOWAIT 若消息队列没有对应类型的消息,函数立即返回
0 调用阻塞直到接收成功
5、msgctl函数
功能:删除队列消息
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
参数:
msqid:消息队列的队列ID
cmd:IPC_RMID 从系统内核中删除消息队列
buf:通常为NULL
6、例子
//发送消息
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
struct msgbuf
{
long mtype;
char mtext[100];
};
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("param error, like this: ./send 1 hello\n");
return -1;
}
struct msgbuf a;
a.mtype = atoi(argv[1]);//接收消息类型
strcpy(a.mtext, argv[2]);//接收消息正文
int msgid = msgget(11111, 0666 | IPC_CREAT);//创建消息队列
msgsnd(msgid, &a, sizeof(a) - sizeof(long), 0);//发送消息
}
//接收消息
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdio.h>
struct msgbuf
{
long mtype;
char mtext[100];
};
int main(int argc, char *argv[])
{
struct msgbuf b;//定义结构体变量
if(argc < 2)
{
printf("param error, like this: ./recv 1\n");
}
int msgid = msgget(11111, 0666 | IPC_CREAT);//创建消息队列
msgrcv(msgid, &b, sizeof(b) - sizeof(long), atoi(argv[1]), 0);//接收消息正文
printf("msg type is %lu, buf is %s\n", b.mtype, b.mtext);//打印
}
总结
总结不动了!