IPC 管道 Linux情况

打印 上一主题 下一主题

主题 859|帖子 859|积分 2577

管道通讯的特点:

1. 单工通讯---- 任何一个时候只能发送方 向 接收方发送数据


2. 流式传输:

    1> 先发送的数据先被接收,不能跳跃式接收 ----- 顺序发送顺序接收
    2> 未被接收的数据仍然滞留在管道中,下一次可以继承接收后续
    3> 已被接收的数据不会再出现管道中
3. 发送接收序次:

按被发送数据所占空间的所在值从小到大依次发送,接收到数据按序次依次存放在目标空间的从小到大的所在位置
Linux支持的管道有两种:

1. 匿名管道:

无名管道,只能用在具备亲缘关系的历程间定名管道:
2. 有名管道,

可以用在恣意历程间,接纳管道文件名给一个管道定名

1. 匿名管道

适用于具有亲缘关系的历程间

父历程向子历程发送数据的代码模板:
  1. int arrfd[2] = {-1,-1};
  2. pid_t pid;
  3. pipe(arrfd);
  4. pid = fork();
  5. if(pid > 0){ //父进程才执行的代码
  6.         close(arrfd[0]);
  7.         //用arrfd[1]发送数据 ------ write(fd,...,...)
  8.         close(arrfd[1])//无需继续发送时,及时调用
  9. }
  10. else if(pid == 0){//子进程才执行的代码
  11.         close(arrfd[1]);
  12.         //用arrfd[0]接收数据 ----- read(fd,...,...)
  13.         close(arrfd[0])//无需继续接收时,及时调用
  14. }
  15. .......
复制代码

子历程向父历程发送数据的代码模板:
  1. int arrfd[2] = {-1,-1};
  2. pid_t pid;
  3. pipe(arrfd);
  4. pid = fork();
  5. if(pid > 0){ //父进程才执行的代码
  6.         close(arrfd[1]);
  7.         //用arrfd[0]接收数据 ----- read(fd,...,...)
  8.         close(arrfd[0])//无需继续接收时,及时调用
  9. }
  10. else if(pid == 0){ //子进程才执行的代码
  11.         close(arrfd[0]);
  12.         //用arrfd[1]发送数据 ------ write(fd,...,...)
  13.         close(arrfd[1])//无需继续发送时,及时调用
  14. }
  15. .......
复制代码
管道读写数据的特点:

匿名、定名通用
管道中无数据可读时,read函数会壅闭
管道中数据已满时,write函数会壅闭
read函数返回0时,表示写端已关闭,不会有后续数据可接收

e.g.
  1. #include<stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. int main(int argc,char *argv[]){
  6.     pid_t pid;
  7.     int fds[2] = {-1,-1};
  8.     int ret = 0;
  9.     ret = pipe(fds);
  10.     if(ret){
  11.         printf("pipe error\n");
  12.         return 1;
  13.     }
  14.     pid = fork(); // fork
  15.     if(pid < 0){
  16.         printf("fork error\n");
  17.         return 2;
  18.     }
  19.     if(pid > 0){
  20.         close(fds[0]); // 关掉读
  21.         fds[0] = -1;
  22.         write(fds[1],"hello",6); // write
  23.         close(fds[1]); // 关掉写
  24.         fds[1] = -1;
  25.     }
  26.     else{ // 子进程
  27.         char buf[8] = "";
  28.         close(fds[1]); // 关掉写
  29.         fds[1] = -1;
  30.         read(fds[0],buf,8); // read
  31.         printf("In child process,buf = %s\n",buf);
  32.         close(fds[0]); // 关掉读
  33.         fds[0] = -1;
  34.     }
  35.     return 0;
  36. }
复制代码
输出:


2. 定名管道

匿名管道没有名字,pipe 函数直接获得两端描述符,然后进行接收发送
匿名管道只能借助于 fork 函数传递同一管道的描述符,因此只能用于具有亲缘关系的历程间
定名管道也称为有名管道
定名管道用管道文件名作为它的名字,因此创建定名管道时需要指定这个文件名,然后以两种不同的方式打开这个文件获得两端描述进行接收发送
只要两个历程操纵的是同一个管道文件,即可通过该管道文件代表的管道进行通讯,因此历程间没必要具有亲缘关系

创建函数:mkfifo


打开函数: open ------ 见系统IO
获得读端描述符:open(管道文件名,O_RDONLY);
获得写端描述符:open(管道文件名,O_WRONLY);
发送数据函数:write ---- 见系统IO
共同open(管道文件名,O_WRONLY)得到的描述符
接收数据函数:read ------ 见系统IO
共同 open (管道文件名,O_RDONLY) 得到的描述符
关闭函数:close ------ 见系统IO

使用定名管道的基本套路:
  1. #define FIFO_NAME "/tmp/myfifo"
  2. 发送进程:
  3. if(access(FIFO_NAME,F_OK)){//文件不存在,意味着命名管道未被创建
  4.         mkfifo(....);
  5. }
  6. ? = open(FIFO_NAME,O_WRONLY);
  7. 调用write函数发送数据
  8. close ----- 无需继续发送数据时及时调用
  9. 接收进程:
  10. if(access(FIFO_NAME,F_OK)){//文件不存在,意味着命名管道未被创建
  11.         mkfifo(....);
  12. }
  13. ? = open(FIFO_NAME,O_RDONLY);
  14. 调用read函数接收数据
  15. close ----- 无需继续接收数据时及时调用
复制代码



p.s.
write_process.c
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. int main(int argc,char *argv[]){
  7.     int wfd = -1;
  8.     if(argc < 2){
  9.         printf("The argument is too few\n");
  10.         return 1;
  11.     }
  12.     if(access(argv[1],F_OK)){ // 如果argv[1]存在,access为0
  13.         mkfifo(argv[1],0666); // 0666为使用权限
  14.     }
  15.     wfd = open(argv[1],O_WRONLY);
  16.     if(wfd < 0){
  17.         printf("w-open %s failed\n",argv[1]);
  18.         return 2;
  19.     }
  20.     write(wfd,"hello",6);
  21.     close(wfd);
  22.     wfd = -1;
  23.     return 0;
  24. }
复制代码
read_process.c
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. int main(int argc,char *argv[]){
  7.     int rfd = -1;
  8.     char buf[8] = "";
  9.     if(argc < 2){
  10.         printf("The argument is too few\n");
  11.         return 1;
  12.     }
  13.     if(access(argv[1],F_OK)){ // 如果argv[1]存在,access为0
  14.         mkfifo(argv[1],0666); // 0666为使用权限
  15.     }
  16.     rfd = open(argv[1],O_RDONLY);
  17.     if(rfd < 0){
  18.         printf("r-open %s failed\n",argv[1]);
  19.         return 2;
  20. }
  21.     read(rfd,buf,6);
  22.     printf("buf:%s\n",buf);
  23.     close(rfd);
  24.     rfd = -1;
  25.     return 0;
  26. }
复制代码

p.s.
运行其中一个程序:发现程序堵塞 (堵塞在open处而不是read/write处)

创建新标签页,运行另一个程序:

程序成功输出


3. 通讯协议的制定

        任何通讯形式,起首必须为参与通讯的各方制定同一的规则,这种规则被称为通讯协议(protocol)。
一份通讯协议至少包括如下内容:

1. 交互的数据种类、作用
2. 每种数据的二进制位组成(统称为数据格式)
        按协议组织好的一种通讯数据,被称为PDU(Protocol Data Unit)
3. 区分数据种类的方式
4. 各种数据的使用序次

在操持每种数据的二进制位组成时,需要考虑怎样制止混包征象:

1. 粘包征象
2. 拆包征象



通讯数据的两种基本组成方案:
1. 文本形式-----即将数据组合成特定形式的字符串,并规定字符串中不同子串的作用
        比方:“人名-年事-工资”
2. 非文本形式----或称为二进制形式,即按字节或位指定数据中不同部门的作用
        比方:20字节存放人名+4字节存放年事+4字节存放工资
3. 由多个文本加非文本组合而成的混合形式-----即交互数据中某些部门组合成字符串,另一些部门以二进制形式存在

交互数据按数据长度是固定还是不固定,组成方案分为如下两种:
1. 定长:全部数据长度固定为多少字节
2. 变长:有的数据长度比较短,有的数据长度比较长

通讯数据组成方案两种分类相互交错就大概存在如下几种情况:
1. 定长文本:
        发送方每次发送:1> 按数据格式在一连内存空间中组织好待发送的数据   2> 发送
        接收方每次接收:1> 分配好用于存放接收数据的内存空间   2> 接收
2. 变长文本
一样平常接纳两种方式来制止混包:
        1> 以某个特别字符或一连几个特别字符的组合作为通讯数据的末端,比方:'\0'、'\n'、"\r\n"
        2> 在字符串内容上做文章。 比方:“0023abcxxx???” 其中0023表示后续有效字符的长度
发送方每次发送:1> 按数据格式在一连内存空间中组织好待发送的数据   2> 发送
接收方每次接收:
方案1过程:
        1)借助于一个临时文件,每次接收一个字节存放到临时文件,当接收到末端标志时结束本轮接收
        2)盘算临时文件大小
        3)动态分配空间
        4)从临时文件中将数据读入动态空间里
方案2过程:
        1) 接收固定长度的字节数
        2) sscanf 将其扫描成整数得到后续需要动态分配空间的大小:整数 + 第1步的固定长度
        3)动态分配空间
        4)strcpy 第1步读到内容到动态空间
        5)按第2步得到的整数去读后续数据
        由于是文本形式,因此与协议数据相干的代码大部门都会涉及字符串处置惩罚
3. 定长非文本
发送方每次发送:1> 按数据格式在一连内存空间中组织好待发送的数据   2> 发送
接收方每次接收:1> 分配好用于存放接收数据的内存空间   2> 接收
4. 变长非文本
5. 定长混合
        发送方每次发送:1> 按数据格式在一连内存空间中组织好待发送的数据   2> 发送
        接收方每次接收:1> 分配好用于存放接收数据的内存空间   2> 接收
6. 变长混合
        无论接纳哪种都要考虑:1> 怎样区分不同通讯数据   2> 怎样制止混包征象
        对于变长非文本和变长混合多数项目接纳变长结构体来组织协议数据

对于通讯数据是变长非文本或变长混合形式的通讯程序,开发过程:
1. 按协议数据格式操持变长结构体--按协议格式组合而成的一个完整数据包在行业里统称为PDU(Protocol Data Unit协议数据单元)
2. 为通讯各方编写各种数据的创建函数 和 同一的烧毁函数 以及发送、接收函数 ------ 统称为协议代码
3. 任何想要发送数据的任务每次发送:
        1> 调用某一个创建函数,在一连的空间中按协议组织好待发送的数据
        2> 调用发送函数
        3> 调用烧毁函数
4. 任何想要接收数据的任务每次接收:
        1> 调用接收函数
        2> 对接收下来的协议数据进行处置惩罚
        3> 调用烧毁函数

现实项目开发中,涉及通讯的软件开发,往往需要程序员:
1. 研究别人提供的通讯协议
2. 自行操持新的通讯协议
二者必选其一
服务端历程:或称服务器历程,为其它历程提供通讯服务
客户端历程:使用通讯服务的历程
示例:编写如下程序:
    客户端历程:接收用户输入的一个整数n,程序产生n个随机数,让用户选择对这n个随机数排序还是求均值,然后将对n个随机数的操纵和n个随机数发送给服务端,接收服务端处置惩罚效果,并表现处置惩罚效果
    服务端历程:接收客户端的操纵哀求和n个随机数,然后完成相应的操纵,将操纵效果发送回给客户端


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表