进程间通信
无名管道(pipe)
无名管道只能继承,在进程中创建管道,一个进程写数据,一个进程读数据。
1、特点
(1)只能用于有亲缘关系的的进程间通信(无实际文件与其对应);
(2)单工通信模式,具有固定的读端和写端。
2、无名管道创建 - pipe
#include<unistd,h>
int pipe(int pfd[2]);
返回值:成功返回0,失败返回EOF
参数:pfd包括两个元素的整形数组,用来保存文件描述符,pfd[0]用于读管道,pfd[1]用于写管道。
例示 - 无名管道
子进程1和子进程2分别往管道中写入字符串,父进程读管道内容并打印。
#include<stdio.h.>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main(void){
pid_t pid1, pid2;
char buf[32]; //缓冲区
int pfd[2];
//先创建管道,在创建子进程
if(pipe(pfd) < 0){
perrpr("pipe");
exit(-1);
}
if((pid1 == fork()) < 0){
perror("fork");
exit(-1);
}else if(pid1 == 0){ //子进程1
strcpy(buf, I'm process1\n");
write(pfd[1], buf, 32);
exit(0);
}else{
if((pid2 == fork()) < 0){
perror("fork");
exit(-1);
}else if(pid2 == 0){ //子进程2
strcpy(buf, I'm process1\n");
write(pfd[1], buf, 32);
exit(0);
}else{
wait(NULL);//父进程
read(pfd[0], buf, 32);
printf("%s\n", buf);
wait(NULL);
read(pfd[0], buf, 32);
printf("%s\n", buf);
}
}
return 0;
}
3、读无名管道
(1)写端存在
至少有一个进程可以通过文件描述符写管道
1)有数据,read返回实际读取的字节数;
2)无数据,进程读阻塞。
(2)写端不存在
1)有数据,read返回都取自结束;
2)无数据,read返回0。
4、写管道
(1)读端存在
1)有足够空间
write返回实际写入的字节数
2)无足够空间/空间不足
原子操作,有多少写多少,阻塞到有空间写完
**如何获取无法管道大小?**
1)循环写入管道,知道阻塞;
2)统计循环次数。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
int cout = 0, pfd[2];
char buf[1024];
if(pipe(pfd) < 0){
perror("pipe\n");
exit(-1);
}
while(1){
printf("wrote %d kbyte \n", ++cout);
}
return 0;
}
(2)无读端
管道断裂!!!
如何验证管道断裂(进程被信号结束)
1)子进程写管道
2)父进程回收
.....
int main(){
pid_t pid;
int pfd[2];
char buf[32];
if(pipe(pfd) < 0){
perror("pipe");
exit(-1);
}
close(pfd[0]); //读端关闭
if((pid == fork()) < 0){
perror("fork");
exit(-1);
}else if(pid == 0){ //子进程
write(pfd[1], buf, 32);
exit(0);
}else{
wait(&status);
printf("status = %x\n", status);
}
return 0;
}
有名管道(fifo)
1、特点
(1)有一个实际文件(有名称,有路径)
(2)打开时可指定读写方式
(3)通过文件IO操作,内容存在内存中(读端写端都关闭时,管道内容被释放)
2、有名管道创建 - mkfifo
#include<unistd.h>
#include<fcntl.h>
int mkfifo(const char *path, mode_t mode);
返回值:成功返回0,失败返回EOF
参数:path创建的管道文件路径(如果没指明,只在当前目录下)
mode管道文件的权限,如0666
3、有名管道读写 - 示例
进程A:循环从键盘输入并写入有名管道mkfifo,输入quit时退出,
进程B:循环统计A每次写入字符串长度。
信号(signal)
1、特点
- 信号机制 - 内核产生的一种通信机制,是在软件层次上对中断机制的一种模拟,是一种异步通信。
补充:中断:外部设备需要被处理时,产生一个中断信号,cpu收到中断信号后,在执行当前指令响应处理中断,cpu返回继续执行指令。
-Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件。kill -l显示当前系统中所有支持的信号类型
-进程对信号有不同的响应方式
1)缺省方式:系统对每个信号都有缺省方式;
2)忽略信号:进程收到信号后不做处理;
3)捕捉信号(注册信号):设定信号处理函数,进程收到后,自动处理。
常用信号 | 默认操作 | |
---|---|---|
SIGHUP | 关闭终端时产生,通常发给所有相关进程 | 终止 |
SIGINT | 再键入INIR(crtl -c)时产生,发送给前台进程 | 终止 |
SIGQUIT | 与SIGINT类似,由QUIT产生 | 终止 |
SIGILL | 遇到非法指令 | 终止 |
SIGSEV(段错误) | 非法访问内核(野指针 空指针 数组越界) | 终止 |
SIGPIPE | 当进程往一个没有读端管道写入时产生代表管道断裂 | 终止 |
SIGKILL(高级别,不能设置) | 用来结束进程 | 终止 |
SIGSTOP(高级别) | 暂停进程 | 暂停进程 |
SIGSTP | ctrl-z发出 | 暂停进程 |
SIGCONT | 信号让进程进入运行太=态 | 继续运行 |
SIGALRM | 用于通知进程定时器时间已到 | 终止 |
SIGUSR1/2 | 保留用户程序使用 | 终止 |
2、信号相关命令
(1)kill [-signal] pid 向一个进程发信号(进程号,进程组号)
- sig 默认发送 -15 终止进程
- pid 指定发送对象
kill -9 -1 发送给所有进程(结束)
kill -9 6437 发送给6437进程
-9 8126 向租号8126的进程组发送
(2)killall [-u user |prog] 会检查权限,防止结束别人进程
- prog 制定进程名
- user 指定用户名
killall a.out 默认发送15
killall -u linux
(3)信号发送 - kill/raise, alarm/pause
#include<unistd.h>
#include<signal.h>
int kill(pid_t pid,int sig);
int raise(int sig);
返回值:成功返回0,失败返回EOF;
参数pid接受进程的进程号,0代表同组进程,-1代表所有进程。
sig信号类型
int alarm(unsigned int seconds);//定时器
成功返回上个定时器的剩余时间,失败返回EOF,Linux系统只有一个定时器
参数:second定时器的时间,单位秒
int pause(void);
进程一直阻塞,直到被信号打断
返回值:被信号中断返回-1,errno为EINTR
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
alarm(3); //指定3秒后闹钟时间到
pause(); //睡眠
printf("I have been wake up\n");
return 0;
}
(4)设置信号响应方式 - signal
#include<unistd.h>
#include<signal.h>
void (*signal(int signo, void (* handler)(int)))(int);
参数:signo要设置的信号类型
第二个函数指针:存放某个函数入口地址,选定义一个信号处理函数
返回值:成功返回原先的信号处理函数,失败返回SIG_ERR
例
void handler(int signo){
if(signo == SIGINT){
printf("I habe got SIGINT!\n");
}else if(signo == SIGQUIT ){
printf("i HAVE GOT SIGQUIR\N")
}
}
int main(){
signal(SIGINT,handler);
signal(SIGQUIT, handler);
while(1)
pause();
return 0;
}