文件重定向-----dup()和dup2()函数

前言:

dup()和dup2()用于复制文件描述符的函数

在linux下,一切皆文件。当文件被打开时,会返回文件描述符用于操作该文件,从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2);)0表示标准输入,1表示标准输出,2表示标准错误。一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看。

如下图

当我们open一个文件的时候,文件描述符里默认打开0,1,2,对应于/dev/tty这个东东,open返回的是文件描述符表最小的可用整数fd。
在这里插入图片描述
当调用dup函数时,内核在进程中创建一个新的文件描述符,此描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。
  dup2和dup的区别就是可以用newfd参数指定新描述符的数值,如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。dup2函数返回的新文件描述符同样与参数oldfd共享同一文件表项。
APUE用另外一个种方法说明了这个问题:
  实际上,调用dup(oldfd)等效于,fcntl(oldfd, F_DUPFD, 0)
  而调用dup2(oldfd, newfd)等效于,close(oldfd);fcntl(oldfd, F_DUPFD, newfd);

函数原型
#include <unistd.h> 
int dup(int oldfd); 
int dup2(int oldfd, int newfd);

返回值

成功:dup函数返回当前系统可用的最小整数值。
    dup2函数返回第一个不小于newfd的整数值。也就是分为两种情况:
    ①、如果newfd已经打开,则先将其关闭,再复制文件描述符。
    ②、如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。

失败:均返回-1,并设置errno。

注意:通过dup和dup2创建的文件描述符并不继承原文件描述符的属性。比如close-on-exec和non-blocking

注:其中最重要的就是可以用来重定向文件,也就是可以把标准输出/标准输入弄到文件上。

dup例子:

这里可以看出dup的作用,复制文件描述符,指向这个文件。
在这里插入图片描述
结果图:
在这里插入图片描述

dup2例子:

这里的话只要是实现一下标准输出重定向,然后又改回来,很显然就要用dup函数先备份一下之前的东东。(也就是/dev/tty)
注意:fflush函数必须加上,这个函数自行查阅,不加上的话文件根本没有内容,为什么呢?跟这个函数有关,读者可自行查阅资料,了解这个函数作用,哈哈。。。
在这里插入图片描述
结果图:
在这里插入图片描述

CGI服务器的原理

在CGI程序,当浏览器使用post方法提交表单数据时,CGI读数据是从标准输入stdin,写数据是写到标准输出stdout(C中使用printf)。按照我们正常的理解,printf的输出应该在终端显示,这是什么原因呢?也是和我们今天介绍的这两个复制文件描述符的函数有关。我们先来看一下代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( sock, 5 );
    assert( ret != -1 );

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        close( STDOUT_FILENO );
        dup( connfd );
    //dup2(connfd, STDOUT_FILENO);
        printf( "hello world!\n" );
        close( connfd );
    }

    close( sock );
    return 0;
}

在上述代码中,我们先关闭标准输出STDOUT_FILENO(1),然后通过dup函复制connfd,因为dup函数返回的系统第一个可用的文件描述符,所以其返回的是1,这样一来,服务器输出到标准输出的内容就会被客户端所获得而不是输出到服务器的终端上。这就是CGI服务器的基本工作原理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值