前言:
1、进程间的通信分为三类:
早期:无名管道(pipe)、有名管道(fifo)和信号(signal)
系统5 IPC对象:共享内存(share memory)、消息队列(message queue)和信号灯(semaphore)
网路:套接字(socket)
2、管道:
管道是Linux中 进程通信的一种方式,他把一个程序的输出,直接接到另外一个程序的输入中,Linux的管道通信主要分为两种,有名管道,无名管道,管道文件表示符 TT。
一、无名管道
1、无名管道的介绍:
无名管道是Linux通信的一种原始方式,具有以下特点:
(1)只能在具有亲缘关系的进程之间通信(父子进程或者兄弟进程)
(2)是单工的(一端收,一端发),具有固定的读端和写端
(3)管道也可以看成一种特殊的文件,对于他的读写也可以使用他的read(),write()等函数,但他不属于任何系统文件,是只存在于内存当中的。
模型:
pipe函数:
pipe:创建无名管道
#include <unistd.h>
int pipe(int pipefd[2]);
功能:
创建无名管道
参数:
pipefd[2]:整型数组
存放两个文件描述符:一个负责读 一个负责写
[0]--->读取
[1]--->写入
返回值:
成功返回0
失败返回-1,并设置错误码
2、测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
//制作无名管道需要用到整型数组 ,一个写,一个读
int pipefd[2];
//创建无名管道 ,返回值为0则创建成功
int ret=pipe(pipefd);
if(ret<0){
perror("pipe");
return -1;
}
printf("[0]:%d\n",pipefd[0]);
printf("[1]:%d\n",pipefd[1]);
//开出父子进程,父子进程依赖无名管道进行通信
pid_t pid=fork();//开出父子进程
if(pid>0){
//父进程的操作 :发送信息给子进程
char buf[50]="";
while(1){
//bzero 清空数组内存垃圾值
bzero(buf,sizeof(buf));
scanf("%s",buf);
write(pipefd[1],buf,strlen(buf));
}
}else if(pid==0){
//子进程的操作,读取无名管道中的内容
char buf[50];
while(1){
bzero(buf,sizeof(buf));
read(pipefd[0],buf,sizeof(buf));
printf("子进程收到:%s\n",buf);
}
}else{
perror("fork");
exit(-1);
}
return 0;
}
3、管道读写注意
当管道中无数据时,读操作会阻塞
向管道中写入数据时,
linux
将不保证写入的原子 性,管道缓冲区一有空闲区域,写进程就会试图
向管道写入数据。如果读进程不读走管道缓冲区 中的数据,那么写操作将会一直阻塞。
只有在管道的读端存在时,向管道中写入数据才 有意义。否则,向管道中写入数据的进程将收到
内核传来的
SIFPIPE
信号
(
通常
Broken pipe
错误
)
。
二、有名管道
1、有名管道特点
有名管道(FIFO)是对无名管道的改进,具有以下特点:
(1)实现两个互不相关进程之间的彼此通信。
(2)该管道可以通过路径来指出,并且在系统中是可见的,建立后两个进程就可以将其当作普通文件进行读写操作。
(3)FIFO严格遵循先进先出规则,不支持如lseek() 操作(定位)。
模型:
2、测试:
通信方1:
#include <stdio.h>
#include <sys/types.h>//mkfifo头文件
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[]){
if(argc!=2){
printf("User:%s <file_name> \n",argv[0]);
return 0;
}
//创建管道文件(利用某一个文件名只能创建一次的特性)
if(access(argv[1],F_OK)){ //判断argv[1]是否已经存在
if(mkfifo(argv[1],0666)){
perror("mkfifo");
exit(-1);
}
}
//2>利用有名管道进行进程间通信
int fd=open(argv[1],O_RDWR);
if(fd<0){
perror("open");
exit(-1);
}
//3>往有名管道里写入数据
char buf[50]="";
while(1){
bzero(buf,sizeof(buf));
scanf("%s",buf);
write(fd,buf,strlen(buf));
}
return 0;
}
通信方2:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[]){
if(argc!=2){
printf("User:%s <file_name> \n",argv[0]);
return 0;
}
//创建管道文件(利用某一个文件名只能创建一次的特性)
if(access(argv[1],F_OK)){ //判断argv[1]是否已经存在
if(mkfifo(argv[1],0666)){
perror("mkfifo");
exit(-1);
}
}
//2>利用有名管道进行进程间通信
int fd=open(argv[1],O_RDWR);
if(fd<0){
perror("open");
exit(-1);
}
//3>往有名管道里读取数据
char buf[50];
while(1){
bzero(buf,sizeof(buf));
read(fd,buf,sizeof(buf));
printf("%s\n",buf);
}
return 0;
}
3、 读取操作:
三、信号通信
1、概述:
信号是在软件层次上对中断机制的一种模拟,是 一种异步通信方式 。信号可以直接进行用户空间进程和内核进程之间 的交互,内核进程也可以利用它来通知用户空间 进程发生了哪些系统事件。
如果该进程当前并未处于执行态,则该信号就由 内核保存起来,直到该进程恢复执行再传递给它; 如果一个信号被进程设置为阻塞,则该信号的传 递被延迟,直到其阻塞被取消时才被传递给进程。
2、生存周期:
3、信号的响应:
(1)忽略信号:对信号不做任何处理,但是有两个信号不能 忽略:即SIGKILL及SIGSTOP。
(2)捕捉信号:定义信号处理函数,当信号发生时,执行相 应的处理函数。
(3)执行缺省操作:Linux对每种信号都规定了默认操作。
4、信号的检测于处理流程:
5、信号的种类:
常用信号:
1) SIGHUP ;--->终端在关闭时,给其下所有的进程发送信号 2) SIGINT :--->结束进程 就是:ctrl + c 3) SIGQUIT: --->结束进程 就是:ctrl + \ 9) SIGKILL: --->强制杀死进程 不能被注册,不能被忽略 --->signal 10) SIGUSR1:--->用户自定义信号 --->signal 12) SIGUSR2:--->用户自定义信号 --->signal 14) SIGALRM:-->闹钟信号,用来倒计时,当到0后,触发信号,结束进程 17) SIGCHLD:--->子进程结束时,给父进程发送的信号 18) SIGCONT:--->继续进程 19) SIGSTOP:--->暂停进程: 不能被注册,不能被忽略 --->signal 20) SIGTSTP:-->暂停进程: ctrl + z
6、发送信号:
1>发送信号
kill
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:
发送信号
参数:
pid:接收信号的进程PID:发送给谁
sig:发送的信号是什么
返回值:
成功返回0
失败返回-1,并设置错误码
2>函数转换类型:
atoi --->char型转int型
#include <stdlib.h>
int atoi(const char *nptr);
功能:
将接收到的char型转化为int型
参数:
nptr:接受到的char型
返回值:
成功返回转化后int型
失败返回-1,并设置错误码
从左往右进行转换的
7、信号处理:signal()函数:
信号注册:
signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:
信号注册
参数:
signum:需要操作的信号
handler:注册事件
SIG_IGN:忽略
SIG_DFL:默认:恢复
捕捉:注册:执行自己写的一个函数
typedef void (*sighandler_t)(int);--->函数指针
返回值:
成功返回sighandler_t类型
失败返回-1,并设置错误码\
8、定时器函数:
pause函数,alarm函数:
#include <unistd.h>
int pause(void);
功能:
将进程挂起(休眠等待唤醒)--->阻塞
唤醒:捕捉信号,执行该信号则解除挂起-->唤醒
参数:
void
返回值:
成功返回0
失败返回-1,并设置错误码
alarm
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:
倒计时(计时器)
当倒计时到0时,触发SIGALRM信号,结束进程
参数:
seconds:秒数
返回值:
第一次调用返回0
二次调用以上,返回剩余秒数
9、测试:代码:
当父进程接受到cltr+c操作时,子进程则打印:我们要去佤邦发财。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
//收到信号后执行的函数,通过信号注册传递
void showMsg(int sig){
printf("我们要去佤邦发财啦!\n");
}
//捕捉信号
void sendMsg(int sig){
kill(getppid(),SIGUSR1);
}
int main(){
//因为操作要开出父子进程:fork
pid_t pid=fork();
if(pid>0){
//父进程操作
signal(SIGUSR1,showMsg);
signal(SIGINT,SIG_IGN);
}else if(pid==0){
//子进程的操作
signal(SIGINT,sendMsg);
}
while(1){
//防止过快,显示,也可以用pause()函数
}
return 0;
}