tee函數在兩個管道文件描述符之間複製數據,也是零拷貝操作,它不消耗數據,所以其源文件描述符上的數據仍然可用於後續的讀操作,其函數原型如下:
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
其參數和上篇splice函數的參數意義相同,但是fd_in和fd_out必須是管道文件描述符,函數成功返回在兩個文件描述符之間複製的數據字節數,返回0沒有複製任何數據,返回-1表示失敗並設置errno。
下例代碼用tee函數實現了linux tee命令的基本功能:
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<stdlib.h>
#define LEN 654
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("usage: %s <file>\n",argv[0]);
exit(1);
}
int pipe_filefd[2],pipe_outfd[2];
pipe(pipe_filefd);
pipe(pipe_outfd);
int filefd = open(argv[1],O_CREAT | O_WRONLY | O_TRUNC,0644);
if(filefd == -1)
perror("open error");
//將標準輸入內容輸入管道中
int n = splice(STDIN_FILENO,NULL,pipe_outfd[1],NULL,LEN,SPLICE_F_MORE | SPLICE_F_MOVE);
if(n == -1)
{
perror("splice_1 error");
exit(1);
}
//將pipe_outfd管道里的數據複製到pipe_filefd管道中
int ret = tee(pipe_outfd[0],pipe_filefd[1],n,SPLICE_F_NONBLOCK);
if(ret == -1)
{
perror("tee error");
exit(1);
}
//pipe_outfd管道中的數據輸出到標準輸出
ret = splice(pipe_outfd[0],NULL,STDOUT_FILENO,NULL,n,SPLICE_F_MORE | SPLICE_F_MOVE);
if(ret == -1)
{
if(errno == EINVAL)
printf("----------------\n");
perror("splice_2 error");
exit(1);
}
//將pipe_filefd管道中的數據輸出到文件中。
splice(pipe_filefd[0],NULL,filefd,NULL,n,SPLICE_F_MORE | SPLICE_F_MOVE);
close(filefd);
close(pipe_filefd[0]);
close(pipe_filefd[1]);
close(pipe_outfd[0]);
close(pipe_outfd[1]);
return 0;
}
上述代碼有個BUG,就是編譯後運行會出現下面的情況
這裏是splice函數報錯,通過驗證錯誤碼是EINVAL,通過上篇博客我們可以知道這個錯誤碼的含義。
文件系統肯定支持splice函數,我代碼中open也設置了截斷標誌,文件描述符有管道文件描述符,offset參數我傳的是NULL,也不存在隨機訪問,神奇的是我新打開一個終端,再運行就可以了,當我再次編譯後運行又會發生同樣的錯誤。效果如下:
這個就很神奇,有哪位大神看到了知道原因,跪求指點。