linux学习---linux基于文件的IPC(匿名管道pipe,命名管道mkfifo,普通文件,socket文件)

来源:互联网 发布:中国质造 淘宝 编辑:程序博客网 时间:2024/05/19 05:41

常用的IPC分为两个类别,一是基于文件,而是基于内存

基于文件的分别有匿名管道,有名管道,普通的文件共享,socket文件

基于内存的有普通内存共享(本文章没有介绍),共享内存,共享信号量,消息队列

如果要看基于内存的IPC,请参考:http://blog.csdn.net/xiaoxiaopengbo/article/details/78431042

本文就针对linux基于文件的IPC

一.匿名管道pipe

管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:

1. 其本质是一个伪文件(实为内核缓冲区)

2. 由两个文件描述符引用,一个表示读端,一个表示写端。

3. 规定数据从管道的写端流入管道,从读端流出。

管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

管道的局限性:

1) 数据一旦被读走,便不在管道中存在,不可反复读取。

2) 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。

3)只能在有公共祖先的进程间使用管道。

常见的通信方式有,单工通信、半双工通信、全双工通信。

函数原形

#include <unistd.h>

int pipe(int pipefd[2]);

  返回值  成功:0;失败:-1,设置errno

Pipefd参数是一个入参,函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0]→ r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。

有记下几种编程模型:单进程半双工,父子进程半双工

下图是单进程的半双工编程模型,并附带程序源码

#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){    intfd[2];    intr;    charbuf[40];    printf("%d\n",getpid());    r=pipe(fd);    write(fd[1],"hello",5);    write(fd[1],"world",5);        r=read(fd[0],buf,20);    buf[r]=0;    printf("1::%s\n",buf);    write(fd[1],"Iam writing to pipe",strlen("I am writing to pipe"));    r=read(fd[0],buf,20);    buf[r]=0;    printf("2::%s\n",buf);        r=read(fd[0],buf,20);    buf[r]=0;    printf("3::%s\n",buf);        while(1);}

执行结果

结论:此程序证明了第一点局限数据一旦被读走,便不在管道中存在,不可反复读取。但是这种编程模型基本没有用途

下面来看看另外一种编程模型,父子进程之前的半双工,同样源码如下

#include <stdio.h>#include <stdlib.h>#include <unistd.h> int main(void){    int    n;    int    fd[2];    pid_t  pid;    char   line[20]={0};     if(pipe(fd) < 0)       printf("pipeerr:%m\n");    if((pid = fork()) < 0) {       printf("forkerr:%m\n");    }else if (pid > 0) {       /* parent */       close(fd[0]);       write(fd[1],"hello world\n", 12);    }else {                 /* child */       close(fd[1]);       n= read(fd[0], line, 20);       printf("%s\n",line);    }    exit(0);}

运行结果

二.有名管道FIFO

上面的匿名管道的缺点其实是很明显的,虽然可以进程间通信,但是只局限于有血缘关系的进程间通信,但是如果我想两个独立的进程用管道来通信呢?有名管道FIFO就有了用武的地方了

创建有名管道函数原形

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname, mode_tmode);

返回值

 返回值  成功:0;失败:-1,设置errno

程序源码,创建两个文件fifoA.c fifoB.c分别编译为A,B

fifoA.c

#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <signal.h>#include <stdlib.h>int fd;int i=0;void end(int s){    printf("catchctrl+c signal\n");    //closepipe    close(fd);    //deletepipe    unlink("my.pipe");    exit(-1);}int main(){    signal(SIGINT,end);    //createpipe file    mkfifo("my.pipe",0666);    //openpipe file    fd=open("my.pipe",O_RDWR);    //writedata per 1 sec    while(1)    {       sleep(1);       write(fd,&i,4);       i++;    }   }

fifoB.c

#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <sys/stat.h>#include <signal.h>#include <stdlib.h>int fd;int i;void end(int s){    printf("catchctrl+c signal\n");    //closepipe    close(fd);    exit(-1);    }int main(){    signal(SIGINT,end);    //openpipe file    fd=open("my.pipe",O_RDWR);    //readdata    while(1)    {       read(fd,&i,4);       printf("%d\n",i);    }}

先运行fifoA.c的程序,发现就会创建一个

此时运行B程序,发现数据并没有丢失,一下会把之前的都打印出来

另外需要注意的是:FIFO和pipe一样,数据不会重复读出

三.基于文件共享的IPC

源码:

分别有procA.c procB.c,分别编译成A,B可以执行程序

ProcA.c

#include <stdio.h>#include <fcntl.h>#include <sys/mman.h>main(){    int*p;    intfd;    inti;    fd=open("tmp",O_RDWR|O_CREAT,0666);    ftruncate(fd,4);    p=mmap(0,4,PROT_READ|PROT_WRITE,           MAP_SHARED,fd,0);    i=0;           while(1)    {       sleep(1);       *p=i;       i++;    }    close(fd);}

ProcB.c

#include <stdio.h>#include <fcntl.h>e#include <sys/mman.h>main(){    int*p;    intfd;        fd=open("tmp",O_RDWR);       p=mmap(0,4,PROT_READ|PROT_WRITE,           MAP_SHARED,fd,0);    while(1)    {       sleep(1);       printf("%d\n",*p);    }    close(fd);}

运行结果

A程序每隔1s写一次数,写的数每次+1

运行B程序如图

发现还是结果会丢失的

四.socket文件的IPC

socket网络编程可能使用得最多,经常用在网络上不同主机之间的通信。其实在同一主机内通信也可以使用socket来完成,socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。在使用socket创建套接字时通过指定参数domain是af_inet(ipv4因特网域)或af_inet6(ipv6因特网域)或af_unix(unix域)来实现。

函数原形

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

int socket(int domain, int type, intprotocol);

第一个参数demain是协议簇

Name                Purpose                          Man page

AF_UNIX, AF_LOCAL   Local communication              unix(7)

AF_INET             IPv4 Internet protocols          ip(7)

AF_INET6            IPv6 Internet protocols          ipv6(7)

AF_IPX              IPX - Novell protocols

AF_NETLINK          Kernel user interface device     netlink(7)

AF_X25              ITU-T X.25 / ISO-8208protocol   x25(7)

AF_AX25             Amateur radio AX.25 protocol

AF_ATMPVC           Access to raw ATM PVCs

AF_APPLETALK        Appletalk                        ddp(7)

AF_PACKET           Low level packet interface       packet(7)

一般常用的就是前三个

Type参数有以下几个选择

SOCK_STREAM     Provides sequenced, reliable, two-way,connection-based

                       byte  streams. An out-of-band data transmission mecha

                       nism may be supported.

SOCK_DGRAM      Supports datagrams (connectionless,unreliable messages

                       of a fixed maximumlength).

SOCK_SEQPACKET  Provides a  sequenced,  reliable, two-way connection-

                       based data transmissionpath  for datagrams  of  fixed

                       maximum  length; a  consumer  is required  to read  anentire packet with each input system call.

SOCK_RAW        Provides raw network protocol access.

SOCK_RDM        Provides a reliable datagram layer thatdoes not  guarantee ordering.

 

SOCK_PACKET     Obsolete and  should  not be used in new programs; see packet(7).

第三个参数很少用,一般通过前两个参数就能确定要传输的协议,我通常都设为0

返回值  成功:返回文件描述符;失败:-1,设置errno

直接贴两个进程间通讯的

socketA.c socketB.c

socketA.c

#include <sys/socket.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <linux/un.h> int main(){    intfd;    intr;    charbuf[200];    //1.建立socket    fd=socket(AF_UNIX,SOCK_DGRAM,0);    if(fd==-1)printf("socketerr:%m\n"),exit(-1);    printf("socket成功\n");    //2.构造本地文件地址    structsockaddr_un addr={0};    addr.sun_family=AF_UNIX;    memcpy(addr.sun_path,"my.sock",strlen("my.sock"));    //3.把socket绑定在地址上    r=bind(fd,(structsockaddr*)&addr,sizeof(addr));    if(r==-1)printf("binderr:%m\n"),exit(-1);    printf("地址绑定成功\n");    //4.接受数据    bzero(buf,sizeof(buf));    r=read(fd,buf,sizeof(buf));    printf("%s\n",buf);    //5.关闭通道    close(fd);    //6.删除socket文件    unlink("my.sock");}

socketB.c

#include <sys/socket.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <linux/un.h> int main(){    intfd;    intr;    charbuf[200];    structsockaddr_un addr={0};    //1.建立socket    fd=socket(AF_UNIX,SOCK_DGRAM,0);    if(fd==-1)printf("socketerr:%m\n"),exit(-1);    printf("socket成功\n");    //2.链接到指定地址    addr.sun_family=AF_UNIX;    memcpy(addr.sun_path,"my.sock",strlen("my.sock"));    r=connect(fd,(structsockaddr*)&addr,sizeof(addr));    if(r==-1)printf("binderr:%m\n"),exit(-1);    printf("地址连接成功\n");    //3.发送数据    write(fd,"hello!",strlen("hello"));     //4.关闭通道    close(fd);}

运行结果

下面举一个网络通信的socket,编程模型是一致的,ipA.c ipB.c

ipA.c

#include <sys/socket.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <linux/un.h>#include <netinet/in.h>#include <arpa/inet.h>int main(){    intfd;    intr;    charbuf[200];    //1.建立socket    fd=socket(AF_INET,SOCK_DGRAM,0);    if(fd==-1)printf("socketerr:%m\n"),exit(-1);    printf("socket成功\n");    //2.构造本地文件地址    structsockaddr_in addr={0};    addr.sin_family=AF_INET;    addr.sin_port=htons(9999);    addr.sin_addr.s_addr=inet_addr("192.168.1.102");    //3.把socket绑定在地址上    r=bind(fd,(structsockaddr*)&addr,sizeof(addr));    if(r==-1)printf("binderr:%m\n"),exit(-1);    printf("地址绑定成功\n");    //4.接受数据    bzero(buf,sizeof(buf));    r=read(fd,buf,sizeof(buf));    printf("%s\n",buf);    //5.关闭通道    close(fd);    //6.删除socket文件 }
ipB.c

#include <sys/socket.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <linux/un.h>#include <netinet/in.h>#include <arpa/inet.h>int main(){    intfd;    intr;    charbuf[200];    //1.建立socket    fd=socket(AF_INET,SOCK_DGRAM,0);    if(fd==-1)printf("socketerr:%m\n"),exit(-1);    printf("socket成功\n");    //2.构造本地文件地址    structsockaddr_in addr={0};    addr.sin_family=AF_INET;    addr.sin_port=htons(9999);    addr.sin_addr.s_addr=inet_addr("192.168.1.102");    //3.把socket绑定在地址上    r=connect(fd,(structsockaddr*)&addr,sizeof(addr));    if(r==-1)printf("binderr:%m\n"),exit(-1);    printf("地址绑定成功\n");    //4.接受数据    write(fd,"hello!",strlen("hello"));    //5.关闭通道    close(fd);    //6.删除socket文件}

运行结果是和上面的类似的

阅读全文
1 0
原创粉丝点击