Linux下的IPC机制
IPC(Inter-Process Communication)是多个进程之间相互沟通的一种方法。在linux下有多种进程间通信的方法。
共享内存
Linux内存共享有多种,如mmap()、Posix共享内存、System V 共享内存。
1>mmp()通过映射一个普通文件实现共享内存,具有文件实体,shmget()对应文件在内存中,无文件实体。
2>mmp()不建议使用叠加方式共享,shmget()用于多个进程间交换数据。
3>mmp() shmget() 进程重启后共享内存中的数据都不会丢失;但是机器重启后只有mmp()方式的共享内存可以保存数据
4>mmap()接口更简单,通用性也更高。
这里首先先使用shmget建立一块共享内存,然后向该内存中写入数据并返回该共享内存shmid。使用另一个程序通过上一程序返回的shmid读该共享内存内的数据
建立共享内存并写入数据的程序:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
void get_buf(char *buf)
{
int i=0;
while((buf[i]=getchar())!='\n'&&i<1024)
i++;
}
int main(void)
{
int shmid;
shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,IPC_CREAT|0666);
if(shmid==-1)
{
perror("shmget");
}
char *buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
get_buf(buf);
printf("%d\n",shmid);
return 0;
}
读取数据的程序
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int shmid;
shmid=atoi(argv[1]);
char *buf;
if((int)(buf=shmat(shmid,NULL,0))==-1)
{
perror("shmat");
exit(1);
}
printf("%s\n",buf);
shmdt(buf);
return 0;
}
管道
管道是由内核管理的一个缓冲区,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。
管道的特点:
1、管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
2、一般只能用于父子进程之间。
3、单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
4、数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
管道程序实例(创建一个从父进程到子进程的管道,并且父进程经由该管道向子进程传送数据):
#include <unistd.h>
#include <stdio.h>
#define MAXLINE 1024
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)
printf("pipe error\n");
if ((pid = fork()) < 0) {
printf("fork error\n");
} else if (pid > 0) { /* parent */
close(fd[0]);
write(fd[1], "hello world\n", 12); /* write data to fd[1] */
} else { /* child */
close(fd[1]);
n = read(fd[0], line, MAXLINE); /* read data from fd[0] */
write(STDOUT_FILENO, line, n); /* write data to standard output */
}
return (0);
}
FIFO
FIFO有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先进程。但是,通过FIFO,不相关的进程之间也能交换数据。
FIFO的用途:用于客户进程--服务器进程应用程序中
FIFO的真正优势在于:服务器可以是一个长期运行的进程(例如守护进程),而且与其客户可以无亲缘关系。
实例:需要对一个输入文件进行两次处理
可以使用FIFO和tee命令如下处理:
mkfifo fifo1
prog3 < fifo1 &
prog1 < (输入文件) | tee fifo1 | prog2
执行流程如下:
信号
信号用于一个或几个进程之间传递异步信号。信号可以有各种异步事件产生,比如键盘中断等。shell也可以使用信号将作业控制命令传递给它的子进程。
实例(向进程本身发送信号,并传递指针参数,信号实现了附加信息的传递):
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
union sigval mysigval;
int i;
int sig;
pid_t pid;
char data[10];
memset(data,0,sizeof(data));
for(i=0;i < 5;i++)
data[i]='2';
mysigval.sival_ptr=data;
sig=atoi(argv[1]);
pid=getpid();
sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;//三参数信号处理函数
act.sa_flags=SA_SIGINFO;//信息传递开关
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
sigqueue(pid,sig,mysigval);//向本进程发送信号,并传递附加信息
}
}
void new_op(int signum,siginfo_t *info,void *myact)//三参数信号处理函数的实现
{
int i;
for(i=0;i<10;i++)
{
printf("%c\n ",(*( (char*)((*info).si_ptr)+i)));
}
printf("handle signal %d over;",signum);
}
消息队列
消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。每个消息队列中的消息,又构成一个独立的链表。
消息队列克服了信号承载信息量少,管道只能承载无格式字符流。
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
参考资料:
Linux 进程间通信(一)(经典IPC:管道、FIFO)
linux基础——linux进程间通信(IPC)机制总结
linux下IPC机制之“内存共享”
Linux下内存共享的一个实例(设置共享内存,一个程序写,一个程序读)