目录
提醒:Linux之进程间通信(二):共享内存、信号灯集、消息队列
1.进程间通信方式
1)早期的进程间通信:无名管道、有名管道、信号
2)systerm V IPC:共享内存、消息队列、信号灯集
3)BSD:套接字
2.无名管道
特点
1.只能用于具有亲缘关系的进程之间的通信
2.半双工的通信模式,具有固定的读端和写端
3.管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数.
4.管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道。
函数接口
函数
int pipe(int fd[2])
功能:创建无名管道
参数:文件描述符 fd[0]:读端 fd[1]:写端
返回值:成功 0
失败 -1
代码演示
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd[2]={0};//定义一个数组,用于存储管道的两个文件描述符f[0]读端、f[1]写端。
if(pipe(fd)<0)//判断创建无名管道是否成功。
{
perror("pipr errpr\n");
return -1;
}
printf("%d %d\n",fd[0],fd[1]);
return 0;
}
注意事项
1.当管道中无数据时,读操作会阻塞
2.管道中装满(管道大小64K)数据写阻塞,一旦有4k空间,写继续
3.只有在管道的读端存在时,向管道中写入数据才有意义。否则,会导致管道破裂,向管道中写入数据的进程将收到内核传来的SIGPIPE信号 (通常Broken pipe错误)。
代码演示
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd[2]={0};
char buf[65536]="";
if(pipe(fd)<0)
{
perror("pipe error");
return -1;
}
write(fd[1],buf,65536);
printf("full\n");
//当管道中至少有4k空间才能继续写入东西
read(fd[0],buf,4096);
write(fd[1],"a",1);
printf("aaaaaaaaaaaa\n");
/*
read(fd[0],buf,5);
printf("buf:%s\n",buf);
read(fd[0],buf,5);//阻塞,管道中没有内容
printf("buf:%s\n",buf);*/
return 0;
}
3.有名管道
特点
1.有名管道可以使互不相关的两个进程互相通信。
2.有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中。
3.进程通过文件IO来操作有名管道
4.有名管道遵循先进先出规则,不支持如lseek() 操作
函数接口
int mkfifo(const char *filename,mode_t mode);
功能:创健有名管道
参数:filename:有名管道文件名
mode:权限
返回值:成功:0
失败:-1,并设置errno号
注意对错误的处理方式:
如果错误是file exist时,注意加判断,如:if(errno == EEXIST)
代码演示
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
if(mkfifo("./fifo",0666)<0)
{
if(errno==EEXIST)//如果有名管道文件已存在,继续运行。
printf("fifo file exist\n");
else
{
perror("mkfifo error\n");
return -1;
}
}
printf("success\n");
int fd = open("./fifo",O_RDWR);
return 0;
}
注:函数只是在路径下创建管道文件,往管道中写的数据依然写在内核空间。
注意事项
1.只写方式,写阻塞,一直到另一个进程把读打开
2.只读方式,读阻塞,一直到另一个进程把写打开
3.可读可写,如果管道中没有数据,读阻塞
4.信号
概念
1)信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
2)信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
3)如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
信号的响应方式
1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
3)执行缺省操作:Linux对每种信号都规定了默认操作
信号种类:
kill -l命令查看信号
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42)SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45)SIGRTMIN+11 46)SIGRTMIN+12 47)SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52)SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57)SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62)SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
常用命令
9)SIGKILL:结束进程,不能被忽略不能被捕捉
19)SIGSTOP:结束进程,不能被忽略不能被捕捉
17)SIGCHLD:子进程退出时给父进程发的信号
2)SIGINT:结束进程,对应快捷方式ctrl+c
20)SIGTSTP:暂停信号,对应快捷方式ctrl+z
3)SIGQUIT:退出信号,对应快捷方式ctrl+\
26)SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。
15)SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号
代码演示
alarm()函数用法
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//多次调用alarm,时间根据最后一次定时发送信号
printf("%d\n",alarm(3));//第一次调用返回值为0
sleep(1);
printf("%d\n",alarm(5));//第二次调用返回值为上一次定时所剩时间。
while(1);
return 0;
}
函数接口
int kill(pid_t pid, int sig);
功能:信号发送
参数:pid:指定进程
sig:要发送的信号
返回值:成功 0 失败 -1
int raise(int sig);
功能:进程向自己发送信号
参数:sig:信号
返回值:成功 0 失败 -1
unsigned int alarm(unsigned int seconds)
功能:在进程中设置一个定时器
参数:seconds:定时时间,单位为秒
返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则
返回上一个闹钟时间的剩余时间,否则返回0。
注意:一个进程只能有一个闹钟时间。如果在调用alarm时
已设置过闹钟时间,则之前的闹钟时间被新值所代替
int pause(void);
功能:用于将调用进程挂起,直到收到信号为止。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:信号处理函数
参数:signum:要处理的信号
handler:信号处理方式
SIG_IGN:忽略信号
SIG_DFL:执行默认操作
handler:捕捉信号 void handler(int sig){} //函数名可以自定义
返回值:成功:设置之前的信号处理方式
失败:-1
typedef unsigned int INT;
typedef void (*sighandler_t)(int); ==> typedef void (*)(int) sighandler_t;
sighandler_t signal(int signum, sighandler_t handler);
||
sighandler_t signal(int signum, void (*handler)(int) );
||
void (* signal(int signum, void (*handler)(int) ))(int);
变量:数据类型 变量名
函数指针: int (*p)(int); ==> int (*)(int) p;
代码演示
kill(),raise() 函数:系统自动发送
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, const char *argv[])
{
// kill(getpid(),SIGINT);//获取进程号,并给该进程号发送SIGINT信号。
raise (SIGINT);//给该进程给该进程号发送SIGINT信号。
while(1);
return 0;
}
signal()函数:从终端输入
#include <stdio.h>
#include <signal.h>
void handler_sig(int sig);
int main(int argc, const char *argv[])
{
//signal(SIGINT,SIG_IGN);//忽略信号
// signal(SIGINT,SIG_DFL);//执行默认操作
signal(SIGINT,handler_sig);//捕获信号
while(1);
return 0;
}
void handler_sig(int sig)
{
printf("hello world\n");
}