1. 进程间通信方式介绍
这篇文章介绍Linux下进程的间的通信方式,常用的方式如下:
1. socket—网络通信
2. 管道---无名管道—命名管道---文件--FIFO
3. 消息队列
4. 共享内存
5. 信号量集
6. 信号—signal捕获信号---kill命令发送信号
int kill(pid_t pid, int sig);
2. 标准流管道
标准流管道像文件操作有标准io流一样,管道也支持文件流模式。用来创建连接到另一进程的管道popen和pclose。
函数原型:
#include <stdio.h>
FILE* popen(const char* command, const char* open_mode);
int pclose(FILE* fp);
popen用于启动进程,用法含义与fopen类似,第二个参数填权限,支持填"r"和"w"。
pclose用于关闭进程,释放资源。
popen启动进程之后可以直接与启动的进程间通信,比较方便。
示例代码: 从标准管道流中读取 打印/etc/profile的内容:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *file=NULL;
size_t len=0;
char buff[1024+1];
file=popen("cat /etc/profile","r"); //执行cat /etc/profile命令,读方式。
if(file!=NULL)
{
len=fread(buff,1,1024,file); //读数据
buff[len]='\0';
printf("len=%d\n %s\n",len,buff);
}
pclose(file); //等待线程结束。
return 0;
}
3. 无名管道
无名管道用于有亲戚关系的进程间通信。 比如: 兄弟进程、父子进程等。
#include <unistd.h>
int pipe(int fds[2]);
pipe函数用于创建一个无名管道,如果成功,fds[0]就存放可读的文件描述符,fds[1]就存放可写文件描述符。
返回值: 0表示成功,-1表示失败。
无名管道的特点:
- 只能在亲缘关系进程间通信(父子或兄弟)
- 半双工(固定的读端和固定的写端)
- 虚拟管道文件是一个存在内存的特殊文件,可以用read、write函数进行操作。
这里说的管道,就像一条水管,有两个端口,一端进水,另一端出水。管道也有两个端口,分别是读端和写端,进水可看成数据从写端被写入,出水可看数据从读端被读出。在程序里分别就对应了read和write函数。
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char **argv)
{
int fds[2];
/*1. 创建无名管道:得到管道读写文件描述符 fds[0]和读端相对应, fds[1]和写端相对应*/
pipe(fds);
/*2. 创建子进程*/
pid_t pid;
pid=fork();
if(pid==0) //子进程
{
char buff[100+1];
int cnt;
//从管道的读端读取数据
cnt=read(fds[0],buff,100); //带阻塞功能
buff[cnt]='\0';
printf("子进程收到的数据:%d,%s\n",cnt,buff);
}
else //父进程
{
char buff[]="1234567890";
//向管道的写端写数据
write(fds[1],buff,strlen(buff));
//等待子进程结束
wait(NULL);
printf("父进程正常结束.\n");
}
return 0;
}
4. 命名管道
无名管道只能在亲缘关系的进程间通信大大限制了管道的使用,有名管道突破了这个限制,通过指定路径名的形式实现不相关进程间的通信,因为命名管道通信使用的管道是一个实体文件,在磁盘上的存在的,而无名管道是存在内存中的虚拟文件,其他进程无法访问,导致没有关联的进程无法进行通信。
4.1 在命令行如何创建管道文件?
[wbyq@wbyq test]$ mkfifo test.fifo
[wbyq@wbyq test]$ ls
test.fifo
[wbyq@wbyq test]$ ls -l
总用量 0
prw-rw-r--. 1 wbyq wbyq 0 10月 15 15:29 test.fifo
4.2 在命令行演示两个相关的进程通过进行管道文件进行通信
4.3 创建fifo文件的函数
1. 创建FIFO文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:创建的FIFO文件的全路径名;
mode:文件访问权限,比如0666。
返回值:如果创建成功,则返回0,否则-1。
2. 删除FIFO文件
#include <unistd.h>
int unlink(const char *pathname);
3. 用命令创建和删除FIFO文件
用命令mkfifo创建。
用命令unlink删除。
4.4 创建写端: FIFO
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./a.out <fifo文件--写>\n");
return 0;
}
int fd=open(argv[1],O_WRONLY);
if(fd<0)
{
printf("%s 文件打开失败.\n",argv[1]);
return 0;
}
char buff[]="1234567890";
write(fd,buff,strlen(buff));
close(fd);
return 0;
}
示例代码: 进程B负责读数据
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./a.out <fifo文件--读>\n");
return 0;
}
int fd=open(argv[1],O_RDONLY);
if(fd<0)
{
printf("%s 文件打开失败.\n",argv[1]);
return 0;
}
char buff[100+1];
int cnt;
cnt=read(fd,buff,100);
buff[cnt]='\0';
printf("buff=%s\n",buff);
close(fd);
return 0;
}
4.5 创建读端: FIFO
在命令行演示两个相关的进程通过进行管道文件进行通信.
代码创建读端,读取数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int fd=0;
int len=0;
char buff[100];
fd=open("myfifo",O_RDONLY);
len=read(fd,buff,100);
buff[len]='\0';
printf("read: %s\n",buff);
return 0;
}