浅析dup()和dup2()函数

23 篇文章 3 订阅
15 篇文章 4 订阅

前言

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

  今天,我们主要说两个用于复制文件描述符的函数dup()和dup2()。

dup()、dup2()函数

函数原形

#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.c

//测试dup函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    int fd = open("a.txt", O_RDWR | O_CREAT);
    if(fd == -1)
    {
        perror("open");
        exit(1);
    }

    printf("file open fd = %d\n", fd);

    // 找到进程文件描述表中第一个可用的文件描述符
    // 将参数指定的文件复制到该描述符后,返回这个描述符
    int ret = dup(fd);  //使ret和fd指向同一个文件
    if(ret == -1)
    {
        perror("dup");
        exit(1);
    }

    printf("dup fd = %d\n", ret);

    char* buf = "hello";
    char* buf1 = " world\n";

    write(fd, buf, strlen(buf));
    write(ret, buf1, strlen(buf1));

    close(fd);


    return 0;
}

dup2.c

//测试dup2函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    int fd = open("b.txt", O_RDWR | O_CREAT);
    if(fd == -1)
    {
        perror("open");
        exit(1);
    }

int fd1 = open("a.txt", O_RDWR);
if(fd1 == -1)
{
    perror("open");
    exit(1);
}

printf("fd = %d\n", fd);
printf("fd1 = %d\n", fd1);

int ret = dup2(fd, fd1);  //让fd1和fd同时指向b.txt
if(ret == -1)
{
    perror("dup2");
    exit(1);
}
printf("current fd = %d\n", ret);
char* buf = "hello ";
char* buf1 = " world!";
write(fd, buf, strlen(buf));
write(fd1, buf1 , strlen(buf1));

close(fd);
close(fd1);


return 0;

}

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服务器的基本工作原理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值