linux管道命令标识符是什么,Linux系统编程2:管道

21bc69ca7dd5

图片来自网络

1. 简介

查看管道命令:man 7 pipe

分类

匿名管道

FIFO管道/命名管道

2 匿名管道

2.1 单工管道

程序进程与Shell命令行进程单项通信。

① 打开管道FILE* popen (const char *command, const char *open_mode)

参数

No.

参数

含义

1

command

命令行字符串

2

open_mode

"r"只读"w"只写

返回值

No.

返回值

含义

1

NULL

文件描述符

2

非NULL

打开失败

② 读取size_t fread ( void *buffer, size_t size, size_t count, FILE *stream)

参数

No.

参数

含义

1

buffer

用于接收数据的内存地址

2

size

读取每个数据项的字节数

3

count

数据项个数

4

stream

输入流

返回值

No.

返回值

含义

1

>count

出错

2

正数

真实读取的数据项个数

③ 写入size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream)

参数

No.

参数

含义

1

buffer

写入数据的内存地址

2

size

写入数据项的字节数

3

count

写入数据项的个数

4

stream

目标文件指针

返回值

No.

返回值

含义

1

>count

出错

2

正数

真实写入的数据项个数

④ 关闭管道int pclose(FILE *stream);

参数

No.

参数

含义

1

stream

文件描述符

返回值

No.

返回值

含义

1

-1

失败

2

0

成功

示例

write

#include

#include

#include

int main(){

FILE* fd = popen("wc","w");

//FILE* fd = popen("ls -l","r");

//char str[] = "123 456";

char str[] = "123 456\n";

size_t n = fwrite(str,sizeof(char),sizeof(str),fd);

if(n > sizeof(str)){

fprintf(stderr,"FILE:%d,LINE:%d-fwrite error",__FILE__,__LINE__);

exit(EXIT_FAILURE);

}

pclose(fd);

}

read

#include

#include

#include

int main(){

FILE* fd = popen("ps -ef","r");

//FILE* fd = popen("ls -l","r");

char buf[BUFSIZ];

size_t count = 0;

printf("read data:\n");

do{

memset(buf,'\0',BUFSIZ);

size_t n = fread(buf,sizeof(char),BUFSIZ-1,fd);

if( n > BUFSIZ - 1 ){

perror("fread error");

exit(EXIT_FAILURE);

}

count += n;

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

}while(!feof(fd));

printf("total size:%ld\n",count);

pclose(fd);

}

本质

启动shell和命令两个进程,从命令进程中读/写文件流。

解决exec和system无法返回输出数据问题

特点

方便使用系统自带功能,并且可以执行比较复杂Shell

默认启动两个进程,效率较低。

操作

管道

文件

打开

popen()

fopen()

关闭

pclose()

fclose()

2.2 半双工管道

① 创建管道int pipe(int filedes[2])

参数

No.

参数

含义

1

filedes[0]

2

filedes[1]

返回值

No.

返回值

含义

1

-1

失败

2

0

成功

② 读取ssize_t write(int fd, const void *buf, size_t nbyte)

参数

No.

参数

含义

1

fd

文件描述符

2

buf

写入数据的内存单元

3

nbyte

写入文件指定的字节数

返回值

No.

返回值

含义

1

-1

出错

2

正数

写入的字节数

③ 写入ssize_t read(int fd, void *buf, size_t count)

参数

No.

参数

含义

1

fd

文件描述符

2

buf

读取数据的内存单元

返回值

No.

返回值

含义

1

-1

出错

2

0

无数据

3

正数

读取的字节数

④ 控制int fcntl(int fd, int cmd, long arg)

如果管道是空的,read()默认是阻塞

参数

No.

参数

含义

1

fd

文件描述符

2

cmd

F_GETFL:获取文件描述符状态;F_SETFL:设置文件描述符状态;

3

arg

O_NONBLOCK:非阻塞;O_BLOCK:阻塞

把文件描述符改为非阻塞的fcntl(filedes,F_SETFL,O_NONBLOCK);

⑤ 关闭管道close(filedes)

示例

单进程读写管道

#include

#include

#include

int main(){

int fd[2];

pipe(fd);

char in[] = "Hello pipe";

write(fd[1],in,sizeof(in));

printf("write:%s\n",in);

char out[sizeof(in)]={0};

ssize_t n = read(fd[0],out,sizeof(out));

if(-1 == n){

perror("read error");

return -1;

}

printf("read:%s\n",out);

close(fd[0]);

close(fd[1]);

}

两进程读写

#include

#include

#include

int main(){

int fd[2];

pipe(fd);

if(!fork()){// child

char in[] = "Hello pipe";

write(fd[1],in,sizeof(in));

printf("child %d write:%s\n",getpid(),in);

}else{// parent

char out[BUFSIZ]={0};

ssize_t n = read(fd[0],out,sizeof(out));

if(-1 == n){

perror("read error");

return -1;

}

printf("parent %d read:%s\n",getpid(),out);

}

close(fd[0]);

close(fd[1]);

}

两进程先读后写

#include

#include

#include

int main(){

int fd[2];

pipe(fd);

if(!fork()){// child

char in[] = "Hello pipe";

sleep(3);

write(fd[1],in,sizeof(in));

printf("child %d write:%s\n",getpid(),in);

}else{// parent

char out[BUFSIZ]={0};

ssize_t n = read(fd[0],out,sizeof(out));

if(-1 == n){

perror("read error");

return -1;

}

printf("parent %d read:%s\n",getpid(),out);

}

close(fd[0]);

close(fd[1]);

}

两进程先读后写(非阻塞)

#include

#include

#include

#include

int main(){

int fd[2];

pipe(fd);

if(!fork()){// child

char in[] = "Hello pipe";

sleep(3);

write(fd[1],in,sizeof(in));

printf("child %d write:%s\n",getpid(),in);

}else{// parent

fcntl(fd[0],F_SETFL,O_NONBLOCK);

char out[BUFSIZ]={0};

ssize_t n = read(fd[0],out,sizeof(out));

if(-1 == n){

perror("read error");

return -1;

}

printf("parent %d read:%s\n",getpid(),out);

}

close(fd[0]);

close(fd[1]);

}

更加规范的写法(关闭不需要的管道)

#include

#include

#include

#include

int main(){

int fd[2];

pipe(fd);

if(!fork()){// child

close(fd[0]);

char in[] = "Hello pipe";

sleep(3);

write(fd[1],in,sizeof(in));

printf("child %d write:%s\n",getpid(),in);

close(fd[1]);

}else{// parent

close(fd[1]);

fcntl(fd[0],F_SETFL,O_NONBLOCK);

char out[BUFSIZ]={0};

ssize_t n = read(fd[0],out,sizeof(out));

if(-1 == n){

perror("read error");

return -1;

}

printf("parent %d read:%s\n",getpid(),out);

close(fd[0]);

}

}

2.3 FIFO管道/命名管道

① 创建命名管道int mkfifo(pathname,mode)

参数

No.

参数

含义

1

pathname

文件路径,文件必须不存在

2

mode

模式

返回值

No.

返回值

含义

1

0

成功

2

非零

失败

示例

#include

#include

int main(){

if(-1 == mkfifo("/tmp/test",0644)){

perror("mkfifo error");

return 1;

}

}

注意:

管道文件通常在/tmp目录下创建。

管道文件大小通常是0

② 打开FIFO文件int open(const char *path, int mode)

参数

No.

参数

含义

1

pathname

文件路径

2

mode

模式

模式

No.

模式

含义

1

O_RDONLY

阻塞只读

2

O_RDONLY | O_NONBLOCK

非阻塞只读

3

O_WRONLY

阻塞只写

4

O_WRONLY | O_NONBLOCK

非阻塞只写

返回值

No.

返回值

含义

1

-1

失败

2

其他

文件描述符

示例

阻塞读

#include

#include

#include // open() O_RDONLY

using namespace std;

#define BUFSIZE 258

int main(){

// 打开命名管道

int fd = open("/tmp/fifo",O_RDONLY);

if(-1 == fd){

perror("open error");

return EXIT_FAILURE;

}else{

cout << "filo open ok" << endl;

}

// 读取字符串

char buffer[BUFSIZE] = {0};

read(fd,buffer,BUFSIZE);

// 打印读取的字符串

cout << buffer << endl;

// 关闭管道

close(fd);

return EXIT_SUCCESS;

}

堵塞写

#include

#include // open() O_WRONLY

#include

using namespace std;

int main(){

// 打开命名管道

int fd = open("/tmp/fifo",O_WRONLY);

if(-1 == fd){

perror("open error");

return EXIT_FAILURE;

}else{

cout << "filo open ok" << endl;

}

// 读取字符串

string str;

getline(cin,str);

// 写入管道

write(fd,str.c_str(),str.size()+1);

// 关闭管道

close(fd);

return EXIT_SUCCESS;

}

写非阻塞

说明:

只需要在open()添加O_NONBLOCK。

写open()非阻塞,读open()阻塞的情况。读open()需要先执行,否则,写open()会出现No such device or address。

#include

#include // open() O_WRONLY O_NONBLOCK

#include

using namespace std;

int main(){

// 打开命名管道

int fd = open("/tmp/fifo",O_WRONLY|O_NONBLOCK);

if(-1 == fd){

perror("open error");

return EXIT_FAILURE;

}else{

cout << "filo open ok" << endl;

}

// 读取字符串

string str;

getline(cin,str);

// 写入管道

write(fd,str.c_str(),str.size()+1);

// 关闭管道

close(fd);

return EXIT_SUCCESS;

}

读非阻塞

说明:

只需要在open()添加O_NONBLOCK。

写open()阻塞,读open()非阻塞的情况。读read()需要处理写open()未执行(read()返回0)和读不到数据(写open()打开但是没有写数据,read()返回-1)的情况。

#include

#include

#include // open() O_RDONLY O_NONBLOCK

using namespace std;

#define BUFSIZE 258

int main(){

// 打开命名管道

int fd = open("/tmp/fifo",O_RDONLY|O_NONBLOCK);

if(-1 == fd){

perror("open error");

return EXIT_FAILURE;

}else{

cout << "filo open ok" << endl;

}

// 读取字符串

char buffer[BUFSIZE] = {0};

int n = -1;

while(n<=0){

n=read(fd,buffer,BUFSIZE);

}

// 打印读取的字符串

cout << buffer << endl;

// 关闭管道

close(fd);

return EXIT_SUCCESS;

}

特点

可以是非亲缘进程之间

FIFO首先会阻塞在open(),等待读写文件的文件描述符都打开。接着阻塞在read()/write()操作,读写操作需要同时执行。

案例

FIFO工具箱

3. 通信分类

21bc69ca7dd5

只写单工

21bc69ca7dd5

只读单工

21bc69ca7dd5

半双工

21bc69ca7dd5

全双工

No.

类型

创建/打开

关闭

1

单工

popen()

pclose

read()

write()

2

半双工

pipe()/open()

close()

read()

write()

3

FIFO半双工

mkfifo()/open

close()/unlink()

read()

write()

4

全双工

socketpair()

close()

read()

write()

3.1 单进程管道

21bc69ca7dd5

单进程管道

管道通常用于进程间通信

3.2 父子进程单向管道

3.3.1 概念图解

21bc69ca7dd5

父子进程管道

21bc69ca7dd5

父进程关闭fd[0] 子进程关闭fd[1]

21bc69ca7dd5

父子进程单向管道

3.3.2 原理图解

21bc69ca7dd5

21bc69ca7dd5

21bc69ca7dd5

3.3 父子进程双向管道

21bc69ca7dd5

父子进程双向管道

4. 文件描述符

4.1 Linux文件读写与标准C的文件读写

文件描述符

文件描述符

文件流

数据

int整数

FILE指针

标准

POSIX

ANSI C

打开

open

fopen

关闭

close

fclose

read

fread

write

fwrite

定位

lseek

fseek

文件流是文件描述符之上的封装。文件流通过增加缓冲区减少读写系统调用次数来提高读写效率。在进程的用户空间封装的FILE结构,以提高可移植性和效率。

4.2 文件描述符原理

Linux内核使用三个关联的数据结构,表示打开的文件。

21bc69ca7dd5

21bc69ca7dd5

打开不同的文件

21bc69ca7dd5

打开相同的文件

21bc69ca7dd5

父子进程共享文件

21bc69ca7dd5

4.3 命令lsof

lsof(list open files):列出当前系统打开文件

列名含义

No.

列名

含义

1

COMMAND

进程的名称

2

PID

进程标识符

3

USER

进程所有者

4

FD

文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等

5

TYPE

文件类型,如PIPE、DIR、REG等

6

DEVICE

指定磁盘的名称

7

SIZE

文件的大小

8

NODE

索引节点(文件在磁盘上的标识)

9

NAME

打开文件的确切名称

常用方法

No.

命令

作用

1

lsof 文件名

查看文件打开信息

2

lsof -d 文件描述符

查看文件描述符信息

3

lsof -p PID

查看进程PID打开的文件信息

4.3 文件描述符复制

分类

文件描述符

文件号

标准输入

STDIN_FILENO

0

标准输出

STDOUT_FILENO

1

标准出错信息

STDERR_FILENO

2

内核为每个进程创建的文件描述符。

① 函数int dup(int oldfd)

参数

No.

参数

含义

1

oldfd

旧文件描述符

返回值

No.

返回值

含义

1

-1

失败

2

其他

新文件描述符

② 函数int dup2(int oldfd, int newfd)

参数

No.

参数

含义

1

oldfd

旧文件描述符

2

newfd

新文件描述符

返回值

No.

返回值

含义

1

-1

失败

2

其他

最小及尚未使用的文件描述符

示例

复制标准输出

新文件描述符与旧文件描述符不同,但是具备旧文件描述符功能

#include

#include

#include

#include

int main(){

int fd = dup(STDOUT_FILENO);

fprintf(fdopen(fd,"w"),"%d printf:Hello dup\n",fd);

}

复制文件描述符

新文件描述符与旧文件描述符不同,但是具备旧文件描述符功能

#include

#include

#include

#include

#include

#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)

int main(){

int fd = open("./test",O_CREAT|O_RDWR,FILE_MODE);

char str[]="Hello dup\n";

write(fd,str,sizeof(str));

int cp_fd = dup(fd);

printf("copy %d to %d",fd,cp_fd);

write(cp_fd,str,sizeof(str));

//fprintf(fdopen(fd,"w"),"%d printf:Hello dup\n",fd);

close(fd);

}

把文件描述符重定向(复制)到标准输出

printf()直接输出到文件中,不再输出到终端

#include

#include

#include

#include

#include

#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)

int main(){

int fd = open("./test",O_CREAT|O_RDWR,FILE_MODE);

char str[]="Hello dup\n";

dup2(fd,STDOUT_FILENO);

printf("%d printf:Hello dup\n",fd);

}

把文件描述符重定向(复制)到标准输出,并且输出后还原

注意把标准输出流从文件重定向(复制)回终端,需要清除缓冲区。

#include

#include

#include

#include

#include

#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)

int main(){

int save_fd = dup(STDOUT_FILENO);

int fd = open("./test",O_CREAT|O_RDWR,FILE_MODE);

if(-1 == dup2(fd,STDOUT_FILENO)){

perror("dup2 error0");

return 1;

}

close(fd);

printf("%d printf:Hello dup\n",fd);

fflush(stdout);// 一定要清除缓冲区,否则会输出到终端

if(-1 == dup2(save_fd,STDOUT_FILENO)){

perror("dup2 error");

return 1;

}

close(save_fd);

printf("%d printf:this is save\n",save_fd);

}

特点

必须是亲缘进程之间

dup()/dup2()原理图

21bc69ca7dd5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值