传统进程间通信
通过文件实现进程间通信 必须人为保证先后顺序 A--->硬盘---> B(B不知道A什么时候把内容传到硬盘中)
1.无名管道
2.有名管道
3.信号
IPC进程间通信
4.消息队列
5.共享内存
6.信号灯集
7.socket通信
一、无名管道(亲缘关系的进程 64K)
原理:
如果A和B进程想要通过无名管道通信,那就必须在内核空间创建一个无名管道(64K),A和B进程必须是亲缘关系的进程,A进程向管道的一端写数据,B进程可以从管道的另外一端读数据。在A进程和B进程进行数据传输的时候是不允许使用lseek函数的。无名管道是半双工的通信方式。如果A进程一直向管道中写数据写满64K的时候A进程阻塞,直到B进程读一部分数据之后A才能继续写。如果B进程在读数据的时候,无名管道是空的,B进程阻塞。(PS:使用无名管道时,不会创建管道文件,使用有名管道文件,会创建)
API
int pipe(int pipefd[2]);
功能:创建一个无名管道
参数:
@pipefd[2]:返回的管道的两端
pipefd[1]:写端
pipefd[0]:读端
返回值:成功返回0,失败返回-1置位错误码
实例
#include <head.h>
// 子进程从终端读取字符串 通过无名管道 父进程读 然后打印到终端
int main(int argc, const char *argv[])
{
int pipefd[2];
if (-1 == pipe(pipefd))
{
perror("pipe error");
exit(-1);
};
char buff[128] = {0};
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
// 子进程
close(pipefd[0]); // 关闭子进程的读端 因为父子进程同时各自拥有读和写 把对应的关一个
while (1)
{
memset(buff, 0, sizeof(buff));
fgets(buff, sizeof(buff), stdin); // 从终端读取数据
if ((buff[strlen(buff) - 1]) == '\n')
{
buff[strlen(buff) - 1] = '\0';
}
write(pipefd[1], buff, sizeof(buff)); // 将读到的写入到管道
if (strcmp(buff, "quit") == 0)
{
break;
}
}
close(pipefd[1]);
exit(0);
}
else
{
// 父进程
close(pipefd[1]); // 关闭父进程的写端
while (1)
{
memset(buff, 0, sizeof(buff));
read(pipefd[0], buff, sizeof(buff)); // 父进程从管道中读
printf("父进程data:%s\n", buff);
if (strcmp(buff, "quit") == 0)
{
break;
}
}
close(pipefd[2]);
wait(NULL);
}
return 0;
}
结果
二、有名管道(任意进程间的通信 64K)
有名管道可以实现任意进程间的通信,有名管道的大小也是64K,有名管道也是不支持lseek,有名管道也是半双工的通信方式。有名管道创建之后会在用户空间产生一个管道文件(p),这个管
道文件是在内存上存储的。如果A和B两个进程想要通过有名管道通信,就打开管道文件,向管道中写向管道中读就可。
API
int mkfifo(const char *pathname, mode_t mode);
功能:创建有名管道
参数:
@pathname:有名管道文件的路径及名字
@mode: 管道文件的权限(mode & ~umask)
返回值:成功返回0,失败返回-1置位错误码
实例
01mkfifo.c
#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc,const char * argv[])
{
//创建管道文件
if(mkfifo(FIFO_PATH,0666)==-1){
perror("mkfifo error");
exit(-1);
}
//阻塞等待读写
getchar();
//删除管道文件
char buff[128]={0};
snprintf(buff,sizeof(buff),"rm -rf %s",FIFO_PATH);
system(buff);
return 0;
}
02read.c
#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc, const char *argv[])
{
int fd;
char buff[128] = {0};
if (-1 == (fd = open(FIFO_PATH, O_RDONLY)))
{
perror("open error");
exit(-1);
}
while (1)
{
memset(buff, 0, sizeof(buff));
read(fd, buff, sizeof(buff));
printf("read:[%s]\n", buff);
if (strcmp(buff, "quit") == 0)
{
break;
}
}
close(fd);
return 0;
}
03write.c
#include <head.h>
#define FIFO_PATH "./myfifo"
int main(int argc, const char *argv[])
{
int fd;
char buff[128] = {0};
if (-1 == (fd = open(FIFO_PATH, O_WRONLY)))
{
perror("open error");
exit(-1);
}
while (1)
{
memset(buff, 0, sizeof(buff));
printf("input:");
fgets(buff,sizeof(buff),stdin);
if(buff[strlen(buff)-1]=='\n'){
buff[strlen(buff)-1]='\0';
}
write(fd, buff, sizeof(buff));
if (strcmp(buff, "quit") == 0)
{
break;
}
}
close(fd);
return 0;
}
结果
三、信号
信号软件层面对中断一种模拟,用户可以给进程发送信号,内核也可以给进程发送信号,进程对信号的响应方式有三种:捕捉,默认,忽略
17号进程 SIGCHLD :当子进程退出的时候,父进程会收到这个信号
注:在所有的信号中,只有SIGKILL/SIGSTOP两个信号不能被捕捉,也不能被忽略。只能执行默认的动作。
killall a.out 给所有a.out命名的进程发送信号(停止态的a.out不会结束)
kill -信号号 pid 给进程发送信号
htop 给进程发送信号
API
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:指定进程对某个信号的响应方式
参数:
@signum:信号号
@handler:处理方法
SIG_IGN :忽略
SIG_DFL:默认
自己实现信号处理函数 :捕捉
void signal_handle(int signo)
{
//信号处理函数
}
返回值:成功返回handler,失败返回SIG_ERR,置位错误码
int raise(int sig);
功能:给自己发信号
参数:
@sig:信号号
返回值:成功返回0,失败返回非0
int kill(pid_t pid, int sig);
功能:给指定pid的进程发送信号
参数:
@pid:进程号
pid > 0 :给pid号的进程发信号
pid = 0 :给同组的进程发送信号
pid = -1:给所有有权限的进程发送信号(PS:慎用 ssh也会被杀死)
pid <-1:首先会对pid取绝对值,给和这个绝对值相同的组的进程发送信号
@信号号
返回值:成功返回0,失败返回-1置位错误码
unsigned int alarm(unsigned int seconds);
功能:当seconds倒计时为0的时候发送SIGALRM信号
参数:
@seconds:秒钟数,如果填写为0,取消挂起的信号
返回值:如果alarm是第一次调用,返回0.
如果alarm不是第一次调用,返回上一次调用的剩余秒钟数
alarm(5); //返回值是0
sleep(2); //延时2s
alarm(5); //返回值是3
//7秒后发出信号 第一次等了2秒 还剩3秒 又调用alarm(5)恢复到5秒 2+5=7
实例
#include <head.h>
void signal_handle(int signo){
printf("收到了ctrl+c的信号\n");
}
int main(int argc, const char *argv[])
{
// 1.忽略 SIG_IGN
// if(SIG_ERR==signal(SIGINT,SIG_IGN)){
// perror("signal error");
// exit(-1);
// }
// 2.默认 SIG_DFL
// if(SIG_ERR==signal(SIGINT,SIG_DFL)){
// perror("signal error");
// exit(-1);
// }
// 3.捕捉 SIGINT
if (SIG_ERR == signal(SIGINT, signal_handle))
{
perror("signal error");
exit(-1);
}
while (1);
return 0;
}
IPC进程间通信见下篇文章