管道半双工通信程序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内核合并dtb文件,c –
- 下一篇: linux sftp自动输入密码,使用a