UNIX环境高级编程习题3.2 自己实现dup2函数功能

1. 题目
编写一个与dup2功能相同的函数,要求不能调用fcntl函数,并且要有正确的出错处理
2. 函数原型

#include <unistd.h>
int dup2(int oldfd, int newfd);

3. 函数介绍

dup2()
       The dup2() system call performs the same task as dup(), but instead  of
       using  the lowest-numbered unused file descriptor, it uses the descrip‐
       tor number specified in newfd.  If the descriptor newfd was  previously
       open, it is silently closed before being reused.

       The  steps  of  closing  and reusing the file descriptor newfd are per‐
       formed atomically.  This is  important,  because  trying  to  implement
       equivalent  functionality  using close(2) and dup() would be subject to
       race conditions, whereby newfd might be reused between the  two  steps.
       Such  reuse  could  happen because the main program is interrupted by a
       signal handler that allocates a file descriptor, or because a  parallel
       thread allocates a file descriptor.

       Note the following points:

       *  If  oldfd  is  not a valid file descriptor, then the call fails, and
          newfd is not closed.

       *  If oldfd is a valid file descriptor, and newfd has the same value as
          oldfd, then dup2() does nothing, and returns newfd.
RETURN VALUE
       On success, these system calls return the new descriptor.  On error, -1
       is returned, and errno is set appropriately.

上面是man手册的描述,错误码和详细信息,还是请大家自己看man手册。man手册对于函数的介绍非常详细。对于dup2的详细介绍还请看《UNIX环境高级编程》这本圣经。
dup2可以用newfd参数指定新描述符的数值。函数成功返回新的描述符(即newfd),失败返回-1。
不过需要注意如下几点:

  • 如果oldfd非法,则返回-1,newfd不关闭
  • 如果newfd已经打开,则先将其关闭
  • 如果newfd==oldfd,dup2则直接返回newfd,而不关闭它
  • dup2是个原子操作,且能被信号打断

4. 解题思路
既然不让用fcntl函数,就只能用多次调用dup函数,直到其返回值为newfd。oldfd和newfd的相应的合法检查,也得通过dup实现。

5. 我的实现
由于用了VA_ARGS,编译的时候要加上-std=c99参数如下:

saber@ubuntu:~$ gcc -g -std=c99 -o mydup2 MyDup2.c

由于是练习题,所以程序的测试部分,简单写了,没有详细去便利所有情况了。注意这个实现不是原子操作的,还有里面的函数close函数也是能被信号打断的。一般对于能够被信号打断的系统调用,都要自己封装一个函数,判断errno是否为EINTR。
代码如下:

/*
*UNIX高级环境编程习题3.2
*Author: Yang
*Date: 2017.03.05
*/

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

#define ERROR_SUCCESS 0
#define ERROR_FAILED -1
#define INVALID_FD -1
#define INVALID_VALUE -1

#define PRINTF(format, ...) \
    do {\
        printf("[<%s>@%s-%d]: " format,\
            __func__, __FILE__, __LINE__, ##__VA_ARGS__);\
    }while(0)

int Dup2Check(int iOldFd, int iNewFd)
{
    //Fd的值由调用者保证
    assert(iOldFd > 0);
    assert(iNewFd > 0);

    int iTempFd = INVALID_FD;
    //系统中进程可以打开的最大文件数
    int iTableSize = getdtablesize();
    int iFlag = INVALID_VALUE;

    if (iTableSize < iNewFd)
    {
        PRINTF("new fd(%d) out of system limit.\n", iNewFd);
        return ERROR_FAILED;
    }

    //不能用fcntl,就用dup检查iOldFd的有效性
    if ((iTempFd = dup(iOldFd)) == INVALID_FD)
    {
        PRINTF("dup old fd(%d) failed, (errno:%d) %s.\n", iOldFd, errno, strerror(errno));
        return ERROR_FAILED;
    }
    else
    {
        if (ERROR_FAILED == close(iTempFd))
        {
            PRINTF("close temp fd(%d) failed, (errno:%d) %s.\n", iTempFd, errno, strerror(errno));
            return ERROR_FAILED;
        }
    }

    //检查iNewfd的有效性,如果打开则关闭
    if ((iTempFd = dup(iNewFd)) == INVALID_FD)
    {
        //iNewFd is not openned
        if (EBADF == errno)
        {
            return ERROR_SUCCESS;
        }
        else
        {
            PRINTF("dup new fd(%d) failed, (errno:%d) %s.\n", iNewFd, errno, strerror(errno));
            return ERROR_FAILED;
        }
    }
    else
    {
        if (ERROR_FAILED == close(iNewFd))
        {
            PRINTF("close new fd(%d) failed, (errno:%d) %s.\n", iNewFd, errno, strerror(errno));
            return ERROR_FAILED;
        }

        if (ERROR_FAILED == close(iTempFd))
        {
            PRINTF("close temp fd(%d) failed, (errno:%d) %s.\n", iTempFd, errno, strerror(errno));
            return ERROR_FAILED;
        }
    }

    return ERROR_SUCCESS;
}

int MyDup2(int iOldFd, int iNewFd)
{
    assert(iNewFd > 0);

    int i;
    int j;
    int iDupFd = INVALID_FD;
    int iIndexHit = INVALID_VALUE;
    int iFdArray[iNewFd];

    if (ERROR_SUCCESS != Dup2Check(iOldFd, iNewFd))
    {
        return INVALID_FD;
    }

    if (iOldFd == iNewFd)
    {
        PRINTF("MyDup2 success. old fd(%d), new fd(%d)\n", iOldFd, iNewFd);
        return iNewFd;
    }

    for (i = 0; i < iNewFd; i++)
    {
        iFdArray[i] = INVALID_FD;
    }

    for (i = 0; i < iNewFd; i++)
    {
        iDupFd = dup(iOldFd);
        if (iDupFd == INVALID_FD)
        {
            break;
        }
        iFdArray[i] = iDupFd;

        if (iDupFd == iNewFd)
        {
            iIndexHit = i;
            break;
        }
     }

    for(j = 0; j < i; j++)
    {
        (void)close(iFdArray[j]);
    }

    if (INVALID_VALUE != iIndexHit)
    {
         PRINTF("MyDup2 success. old fd(%d), new fd(%d)\n", iOldFd, iNewFd);
         return iNewFd;
    }
    else
    {
        PRINTF("MyDup2 failed. errno=%d\n", errno);
        return INVALID_FD;
    }
}

int main(int argc, char *argv[])
{
    int iOldFd = INVALID_FD;
    int iNewFd = 10;
    char *sFileName = "dup2test.txt";

    if ((iOldFd = open(sFileName, O_RDWR)) >0 )
    {
        MyDup2(iOldFd, iNewFd);
    }

    MyDup2(1, iNewFd);
    MyDup2(100, iNewFd);

    (void)close(iOldFd);
    if(iOldFd != iNewFd)
        (void)close(iNewFd);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值