进程间通信的应用场景:数据传输,共享数据,通知事件,资源共享,进程控制
同一主机进程间通信:
unix进程间通信方式:有名管道,无名管道,信号。
system V进程间通信方式和POSIX进程间通信方式有
消息队列,共享内存,信号量。
不同主机(网络)进程间通信:Socket
无名管道:pipe创建管道 fork创建子进程
只能用于具有血缘关系的进程之间通信
生命周期随进程,进程退出,管道释放
管道是半双工的,数据只能从一个方向传输
管道是基于字节流的
管道是自带同步机制的,在保证数据安全的前提下,按照特定顺序访问临界资源
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> //exit
int main ()
{
int fds[2];
pipe(fds);
pid_t pid = fork();
if(pid == 0)//child
{
char buf[10] = {0};
read(fds[0], buf, sizeof(buf));
printf("child read= %s\n", buf);
exit(0);
}else if(pid > 0)//parent
{
write(fds[1], "hello", 5);
wait(NULL);//
exit(0);
}else
{
perror("fork error\n");
exit(1);
}
}
有名管道:mkfifo创建管道 access判断文件是否存在 read write
是一种半双工的通信方式,但是允许无血缘关系进程间通信
有名管道是一种特殊类型的文件,与无名管道基本相似
//read.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//pipe
#include <stdlib.h>//exit
//mkfifo tmp
//先读后写
int main ()
{
int fd = open("tmp", O_RDWR);
if(fd < 0)
{
perror("open error\n");
exit(1);
}
char buf[10] = {0};
read(fd, buf, sizeof(buf));
printf("read buf =%s\n", buf);
close(fd);
return 0;
}
//write.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//pipe
#include <stdlib.h>//exit
int main ()
{
int fd = open("tmp", O_RDWR);
if(fd < 0)
{
perror("open error\n");
exit(1);
}
write(fd, "hello", 5);
close(fd);
return 0;
}
消息队列:
消息队列是消息的链表,存放在内核中并由消息队列标识符表示。
特点:生命周期随内核,消息队列会一直存在,需要我们显示的调用接口删除或使用命令删除
消息队列可以双向通信
克服了管道只能承载无格式字节流的缺点
ipcs:显示IPC资源
ipcrm:手动删除IPC资源
msgtype > 0
接收消息类型为msgtype的消息
msgtype==0
接收消息队列中最前面的那个消息
msgtype<0
例-5(会读绝对值|-5|的一到四)
//read
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
//read.c -o read
//write.c -o write
//ipcs存在这里
struct msgbuf
{
long mtype;
char mtext[100];
};
int main ()
{
int id = msgget(0x123456, IPC_CREAT | IPC_PRIVATE);
struct msgbuf buf;
msgrcv(id, &buf, sizeof(buf), 10, IPC_NOWAIT);
printf("buf.mtype =%d, buf.mtext= %s\n", buf.mtype, buf.mtext);
return 0;
}
//write
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf
{
long mtype;
char mtxt[100];
};
int main ()
{
int id = msgget(0x123456, IPC_PRIVATE);
struct msgbuf buf;
buf.mtype = 10;
strcpy(buf.mtxt, "hello");
msgsnd(id, &buf, sizeof(buf), IPC_NOWAIT);
return 0;
}
共享内存:
在对个处理器的计算机系统中,可以被不同的CPU访问的大容量空间。就是说一块物理内存被映射到两个进程的地址空间,两个进程都可以访问这段空间,从而实现进程间通信
特点:
共享内存是最快的IPC形式,因为内存映射到共享它的进程的地址空间,这些进程数据传递就不再涉及到内核了,也就是说说不再通过执行进入内核的系统调用来传递彼此的数据,所以,它的速度是最快的。
共享内存的生命周期随进程,也需要显示地删除。
共享内存没有互斥与同步机制,因此,我们在使用时,需要自己添加。
//read
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main ()
{
int id = shmget(0x8989, 4096, IPC_PRIVATE | IPC_CREAT);
char *p = shmat(id, NULL, 0);
printf("p= %s\n", p);
shmdt(p);//解除映射
return 0;
}
//write
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
int main ()
{
int id = shmget(0x8989, 4096, IPC_PRIVATE | IPC_CREAT);
char *p = shmat(id, NULL, 0);
strcpy(p, "aaaa");
shmdt(p);//解除映射
return 0;
}
信号:
是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生
按键 CTRL +c CTRL + z
系统调用 kill raise abort
软件条件产生 alarm
硬件异常 段错误
可以使用kill -l命令查看Linux中的信号列表。
#include <stdio.h>
#include <signal.h>
//pkill +文件名 杀死文件
//kill +编号 +进程号
void handler(int signum)
{
printf("signum = %d\n", signum);
}
int main ()
{
signal(SIGINT, handler);
// signal(SIGINT, SIG_IGN);//忽视信号
// signal(SIGINT, SIG_DFL);//默认信号
// signal(SIGKILL, SIG_DFL);// SIGKILL SIGSTOP//不能捕获和忽视
// SIGKILL SIGSTOP//不能捕获和忽视
while(1);
return 0;
}