1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 管道半双工通信程序linux Linux进程间通信的几种方法-半双工管道 命名管道 消息队列...

管道半双工通信程序linux Linux进程间通信的几种方法-半双工管道 命名管道 消息队列...

时间:2022-03-01 12:07:03

相关推荐

管道半双工通信程序linux Linux进程间通信的几种方法-半双工管道 命名管道 消息队列...

1、半双工管道

简单实现

半双工管道可以实现父进程和子进程之间或者子进程之间(前提是有共同的祖先)的通信

因为是半双工,所以两端不可能同时读取,而是一端读一端取,而且当一端分配到读任务后,那么他就固定了,不能再担当写的角色了,相反亦然。

测试程序如下:

#include

#include

#include

#include

#include

intmain(void)

{

intfd[2],nbytes;

pid_tchildpid;

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("Receivedstring:%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

#defineFIFO_CHANNEL"my_fifo"/*宏定义,fifo路径*/

intmain()

{

intfd;

charbuf[80];

if(mkfifo(FIFO_CHANNEL,0777)==-1)/*创建命名管道,返回-1表示失败*/

{

perror("Can'tcreateFIFOchannel");

return1;

}

if((fd=open(FIFO_CHANNEL,O_RDONLY))==-1)/*以只读方式打开命名管道*/

{

perror("Can'topentheFIFO");

return1;

}

while(1)/*不断从管道中读取信息*/

{

read(fd,buf,sizeof(buf));

printf("MessagefromClient:%s\n",buf);

sleep(3);/*sleep3s*/

}

close(fd);/*关闭管道*/

return0;

}

client.c

#include

#include

#include

#include

#include

#defineFIFO_CHANNEL"my_fifo"/*宏定义,fifo路径*/

intmain()

{

intfd;

chars[]="Hello!";

if((fd=open(FIFO_CHANNEL,O_WRONLY))==-1)/*以读写方式打开命名管道,返回-1代表失败*/

{

perror("Can'topentheFIFO");

return1;

}

while(1)/*不断向管道中写信息*/

{

write(fd,s,sizeof(s));

printf("Write:%s\n",s);

sleep(3);/*sleep3s*/

}

close(fd);/*关闭管道*/

return0;

}

结果截图:

此时文件系统中会多一个my_fifo的特殊文件。

3、消息队列

先介绍消息队列常用函数

#include

#include

key_tftok(constchar*pathname,intproj_id);

注意: pathname必须是已经存在的目录

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过该ftok函数得到。

需要用 mkdir -p /ipc/msg

#include

#include

#include

intmsgget(key_tkey,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_tmsgrcv(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

voidmsg_show_attr(intmsg_id,structmsqid_dsmsg_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_tkey;

structmsgmbuf{

intmtype;

charmtext[10];

};

structmsqid_dsmsg_info;

structmsgmbufmsg_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;

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。