进程间通信
进程间通信方式:
进程用户空间是相互独立的,一般而言是不能相互访问的。但很多情况下进程间需要互相通信,来完成系统的某项功能。进程通过与内核及其它进程之间的互相通信来协调它们的行为,不同进程之间进行数据交互即为进程间通信。
常见的进程间通信方式有:有名管道、无名管道、消息队列、共享内存、信号、信号量、socket。因为信号量与socket涉及到线程与网络编程的内容,所以本文不作简述。
管道:
管道分为无名管道与有名管道,无名管道是一种半双工的通信方式,数据只能单向流动且只能在具有亲缘关系的进程间使用(如:父子进程)。
有名管道也是半双工的通信方式,但允许无亲缘关系的进程间使用。
无名管道:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int fds[2] = {0}; // fds即为创建的无名管道
pipe( fds );
pid_t pid = fork();
if( pid > 0 ) {
write( fds[1], "hello", 5 ); // 父进程在fds[1]输入数据
wait( NULL );
}else if( pid == 0 ) {
char buf[10] = {0};
read( fds[0], buf, sizeof(buf) ); // 子进程在fds[0]读取数据
printf( "buf if %s \n",buf );
}
return 0;
}
有名管道:(有名管道需要以指令”mkfifo+管道名”创建管道)
write.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open( "pipeline", O_RDWR ); // 通过打开管道文件进行数据传输
if( fd < 0 )
{
perror( "pipe open error" );
return 0;
}
int ret = write( fd, "hello", 5 );
if( ret < 0 )
{
perror( "pipe write error" );
return 0;
}
close( fd );
return 0;
}
read.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open( "pipeline", O_RDWR ); // 通过打开管道文件进行数据读取
if( fd < 0 )
{
perror( "pipe open error" );
return 0;
}
char buf[1024] = {0};
int ret = read( fd, buf, 5 );
if( ret == 0 )
{
perror( "pipe read error" );
return 0;
}
else
{
printf( "pipeline is %s \n", buf );
}
close( fd );
return 0;
}
消息队列:
消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点。
read.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
struct msgbuf
{
long mtype;
char mtext[100];
};
int main()
{
int mid = msgget( 0x12345678, IPC_PRIVATE ); // 打开消息队列
struct msgbuf msg;
msgrcv( mid, &msg, sizeof(msg), 10, IPC_NOWAIT ); // 通过结构体成员mtext读取数据
printf( "%s \n", msg.mtext );
return 0;
}
write.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
struct msgbuf
{
long mtype;
char mtext[100];
};
int main()
{
int mid = msgget( 0x88888888, IPC_CREAT | IPC_PRIVATE ); // 打开消息队列
struct msgbuf msg;
msg.mtype = 10;
strcpy( msg.mtext, "hello" );
msgsnd( mid, &msg, sizeof(msg), IPC_NOWAIT ); // 通过结构体成员mtext传输数据
return 0;
}
共享内存:
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC(进程间通信)方式,它是针对其它进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步与通信。
read.c:
#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
int id = shmget( 0x8989, 1024, IPC_CREAT ); // 创建获取ID
char *ptr = shmat( id, NULL, IPC_NOWAIT ); // 通过shmat映射内存后对共享内存进行操作
printf( "read: %s \n", ptr );
shmdt( ptr ); // 删除映射
return 0;
}
write.c:
#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
int main()
{
int id = shmget( 0x8989, 1024, IPC_CREAT );
char *ptr = shmat( id, NULL, IPC_NOWAIT );
sprintf( ptr, "hello" );
shmdt( ptr );
return 0;
}
信号:
signal 函数作用是设置信号处理方式。signal()会依参数 signum 指定的信号编号(0~64)来设置该信号的处理函数。当指定的信号到底时,就会跳转到参数 handler 指定的函数执行。
具体信号可通过指令:kill -l查看。
#include <stdio.h>
#include <signal.h>
void handler( int sig )
{
printf( "hello \n" );
}
int main()
{
signal( SIGINT, handler ); // 将信号与函数关联后,只要触发信号就会执行该函数
while(1);
return 0;
}