管道半双工通信程序linux,Linux进程间通信的几种方法-半双工管道,命名管道,消息队列...
1、半双工管道
简单实现
半双工管道可以实现父进程和子进程之间或者子进程之间(前提是有共同的祖先)的通信
因为是半双工,所以两端不可能同时读取,而是一端读一端取,而且当一端分配到读任务后,那么他就固定了,不能再担当写的角色了,相反亦然。
测试程序如下:
#include
#include
#include
#include
#include
intmain(void)
{
intfd[2],nbytes;
pid_t childpid;
charstring[] ="Hello, World!\n";
charreadbuffer[80];
pipe(fd);
intw_fd = fd[1];//写
intr_fd = fd[0];//读
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
close(r_fd);//关闭读
write(w_fd,string,strlen(string));
exit(0);
}
else
{
close(w_fd);关闭写
nbytes = read(r_fd, readbuffer,sizeof(readbuffer));
printf("Received string:%s\n",readbuffer);
}
return0;
}
不能看出,为什么半双工管道两端是单一角色了,因为开始读或者写之前必须关闭写或读的fd
半双工管道的阻塞性
写端对读端具有依赖性:假如读端被关闭了,那么再写入管道就没有意义的,此时写入管道会返回-1。
阻塞性:
上例程序的这句代码 bytes = read(r_fd, readbuffer,sizeof(readbuffer));
指明了管道的大小,即为sizeof(readbuffer),写入比该数子大的数据时,先只会写sizeof(readbuffer)个数据到管道,然后写端阻塞,等待读端取走数据,然后按同样的规则写入剩余的部分,这个也体现了写入操作的非原子性。写请求字节数还有一个最大阀值,在/usr/include/linux有文件 limits.h中宏定义
#define PATH_MAX 4096
此时,假如指明的管道大小大于PATH_MAX ,系统会按这个PATH_MAX 作为写请求最大字节数。
2、命名管道
先说说命名管道相比半双工管道的优势,不再需要进程之间有亲属关系了,因为是以一种文件的形式存在,所以对文件的大部分操作都支持。建立命名管道的方法为int makefifo(const char *pathname, mode_t mode);
先解释下这个函数,pathname为管道名称,mode为建立管道的选项,返回值0为成功,-1为失败。
命名管道的阻塞性:
阻塞性可通过mode参数指定:
1、当以阻塞(未指定O_NONBLOCK)方式只读打开FIFO的时候,则将会被阻塞,知道有其他进程以写方式打开该FIFO。
2、类似的,当以阻塞(未指定O_NONBLOCK)方式只写打开FIFO的时候,则将会被阻塞,知道有其他进程以读方式打开该FIFO。
3、当以非阻塞方式(指定O_NONBLOCK)方式只读打开FIFO的时候,则立即返回-1,其errno是ENXIO。
测试程序
server.c
#include
#include
#include
#include
#include
#define FIFO_CHANNEL "my_fifo" /* 宏定义,fifo路径 */
intmain()
{
intfd;
charbuf[80];
if(mkfifo(FIFO_CHANNEL,0777)==-1)/* 创建命名管道,返回-1表示失败 */
{
perror("Can't create FIFO channel");
return1;
}
if((fd=open(FIFO_CHANNEL,O_RDONLY))==-1)/* 以只读方式打开命名管道 */
{
perror("Can't open the FIFO");
return1;
}
while(1)/* 不断从管道中读取信息 */
{
read( fd, buf, sizeof(buf) );
printf("Message from Client: %s\n",buf );
sleep(3); /* sleep 3s */
}
close(fd); /* 关闭管道 */
return0;
}
client.c
#include
#include
#include
#include
#include
#define FIFO_CHANNEL "my_fifo" /* 宏定义,fifo路径 */
intmain()
{
intfd;
chars[]="Hello!";
if((fd=open(FIFO_CHANNEL,O_WRONLY))==-1)/* 以读写方式打开命名管道,返回-1代表失败 */
{
perror("Can't open the FIFO");
return1;
}
while(1)/* 不断向管道中写信息 */
{
write( fd, s, sizeof(s) );
printf("Write: %s\n",s);
sleep(3); /* sleep 3s */
}
close(fd); /* 关闭管道 */
return0;
}
结果截图:
此时文件系统中会多一个my_fifo的特殊文件。
3、消息队列
先介绍消息队列常用函数
#include
#include
key_t ftok(constchar* pathname,intproj_id);
注意: pathname必须是已经存在的目录
系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过该ftok函数得到。
需要用 mkdir -p /ipc/msg
#include
#include
#include
intmsgget(key_t key,intmsgflag);
获取消息的msgget()函数,第一个参数为键值,msgflag可以指定参数
IPC_CREATE:如果在内存中不存在该队列,则创建它
IPC_EXCL:当与IPC_CREATE一起使用时,如果队列早已存在则将出错
#include
#include
#include
intmsgsnd(intmsqid,constvoid*msgp,size_tmsgsz,intmsgflg);
发送消息msgsnd()函数,第一个参数为msgget返回值,第二个参数为消息缓冲区,第三个参数为消息长度,msgflg可以设置为0(表示忽略),也可只是为IPC_NOWAIT,如果消息队列已满,则消息将不会被写入队列中,未设IPC_NOWAIT,将会阻塞,直到可以写消息为止。
#include
#include
#include
ssize_t msgrcv(intmsqid,void*msgp,size_tmsgsz,longmsgtyp,intmsgflg);
接受消息msgrcv,第一个参数由msgget指定,第二个参数指定缓冲区,第三个参数代表缓冲区大小,不包括mtype成员的长度,第四个参数指定要从队列中获取的消息类型。msgflg可设置为IPC_NOWAIT功能同上。
#include
#include
#include
intmsgctl(intmsqid,intcmd,structmsqid_ds *buf);
消息控制msgctl函数,该函数向内核发送一个cmd命令,内核根据此来判断进行何种操作。
IPC_STAT 获取队列的msqid _ds结构
IPC_SET 设置队列的msqid_ds结构的ipc_perm成员值
IPC_RMID内核删除队列
以下为消息队列的一个例子:
#include
#include
#include
#include
#include
#include
#include
void msg_show_attr(int msg_id, struct msqid_ds msg_info)
{
intret = -1;
sleep(1);
ret = msgctl(msg_id, IPC_STAT, &msg_info);
if( -1 == ret)
{
printf("获得消息信息失败\n");
return;
}
printf("\n");
printf("现在队列中的字节数:%d\n",msg_info.msg_cbytes);
printf("队列中消息数:%d\n",msg_info.msg_qnum);
printf("队列中最大字节数:%d\n",msg_info.msg_qbytes);
printf("最后发送消息的进程pid:%d\n",msg_info.msg_lspid);
printf("最后接收消息的进程pid:%d\n",msg_info.msg_lrpid);
printf("最后发送消息的时间:%s",ctime(&(msg_info.msg_stime)));
printf("最后接收消息的时间:%s",ctime(&(msg_info.msg_rtime)));
printf("最后变化时间:%s",ctime(&(msg_info.msg_ctime)));
printf("消息UID是:%d\n",msg_info.msg_perm.uid);
printf("消息GID是:%d\n",msg_info.msg_perm.gid);
}
intmain(void)
{
intret = -1;
intmsg_flags, msg_id;
key_t key;
structmsgmbuf{
intmtype;
charmtext[10];
};
structmsqid_ds msg_info;
structmsgmbuf msg_mbuf;
intmsg_sflags,msg_rflags;
char*msgpath ="/ipc/msg/";
key = ftok(msgpath,'b');
if(key != -1)
{
printf("成功建立KEY\n");
}
else
{
printf("建立KEY失败\n");
}
msg_flags = IPC_CREAT|IPC_EXCL;
msg_id = msgget(key, msg_flags|0x0666);
if( -1 == msg_id)
{
printf("消息建立失败\n");
return0;
}
msg_show_attr(msg_id, msg_info);
msg_sflags = IPC_NOWAIT;
msg_mbuf.mtype = 10;
memcpy(msg_mbuf.mtext,"测试消息",sizeof("测试消息"));
ret = msgsnd(msg_id, &msg_mbuf, sizeof("测试消息"), msg_sflags);
if( -1 == ret)
{
printf("发送消息失败\n");
}
msg_show_attr(msg_id, msg_info);
msg_rflags = IPC_NOWAIT|MSG_NOERROR;
ret = msgrcv(msg_id, &msg_mbuf, 10,10,msg_rflags);
if( -1 == ret)
{
printf("接收消息失败\n");
}
else
{
printf("接收消息成功,长度:%d\n",ret);
}
msg_show_attr(msg_id, msg_info);
msg_info.msg_perm.uid = 8;
msg_info.msg_perm.gid = 8;
msg_info.msg_qbytes = 12345;
ret = msgctl(msg_id, IPC_SET, &msg_info);
if( -1 == ret)
{
printf("设置消息属性失败\n");
return0;
}
msg_show_attr(msg_id, msg_info);
ret = msgctl(msg_id, IPC_RMID,NULL);
if(-1 == ret)
{
printf("删除消息失败\n");
return0;
}
return0;
}
管道半双工通信程序linux,Linux进程间通信的几种方法-半双工管道,命名管道,消息队列...相关推荐
- linux安装软件的几种方法
一.rpm包安装方式步骤: 1.找到相应的软件包,比如soft.version.rpm,下载到本机某个目录: 2.打开一个终端,su -成root用户: 3.cd soft.version.rpm所在 ...
- Linux系统挂起进程的几种方法
Linux系统挂起进程的几种方法 法一 nohup run.sh & --> 输入 exit 推出,会自动 将 输出 写到 当前目录下的 nohup.txt里 法二 使用 tmux 的方 ...
- LINUX查看进程的4种方法(小结)
进程是在 CPU 及内存中运行的程序代码,而每个进程可以创建一个或多个进程(父子进程). 查看进程方法 第一种: ps aux ps命令用于报告当前系统的进程状态.可以搭配kill指令随时中断.删除不 ...
- dpkg安装软件流程_详解linux安装软件的几种方法
一.rpm包安装方式步骤: 1.找到相应的软件包,比如soft.version.rpm,下载到本机某个目录: 2.打开一个终端,su -成root用户: 3.cd soft.version.rpm所在 ...
- linux监控命令执行,你可能不知道的 即时监控 Linux 使用者执行指令的三种方法...
原标题:你可能不知道的 即时监控 Linux 使用者执行指令的三种方法 这里介绍如何在 Linux 系统上以管理者权限即时监控一般使用者所执行的任何指令. Linux 的 root 管理者可对系统进行 ...
- 找回 linux root密码的几种方法
找回 linux root密码的几种方法 第1种方法: 1.在系统进入单用户状态,直接用passwd root去更改 2.用安装光盘引导系统,进行linux rescue状态,将原来/分区挂接上来, ...
- linux修改文件名的三种方法
文章目录 前言 一.用mv命令修改文件名 二.使用cp命令修改 三.使用rename命令修改 总结 前言 我们在使用linux系统过程中为了便于记忆或整理维护,经常需要对文件名进行修改,下面文章介绍了 ...
- linux:线程同步的5种方法
linux:线程同步的5种方法 一.为什么要使用线程: 二.线程同步的5种方法 2.1 互斥量 2.2 读写锁 2.3 条件变量 2.4 自旋锁 2.5 屏障 一.为什么要使用线程: <1> ...
- linux系统 清屏命令,【转】linux清屏的几种方法
在windows的DOS操作界面里面,清屏的命令是cls,那么在linux 里面的清屏命令是什么呢?下面笔者分享几种在linux下用过的清屏方法. 1.clear命令.这个命令将会刷新屏幕,本质上只是 ...
最新文章
- 一文让你完全弄懂回归问题、激活函数、梯度下降和神经元模型实战《繁凡的深度学习笔记》第 2 章 回归问题与神经元模型(DL笔记整理系列)
- vue.js 前端开发常见问题
- 斐波那契查找算法中为什么需要把数组长度扩充到f[k]-1而不是f[k]或者f[k+1]
- vc10的C2664和C2065错误
- Jmeter测试——java测试脚本编写
- Java反射学习笔记
- win10如何删除计算机用户,Win10系统如何删除账户?Win10系统删除账户的方法
- 智慧路灯杆网关_路灯杆控制网关_路灯杆通信网关
- android别踩白块设计,别踩白块儿实例——按键精灵手机助手
- python批量导入图片_Python批量导入图片生成PowerPoint 2007+文件
- 手把手带你做一个Python打飞机游戏
- 也许通过社群找工作,是未来的趋势。
- Python那些让我疑惑许久的代码
- Springboot项目 web 添加 favicon.ico图标
- allegro PCB 检查报告中的Report DangLine以及禁用Antenna vias
- 下列关于python函数参数说法错误的是_以下关于函数说法错误的是
- AGV小车如何实现无人搬运自动导引代替人工
- 简单响应式Bootstrap框架中文官网页面模板
- 将安装包 ggs_Adapters_Linux_x64.tar 在主机的 GG_HOME 下解压,环境变量GG_HOME没有目录
- 水文遥测终端(水文遥测终端机)遥测终端机RTU 中小河流水文水雨情自动监测设备
热门文章
- hadoop大数据--深入讲解hdfs源码
- Spring整合junit4实现对方法的测试
- 微型计算机的层次结构,计算机系统层次结构微程序级
- python url编码 空格_使用请求和python时URL中的空格
- SpringBoot 中配置加载优先级
- Android 室内定位系列:1地图构建
- TCP/IP / IP 头
- java.lang包含_原因:java.lang.IllegalArgumentException:包含(1)...
- wxwidget编译安装_wxWidgets编译安装方法 | 学步园
- dedecms 备份和恢复的完整流程