进程间通信:一共6种 ,信号、信号量、管道(只能亲缘进程间通信)(匿名管道 pipe、命名管道mkfifo)、消息队列、共享内存(效率最高)、套接字(网络)。
信号:(一般不可靠)
(
signal)
是一种处理异步事件的方式
1.信号的概念:
信号是软件层面的中断,信号的响应依赖于中断
并发:
同步
异步:异步时间的处理:查询法,通知法
信号量:(
Semaphore)
进程间通信处理同步互斥的机制
。是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。 例如互斥锁 pthread_mutex_t
2.相关命令:kill -l 查看所有的信号
1-31的为标准信号
34及以后的为实时信号
3.signal函数
signal(信号no,注册行为的函数):给一个指定的信号注册一个行为
信号no:可以使用宏值,也可以使用宏,见kill -l列表
注册行为的函数:可以是SIG_IGN(忽略)
也可以是SIG_DEL(默认行为)
或者就是一个函数的入口地址
信号会打断阻塞的系统调用
void(*signal(int signum,void(*func)(int))(int);
4.信号的行为是不可靠的
第一个信号还未响应完毕,又来了另一个相同的信号,那么信号处理的行为将会不可靠
5.可重入的函数
在信号处理中,第一次调用未结束,发生第二次信号调用处理函数,那么程序不出任何的问题,
代表该函数是可重入的,所有的系统调用都是可重入的,一部分库函数是可重入的,
如:rand是不可重入的,但提供了一个rand_r
6.常用函数:
kill(pid,sig):给进程发送信号,成功返回0,失败返回-1,并设置errno
alarm(秒数):隔多少秒之后发送一个SIGALRM信号
pause():会令目前的进程进入睡眠状态,直到收到信号中断
练习:做一个程序,让其跑5秒钟,做变量累加,查看结果
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
static long i=0;
void fun_hander(int s)
{
printf("%ld\n",i);
exit(1);
}
int main()
{
// long i=0;
alarm(5);//用闹钟定时5s
signal(SIGALRM,fun_hander);//利用signa函数重新定位到fun_hander函数当中
while(1)
{
i++;
}
//printf("%ld\n",i);
}
1.打开文件,可能打开失败,判断假错,如果是信号打断的,需要从新再执行打开
2.读文件写文件
流量控制
令牌桶
管道:
匿名管道:
pipe(管道文件描述符数组):创建一个管道文件,拥有读写两端
管道文件描述符数组:0下标是读端,1下标是写端
用管道实现进程间通信,父子进程都会拿到读写两端,使用什么端就关闭另一端
管道是一个单工的
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 1024
int main(void)
{
int arr[2];
pid_t pid;
char buf[BUFSIZE];
pipe(arr);//创建一个管道
pid = fork();//创建一个进程
if(pid < 0)
{
perror("fork()");
exit(1);
}
if(pid == 0)
{
//child
close(arr[1]);//关闭数据写端
read(arr[0],buf,BUFSIZE);//读取管道数据
puts(buf);//打印获得数据
close(arr[0]);
exit(0);
}else//父进程给子进程发消息
{
//parent
close(arr[0]);//关闭管道读端
write(arr[1],"hello",6);//数据写入管道写端
close(arr[1]);//写完关闭管道写端
wait(NULL);//爸爸等着给儿子收尸
}
exit(0);
}
mpg123 -:从文件或标准输入中获取数据解码播放
cat *.mp3 | mpg123 -
共享内存ipcs:
ipcs:查看当前进程中所有ipc对象
ipcrm :删除一个ipc对象
-m:共享内存
-q:消息队列
共享内存对象都有一个id,是唯一标识符
有一个关联的key值,其余进程通过key获取到共享内存的id号
ftok(路径,proj_id):成功时返回一个合法的key值,失败时返回-1
路径:必须存在且可以访问的文件的路径
proj_id:
用于生成key的数字,会使用其低八位来与文件路径的inode运算,不能为0,一般给一个字符常量
简单实例:
key_t key;//声明一个key值
if((key = ftok(".",'a')) < 0)//计算出key值 file to key
{
perror("ftok()");
exit(1);
}
共享内存使用步骤:
1.创建/打开共享内存
shmget(key值,大小,shmflg):成功返回共享内存的id,失败时返回-1并设置errnokey值可以指定为:IPC_PRIVATE:就是0值,代表私有私有的只有拥有亲缘关系的进程才会访问得到,其他进程无法访问。
shmflg:共享内存标志位:IPC_CREAT|0666,私有的不用或上IPC_CREAT多个进程使用共享内存通信,都会调用该函数,第一个调用的负责创建,其余的负责根据key值找到共享内存的路径返回其id
2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
shmat(要映射的共享内存id,映射后的地址,标志位):成功时返回映射后的地址,失败返回(void *)-1映射后的地址:可以自己指明一块地址(自己创建比较麻烦),NULL表示系统自动映射标志位:为0表示可读可写,SHM_RDONLY表示只读
3.读写共享内存
通过读写映射后的地址
4.撤销共享内存映射,不使用内存共享时应撤销映射
shmdt(共享内存映射的地址):成功返回0,失败返回-1
5.删除共享对象
shmctl(共享内存的id,cmd,NULL):删除共享内存段cmd:IPC_RMID,删除
每块共享内存大小有限制
ipcs -l可以查看
cat /proc/sys/kernel/shmmax :可以查看或修改共享内存的大小
简单实例
shm.h
#ifndef SHM_H__
#define SHM_H__
#define DATASIZE 1024
typedef struct
{
int flag;// 0:read 1:write
char data[DATASIZE];
}msg_t;
#endif
shma.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm.h"
#define SHMSIZE 1024
int main(void)
{
key_t key;//声明一个key值
int shmid;
msg_t *msg;
//msg.addr = NULL;
//1.计算出一个key
if((key = ftok(".",'a')) < 0)//计算出key值 file to key
{
perror("ftok()");
exit(1);
}
//2.创建一段共享内存
if((shmid = shmget(key,SHMSIZE,IPC_CREAT|0666)) < 0)//创建一端共享内存空间
{
perror("shmget()");
exit(1);
}
//映射到该地址 msg_t *msg
if((msg = shmat(shmid,NULL,0)) == (void *)-1)//映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
{
perror("shmat()");
exit(1);
}
msg->flag = 1;
//write
while(1)
{
if(msg->flag)
{
fgets(msg->data,SHMSIZE,stdin);//将数据写到标准输入流当中
msg->flag = 0;
}
}
if(shmdt(msg) < 0)//撤销共享内存映射,不使用内存共享时应撤销映射
{
perror("shmdt()");
exit(1);
}
if(shmctl(shmid,IPC_RMID,NULL) < 0)删除共享对象
{
perror("shmctl()");
exit(1);
}
exit(0);
}
shmb.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm.h"
#define SHMSIZE 1024
int main(void)
{
key_t key;
int shmid;
msg_t *msg;
if((key = ftok(".",'a')) < 0)
{
perror("ftok()");
exit(1);
}
if((shmid = shmget(key,SHMSIZE,IPC_CREAT|0666)) < 0)
{
perror("shmget()");
exit(1);
}
if((msg = shmat(shmid,NULL,0)) == (void *)-1)
{
perror("shmat()");
exit(1);
}
//read
while(1)
{
if(!(msg->flag))
{
fputs(msg->data,stdout);
msg->flag = 1;
}
}
if(shmdt(msg) < 0)
{
perror("shmdt()");
exit(1);
}
if(shmctl(shmid,IPC_RMID,NULL) < 0)
{
perror("shmctl()");
exit(1);
}
exit(0);
}
消息队列:
1.打开消息队列:
msgget(key,msgflg):成功返回消息队列id,失败时返回-1key:和消息队列所关联的key值msgflg:标志位,IPC_CREAT|0666
2.向消息队列发送消息:msgsnd(消息队列id,发送消息的地址,消息的大小):成功返回0,失败返回-1标志位:0或IPC_NOWAIT,0代表发送成功该函数才返回,另是不管成功与否
消息格式:双方约定消息的格式,采取结构体形式首成员必须为 long,代表消息的类型,接受者根据类型接受指定的数据包后面的成员都为数据成员,真正发送的消息应该要减去long成员
3.从消息队列接收消息:msgrcv(消息队列的id,接收到的消息存放处,指定要接收的消息长度,指定接收的消息的类型(对应发送时的设置),标志位):成功返回成功收到的消息长度,失败返回-1标志位:0或IPC_NOWAIT,0代表发送成功该函数才返回,另是不管成功与否
4.删除消息队列:
msgctl(消息队列id,IPC_RMID,NULL):删除消息队列,成功返回0,失败返回-
简单实例:
msg.h
#ifndef _MSG_H
#define _MSG_H
#define DATASIZE 1024
typedef struct
{
long mtype;//首成员必须为long,代表消息的类型,接受者根据类型接受指定的数据包
char data[DATASIZE];
}msg_t;
#endif
msga.c
#include"msg.h"
///
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include <sys/wait.h>
#include<signal.h>
#include<string.h>
#define BUFSIZE 1024
static int flag =1;
void kill_handle(int s)
{
flag=0;
}
int main(void)
{
key_t key;
msg_t smsg,rmsg;
pid_t pid;
key=ftok(".",'a');
if(key<0)
{
perror("ftok()");
exit(1);
}
//1.打开消息队列:
int id= msgget(key,IPC_CREAT|0666);
if(id<0)
{
perror("msgget()");
exit(1);
}
pid=fork();
if(pid<0)
{
perror("fork()");
exit(0);
}
if(pid==0)//子进程接收另一分文件发送过来的消息
{
signal(SIGKILL,kill_handle);
while(flag)
{
// 3.从消息队列接收消息:
msgrcv(id,&rmsg,BUFSIZE,200,0);
printf("%s",rmsg.data);
}
exit(1);
}
else
{
while(1)
{
smsg.mtype=100;
fgets(smsg.data,DATASIZE,stdin);
//2.向消息队列发送消息:
msgsnd(id,&smsg,DATASIZE,0);
if(strncmp(smsg.data,"exit",4)==0)
{
//strcp();
kill(pid,SIGKILL);
break;
}
}
}
//4.删除消息队列:
msgctl(id,IPC_RMID,NULL);
exit(0);
}
msgb.c
#include"msg.h"
///
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include <sys/wait.h>
#include<string.h>
static int flag=1;
#define BUFSIZE 1024
void kill_handle(int s)
{
flag=0;
}
int main(void)
{
key_t key;
msg_t rmsg,smsg;
pid_t pid;
char buf[BUFSIZE];
key=ftok(".",'a');
if(key<0)
{
perror("ftok()");
exit(1);
}
int id= msgget(key,IPC_CREAT|0666);
if(id<0)
{
perror("msgget()");
exit(1);
}
pid=fork();
if(pid<0)
{
perror("fork()");
exit(0);
}
if(pid==0)
{
signal(SIGKILL,kill_handle);
while(flag)
{
smsg.mtype=200;
fgets(smsg.data,DATASIZE,stdin);
msgsnd(id,&smsg,DATASIZE,0);
}
exit(1);
}else{
while(1)
{
//msg.mtype=100;
//fgets(msg.data,DATASIZE,stdin);
msgrcv(id,&rmsg,BUFSIZE,100,0);
if(strncmp(rmsg.data,"exit",4)==0)
{
kill(pid,SIGKILL);
break;
}
printf("%s",rmsg.data);
}
}
msgctl(id,IPC_RMID,NULL);
wait(NULL);
exit(0);
}