linux 管道文件上机总结,[转载]LINUX 管道 fifo 等总结
Linux進程通信:命名管道FIFO小結
Linux下進程之間通信可以用命名管道FIFO完成。命名管道是一種特殊類型的文件,因為Linux中所有事物都是文件,它在文件系統中以文件名的形式存在。
在程序中,我們可以使用兩個不同的函數調用來建立管道:
#include
#include
int mkfifo(const char *filename,
mode_t mode);
int mknode(const char *filename,
mode_t mode | S_IFIFO, (dev_t) 0 );
下面先來創建一個管道:
#include?
#include?
#include?
#include?
intmain()
{
intres?=?mkfifo("/tmp/my_fifo",?0777);
if(res?==?0)
{
printf("FIFO?created/n");
}
exit(EXIT_SUCCESS);
}
#include
#include
#include
#include
int main()
{
int res = mkfifo("/tmp/my_fifo", 0777);
if (res == 0)
{
printf("FIFO created/n");
}
exit(EXIT_SUCCESS);
}
編譯這個程序:
gcc –o fifo1.c
fifo
運行這個程序:
$ ./fifo1
用ls命令查看所創建的管道
$ ls -lF
/tmp/my_fifo
prwxr-xr-x 1 root root 0 05-08
20:10 /tmp/my_fifo|
注意:ls命令的輸出結果中的第一個字符為p,表示這是一個管道。最后的|符號是由ls命令的-F選項添加的,它也表示是這是一個管道。
雖然,我們所設置的文件創建模式為“0777”,但它被用戶掩碼(umask)設置(022)給改變了,這與普通文件創建是一樣的,所以文件的最終模式為755。
打開FIFO一個主要的限制是,程序不能是O_RDWR模式打開FIFO文件進行讀寫操作,這樣做的后果未明確定義。這個限制是有道理的,因為我們使用FIFO只是為了單身傳遞數據,所以沒有必要使用O_RDWR模式。如果一個管道以讀/寫方式打開FIFO,進程就會從這個管道讀回它自己的輸出。如果確實需要在程序之間雙向傳遞數據,最好使用一對FIFO,一個方向使用一個。
當一個Linux進程被阻塞時,它并不消耗CPU資源,這種進程的同步方式對CPU而言是非常有效率的。
有關Linux下命名管道FIFO的讀寫規則可以參見之前所寫的一篇文章:。
一、實驗:使用FIFO實現進程間通信
兩個獨立的程序:
1.
生產者程序,它在需要時創建管道,然后盡可能快地向管道中寫入數據。
2.
消費者程序,它從FIFO中讀取數據并丟棄它們。
生產者程序fifo2.c:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?FIFO_NAME?"/tmp/Linux/my_fifo"
#define?BUFFER_SIZE?PIPE_BUF
#define?TEN_MEG?(1024?*?1024?*?10)
intmain()
{
intpipe_fd;
intres;
intopen_mode?=?O_WRONLY;
intbytes?=?0;
charbuffer[BUFFER_SIZE?+?1];
if(access(FIFO_NAME,?F_OK)?==?-1)
{
res?=?mkfifo(FIFO_NAME,?0777);
if(res?!=?0)
{
fprintf(stderr,?"Could?not?create?fifo?%s/n",?FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process?%d?opening?FIFO?O_WRONLY/n",?getpid());
pipe_fd?=?open(FIFO_NAME,?open_mode);
printf("Process?%d?result?%d/n",?getpid(),?pipe_fd);
if(pipe_fd?!=?-1)
{
while(bytes?
{
res?=?write(pipe_fd,?buffer,?BUFFER_SIZE);
if(res?==?-1)
{
fprintf(stderr,?"Write?error?on?pipe/n");
exit(EXIT_FAILURE);
}
bytes?+=?res;
}
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process?%d?finish/n",?getpid());
exit(EXIT_SUCCESS);
}
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/Linux/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)
int main()
{
int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes = 0;
char buffer[BUFFER_SIZE + 1];
if (access(FIFO_NAME, F_OK) == -1)
{
res = mkfifo(FIFO_NAME, 0777);
if (res != 0)
{
fprintf(stderr, "Could not create fifo %s/n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY/n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d/n", getpid(), pipe_fd);
if (pipe_fd != -1)
{
while (bytes < TEN_MEG)
{
res = write(pipe_fd, buffer, BUFFER_SIZE);
if (res == -1)
{
fprintf(stderr, "Write error on pipe/n");
exit(EXIT_FAILURE);
}
bytes += res;
}
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finish/n", getpid());
exit(EXIT_SUCCESS);
}
消費者程序fifo3.c:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?FIFO_NAME?"/tmp/Linux/my_fifo"
#define?BUFFER_SIZE?PIPE_BUF
intmain()
{
intpipe_fd;
intres;
intopen_mode?=?O_RDONLY;
charbuffer[BUFFER_SIZE?+?1];
intbytes?=?0;
memset(buffer,?'/0',sizeof(buffer));
printf("Process?%d?opeining?FIFO?O_RDONLY/n",?getpid());
pipe_fd?=?open(FIFO_NAME,?open_mode);
printf("Process?%d?result?%d/n",?getpid(),?pipe_fd);
if(pipe_fd?!=?-1)
{
do{
res?=?read(pipe_fd,?buffer,?BUFFER_SIZE);
bytes?+=?res;
}while(res?>?0);
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process?%d?finished,?%d?bytes?read/n",?getpid(),?bytes);
exit(EXIT_SUCCESS);
}
#include
#include
#include
#include
#include
#include
#include
#define FIFO_NAME "/tmp/Linux/my_fifo"
#define BUFFER_SIZE PIPE_BUF
int main()
{
int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes = 0;
memset(buffer, '/0', sizeof(buffer));
printf("Process %d opeining FIFO O_RDONLY/n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d/n", getpid(), pipe_fd);
if (pipe_fd != -1)
{
do{
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes += res;
}while(res > 0);
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read/n", getpid(), bytes);
exit(EXIT_SUCCESS);
}
編譯這兩個程序:
gcc –o fifo2
fifo2.c
gcc –o fifo3
fifo3.c
運行這兩個程序:
[root@localhost chaper12]# ./fifo2
&
à后臺執行,寫數據
[2] 23121
Process 23121 opening FIFO
O_WRONLY
[root@localhost chaper12]# time
./fifo3à讀數據
Process 24155 opeining FIFO
O_RDONLY
Process 23121 result
3
Process 24155 result
3
Process 23121
finish
Process 24155 finished, 10485760
bytes read
[2]- Done
./fifo2
real 0m0.214s
user 0m0.000s
sys 0m0.179s
以上兩個程序均是使用阻塞模式FIFO。Linux會安排好這兩個進程之間的調試,使它們在可以運行的時候運行,在不能運行的時候阻塞。因此,寫進程將在管道滿時阻塞,讀進程將在管道空時阻塞。
虛擬機上,time命令顯示,讀進程只運行了0.2秒的時間,卻讀取了10M字節的數據。這說明管道在程序之間傳遞數據是非常有效的。
二、實驗:使用FIFO的客戶/服務器應用程序
利用FIFO實現一個客戶/服務器的應用程序,服務器進程接受請求,對它們進程處理,最后把結果數據返回給發送請求的客戶方。
首先建立一個頭文件client.h,它定義了客戶和服務器程序都要用到的數據結構,并包含了必要的頭文件。
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?SERVER_FIFO_NAME?"/tmp/Linux/chaper12/server_fifo"
#define?CLIENT_FIFO_NAME?"/tmp/Linux/chaper12/client_%d_fifo"
#define?BUFFER_SIZE?PIPE_BUF
#define?MESSAGE_SIZE?20
#define?NAME_SIZE?256
typedefstructmessage
{
pid_t?client_pid;
chardata[MESSAGE_SIZE?+?1];
}message;
#include
#include
#include
#include
#include
#include
#include
#define SERVER_FIFO_NAME "/tmp/Linux/chaper12/server_fifo"
#define CLIENT_FIFO_NAME "/tmp/Linux/chaper12/client_%d_fifo"
#define BUFFER_SIZE PIPE_BUF
#define MESSAGE_SIZE 20
#define NAME_SIZE 256
typedef struct message
{
pid_t client_pid;
char data[MESSAGE_SIZE + 1];
}message;
接下來是服務器程序server.c,在這一部分,是以只讀阻塞模式打開服務器管道,用于接收客戶發送過來的數據,這些數據采用message結構體封裝。
#include?"client.h"
intmain()
{
intserver_fifo_fd;
intclient_fifo_fd;
intres;
charclient_fifo_name[NAME_SIZE];
message?msg;
char*p;
if(mkfifo(SERVER_FIFO_NAME,?0777)?==?-1)
{
fprintf(stderr,?"Sorry,?create?server?fifo?failure!/n");
exit(EXIT_FAILURE);
}
server_fifo_fd?=?open(SERVER_FIFO_NAME,?O_RDONLY);
if(server_fifo_fd?==?-1)
{
fprintf(stderr,?"Sorry,?server?fifo?open?failure!/n");
exit(EXIT_FAILURE);
}
sleep(5);
while(res?=?read(server_fifo_fd,?&msg,sizeof(msg))?>?0)
{
p?=?msg.data;
while(*p)
{
*p?=?toupper(*p);
++p;
}
sprintf(client_fifo_name,?CLIENT_FIFO_NAME,?msg.client_pid);
client_fifo_fd?=?open(client_fifo_name,?O_WRONLY);
if(client_fifo_fd?==?-1)
{
fprintf(stderr,?"Sorry,?client?fifo?open?failure!/n");
exit(EXIT_FAILURE);
}
write(client_fifo_fd,?&msg,?sizeof(msg));
close(client_fifo_fd);
}
close(server_fifo_fd);
unlink(SERVER_FIFO_NAME);
exit(EXIT_SUCCESS);
}
#include "client.h"
int main()
{
int server_fifo_fd;
int client_fifo_fd;
int res;
char client_fifo_name[NAME_SIZE];
message msg;
char *p;
if (mkfifo(SERVER_FIFO_NAME, 0777) == -1)
{
fprintf(stderr, "Sorry, create server fifo failure!/n");
exit(EXIT_FAILURE);
}
server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);
if (server_fifo_fd == -1)
{
fprintf(stderr, "Sorry, server fifo open failure!/n");
exit(EXIT_FAILURE);
}
sleep(5);
while (res = read(server_fifo_fd, &msg, sizeof(msg)) > 0)
{
p = msg.data;
while (*p)
{
*p = toupper(*p);
++p;
}
sprintf(client_fifo_name, CLIENT_FIFO_NAME, msg.client_pid);
client_fifo_fd = open(client_fifo_name, O_WRONLY);
if (client_fifo_fd == -1)
{
fprintf(stderr, "Sorry, client fifo open failure!/n");
exit(EXIT_FAILURE);
}
write(client_fifo_fd, &msg, sizeof(msg));
close(client_fifo_fd);
}
close(server_fifo_fd);
unlink(SERVER_FIFO_NAME);
exit(EXIT_SUCCESS);
}
客戶端程序client.c,這個程序用于向服務器發送消息,并接收來自服務器的回復。
#include?"client.h"
intmain()
{
intserver_fifo_fd;
intclient_fifo_fd;
intres;
charclient_fifo_name[NAME_SIZE];
message?msg;
msg.client_pid?=?getpid();
sprintf(client_fifo_name,?CLIENT_FIFO_NAME,?msg.client_pid);
if(mkfifo(client_fifo_name,?0777)?==?-1)
{
fprintf(stderr,?"Sorry,?create?client?fifo?failure!/n");
exit(EXIT_FAILURE);
}
server_fifo_fd?=?open(SERVER_FIFO_NAME,?O_WRONLY);
if(server_fifo_fd?==?-1)
{
fprintf(stderr,?"Sorry,?open?server?fifo?failure!/n");
exit(EXIT_FAILURE);
}
sprintf(msg.data,?"Hello?from?%d",?msg.client_pid);
printf("%d?sent?%s?",?msg.client_pid,?msg.data);
write(server_fifo_fd,?&msg,?sizeof(msg));
client_fifo_fd?=?open(client_fifo_name,?O_RDONLY);
if(client_fifo_fd?==?-1)
{
fprintf(stderr,?"Sorry,?client?fifo?open?failure!/n");
exit(EXIT_FAILURE);
}
res?=?read(client_fifo_fd,?&msg,?sizeof(msg));
if(res?>?0)
{
printf("received:%s/n",?msg.data);
}
close(client_fifo_fd);
close(server_fifo_fd);
unlink(client_fifo_name);
exit(EXIT_SUCCESS);
}
#include "client.h"
int main()
{
int server_fifo_fd;
int client_fifo_fd;
int res;
char client_fifo_name[NAME_SIZE];
message msg;
msg.client_pid = getpid();
sprintf(client_fifo_name, CLIENT_FIFO_NAME, msg.client_pid);
if (mkfifo(client_fifo_name, 0777) == -1)
{
fprintf(stderr, "Sorry, create client fifo failure!/n");
exit(EXIT_FAILURE);
}
server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
if (server_fifo_fd == -1)
{
fprintf(stderr, "Sorry, open server fifo failure!/n");
exit(EXIT_FAILURE);
}
sprintf(msg.data, "Hello from %d", msg.client_pid);
printf("%d sent %s ", msg.client_pid, msg.data);
write(server_fifo_fd, &msg, sizeof(msg));
client_fifo_fd = open(client_fifo_name, O_RDONLY);
if (client_fifo_fd == -1)
{
fprintf(stderr, "Sorry, client fifo open failure!/n");
exit(EXIT_FAILURE);
}
res = read(client_fifo_fd, &msg, sizeof(msg));
if (res > 0)
{
printf("received:%s/n", msg.data);
}
close(client_fifo_fd);
close(server_fifo_fd);
unlink(client_fifo_name);
exit(EXIT_SUCCESS);
}
編譯程序:
gcc –o server
server.c
gcc –o client
client.c
測試這個程序,我們需要一個服務器進程和多個客戶進程。為了讓多個客戶進程在同一時間啟動,我們使用了shell命令:
[root@localhost chaper12]# ./server
&
[26] 5171
[root@localhost chaper12]# for i in
1 2 3 4 5; do ./client & done
[27] 5172
[28] 5173
[29] 5174
[30] 5175
[31] 5176
[root@localhost chaper12]# 5172
sent Hello from 5172 received:HELLO FROM 5172
5173 sent Hello from 5173
received:HELLO FROM 5173
5174 sent Hello from 5174
received:HELLO FROM 5174
5175 sent Hello from 5175
received:HELLO FROM 5175
5176 sent Hello from 5176
received:HELLO FROM 5176
分析這個例子,服務器以只讀模式創建它的FIFO并阻塞,直到第一個客戶以寫方式打開同一現個FIFO來建立連接為止。此時,服務器進程解除阻塞并執行sleep語句,這使得來自客戶的數據排除等候。在實際應用程序中,應該把sleep語句刪除,這里面只是為了演示當有多個客戶請求同時到達時,程序的正確操作方法。
與此同時,在客戶端打開服務器FIFO后,它創建自己唯一的一個命名管道以讀取服務器返回的數據。完成這些工作后,客戶發送數據給服務器(如果管道滿或服務器仍處于休眠就阻塞),并阻塞于對自己FIFO的read調用上,等待服務器響應。
接收到來自客戶的數據后,服務器處于它,然后以寫的方式打開客戶管道并將處理后的數據返回,這將解除客戶端的阻塞狀態,客戶程序就可以從自己的管道里面讀取服務器返回的數據了。
整個處理過程不斷重復,直到最后一個客戶關閉服務器管道為止,這將使服務器的read調用失敗(返回0),因為已經沒有進程以寫方式打開服務器管道了。如果這是一個真正的服務器進程的話,它還需要繼續等待其他客戶的請求,我們就需要對它進行修改,有兩種方法:
(1)對它自己的服務器管道打開一個文件描述符,這樣read調用將阻塞而不是返回0。
(2)當read調用返回0時,關閉并重新打開服務器管道,使服務器進程阻塞在open調用處以等待客戶的到來,就像它最初啟動時那樣。
總結
以上是生活随笔為你收集整理的linux 管道文件上机总结,[转载]LINUX 管道 fifo 等总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 部署项目的问题(三)—— node启动服
- 下一篇: linux通过spi和stm32通信,双