【问题标题】:Strange behavior of dup system calldup 系统调用的奇怪行为
【发布时间】:2018-03-29 16:08:02
【问题描述】:

所以,我在阅读有关 Linux 中的文件 I/O 的文章并想尝试一下。但是,我在代码中遇到了两个奇怪的行为,我正在努力找出它们的原因。

/*
 * This program shows the usage of dup and dup2 functions
 */

#include "creep.h"
#include <fcntl.h>

#define BUFSIZE 2048

int main(int argc, char *argv[]) {

    int fd, dup_fd, n;
    char buf[BUFSIZE], buff[BUFSIZE];
    if (argc != 2) 
        err_quit("Usage: dup <filename>\n");

    fd = open(argv[1], O_RDWR);

    while ((n = read(fd, buf, BUFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");

    if (n < 0)
        err_sys("read error");

    dup_fd = dup(fd);

    while ((n = read(dup_fd, buff, BUFSIZE)) > 0)
        if (write(STDOUT_FILENO, buff, n) != n)
            err_sys("write error");

    if (n < 0)
        err_sys("read error");

    printf("\nValues are : %d and %d\n", fd, dup_fd);

    close(fd);
    exit(0);
}

现在,当我运行这个程序时:-

# ./dup dup.c

它实际上只打印文件一次,而不是第二个重复描述符。

我对上面的内容进行了搜索,希望找出正在发生的事情,但我只能看到这个 ->

open("dup.c", O_RDWR)                   = 3
read(3, "/*\n * This program shows the usa"..., 2048) = 708
write(1, "/*\n * This program shows the usa"..., 708/
------------omitting the file from trace-------------
read(3, "", 2048)                       = 0
dup(3)                                  = 4
read(4, "", 2048)                       = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f170112d000
write(1, "\n", 1
)                       = 1
write(1, "Values are : 3 and 4\n", 21Values are : 3 and 4
)  = 21
close(3)   

第一次在fd 上进行读/写调用时,缓冲区中有文件内容,这有点奇怪。但是对于dup_fd,缓冲区是空的。我不知道为什么会这样。以前,我两次都使用相同的缓冲区,我认为我应该使用单独的缓冲区,但无济于事。

我读到dup 函数给出了编号最小的可用文件描述符,它是原始文件描述符的副本。重复是指进程表条目中的文件指针指向文件描述符的同一个文件表。

为什么我无法读取具有重复文件描述符的文件。是不是我做错了什么?

creep.h 头文件:-

/*
 * My own header, to be included before all standard system headers written by me.
 */
#ifndef _CREEP_H
#define _CREEP_H

#define _POSIX_C_SOURCE 200809L

#if defined(SOLARIS)
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 700
#endif

#include <sys/types.h>   /* some systems will require this */
#include <sys/stat.h>
#include <sys/termios.h>   /* for winsize */

#if defined(MACOS) || !defined(TIOCGWINSZ)
#include <sys/ioctl.h>
#endif

#include <stdio.h>     /* for convenience */
#include <stdlib.h>    /* for convenience */
#include <stddef.h>    /* for offsetof */
#include <string.h>    /* for convenience */
#include <unistd.h>    /* for convenience */
#include <signal.h>    /* for SIG_ERR */
#include <errno.h>     /* for definition of errno */
#include <stdarg.h>    /* ISO C variable arguments */
#include <syslog.h>    /* for convenience */


#define MAXLINE 4096           /* max line length */

/*
 * Default file access permissions for new files.
 */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

/*
 * Default permissions for new directories.
 */
#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | SIXOTH)

typedef void Sigfunc(int);   /* for signal handlers */

#define min(a,b)  ((a) < (b) ? (a) : (b))
#define max(a,b)  ((a) > (b) ? (a) : (b))

/*
 * Prototypes for my own functions.
 */
char *path_alloc(size_t *);  
long open_max(void);

int set_cloexec(int);
void clr_fl(int, int);
void set_fl(int, int);

void pr_exit(int);

void pr_mask(const char *);
Sigfunc *signal_intr(int, Sigfunc *);

void daemonize(const char *);

void sleep_us(unsigned int);
ssize_t readn(int, void *, size_t);
ssize_t writen(int, const void *, size_t);

int fd_pipe(int *);
int recv_fd(int, ssize_t (*func) (int, const void *, size_t));
int send_fd(int, int);
int send_err(int, int, const char *);
int serv_listen(const char *);
int serv_accept(int, uid_t *);
int cli_conn(const char *);
int buf_args(char *, int (*func)(int, char **));
int tty_cbreak(int);
int tty_raw(int);
int tty_reset(int);
void tty_atexit(void);
struct termios *tty_termios(void);

int ptym_open(char *, int);
int ptys_open(char *);
#ifdef TIOCGWINSZ
pid_t pty_fork(int *, char *, int, const struct termios *, const struct winsize *);
#endif

int lock_reg(int, int, int, off_t, int, off_t);

#define read_lock(fd, offset, whence, len) \
    lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
    lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
    lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lcok(fd, offset, whence, len) \
    lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
    lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))

pid_t lock_test(int, int, off_t, int, off_t);

#define is_read_lockable(fd, offset, whence, len) \
    (lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len) \
    (lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)

void err_msg(const char *, ...); 
void err_dump(const char *, ...) __attribute__((noreturn));
void err_quit(const char *, ...) __attribute__((noreturn));
void err_cont(int, const char *, ...);
void err_exit(int, const char *, ...) __attribute__((noreturn));
void err_ret(const char *, ...);
void err_sys(const char *, ...) __attribute__((noreturn));

void log_msg(const char *, ...);
void log_open(const char *, int, int);
void log_quit(const char *, ...) __attribute__((noreturn));
void log_ret(const char *, ...);
void log_sys(const char *, ...) __attribute__((noreturn));
void log_exit(int, const char *, ...) __attribute__((noreturn));

void TELL_WAIT(void);
void TELL_PARENT(pid_t);
void TELL_CHILD(pid_t);
void WAIT_PARENT(void);
void WAIT_CHILD(void);

/*
 * ERROR Function Definitions
 */

static void err_doit(int, int, const char *, va_list);

/*
 * Nonfatal error rleated to a system call.
 * Print a message and return.
 */
void err_ret(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
}

/*
 * Fata error related to a system call.
 * Print a message and terminate.
 */
void err_sys(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * Nonfatal error unrealted to a system call.
 * Error code passed as explicit parameter.
 * Print a message and return.
 */
void err_cont(int error, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
}

/*
 * Fatal error unrelated to a system call.
 * Error code passed as explicit parameter.
 * Print a message and return.
 */
void err_exit(int error, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
    exit(1);
}   

/*
 * Fatal error related to a system call.
 * Print a message, dump core and terminate.
 */
void err_dump(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    abort();     /* dump core and terminate */
    exit(1);     /* shouldn't get here */
}

/*
 * Nonfatal error unrelated to a system call.
 * Print a message and return.
 */
void err_msg(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
}

/*
 * Fatale error unrelated to a system call.
 * Print a message and terminate.
 */
void err_quit(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
    char buf[MAXLINE];
    vsnprintf(buf, MAXLINE-1, fmt, ap);
    if (errnoflag)
        snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s", strerror(error));
    strcat(buf, "\n");
    fflush(stdout);
    fputs(buf, stderr);
    fflush(NULL);
}

#if 0
/*
 * ERROR Routines for programs that can run as a Daemon.
 * Commented out because of undefined reference of log_to_stderr.
 */

static void log_doit(int, int, int, const char *, va_list ap);

/*
 * Caller must define and set this: nonzero if 
 * interactive, zero if daemon
 */
extern int log_to_stderr;

/*
 * Initialize syslog(), if running as daemon.
 */
void log_open(const char *ident, int option, int facility) {
    if (log_to_stderr == 0)
        openlog(ident, option, facility);
}

/*
 * Nonfatal error rleated to a system call.
 * Print a message with the systems' errno value and return.
 */
void log_ret(const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        log_doit(1, errno, LOG_ERR, fmt, ap);
        va_end(ap);
}

/*
 * Fata error related to a system call.
 * Print a message and terminate.
 */
void log_sys(const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        log_doit(1, errno, LOG_ERR, fmt, ap);
        va_end(ap);
        exit(2);
}

/*
 * Fatal error unrelated to a system call.
 * Error code passed as explicit parameter.
 * Print a message and return.
 */
void log_exit(int error, const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        log_doit(1, error, LOG_ERR, fmt, ap);
        va_end(ap);
        exit(2);
}

/*
 * Nonfatal error unrelated to a system call.
 * Print a message and return.
 */
void log_msg(const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        log_doit(0, 0, LOG_ERR, fmt, ap);
        va_end(ap);
}

/*
 * Fatale error unrelated to a system call.
 * Print a message and terminate.
 */
void log_quit(const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        log_doit(0, 0, LOG_ERR, fmt, ap);
        va_end(ap);
    exit(2);
}

/*
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
static void log_doit(int errnoflag, int error, int priority, const char *fmt, va_list ap) {
        char buf[MAXLINE];
        vsnprintf(buf, MAXLINE-1, fmt, ap);
        if (errnoflag)
                snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s", strerror(error));
        strcat(buf, "\n");
    if (log_to_stderr) {
            fflush(stdout);
            fputs(buf, stderr);
        fflush(stderr);
    } else {
        syslog(priority, "%s", buf);
    }
}

#endif


#endif /* _CREEP_H */

【问题讨论】:

  • "creep.h"?...
  • 这是一个头文件,其中包含一些为我方便而预先编写的函数。就像示例代码中的err_sys。正如我们在 strace 中看到的系统调用,我们可以放心地假设链接和其他一切都很好。
  • 很好,但它对试图编译您的代码并自己尝试的人构成了障碍。
  • 点。我很少在这里提问,我忘记了最佳实践。添加文件。

标签: c systems-programming dup


【解决方案1】:

当您到达dup 时,您的第一个循环已经结束。

为什么结束了?因为你阅读了一切。 fd 在文件末尾。

然后你复制它。现在您有 2 个fd 引用同一个文件对象(PO​​SIX 术语中的“打开文件描述”)。它们在文件的末尾。

您需要倒带 (lseek(dup_fd, 0, SEEK_SET)) 才能再次读取文件。请注意,查找是对文件对象的操作,因此它会影响fds。您可以寻找fd 到位置0,然后从dup_fd 读取,它会再次读取内容。

您甚至可以在调用read(fd,...)read(dup_fd,...) 之间交替调用,如果您在阅读时打印每个块,您仍然可以按顺序获取文件内容。

【讨论】:

    【解决方案2】:

    这是预期的行为。 The POSIX documentation for dup() 状态:

    dup() 函数为服务提供了一个替代接口 由fcntl() 使用F_DUPFD 命令提供。通话 dup(fildes) 应相当于:

    fcntl(fildes, F_DUPFD, 0);
    

    The fcntl() documentation 状态:

    F_DUPFD

    返回一个新的文件描述符,该描述符应按照中所述分配 File Descriptor Allocation,但应该是最低的 编号大于或等于第三个的可用文件描述符 参数arg,作为int 类型的整数。 新文件 描述符应引用相同的打开文件描述作为 原始文件描述符,并应共享任何锁。 FD_CLOEXEC 与新文件描述符关联的标志应被清除以保持 该文件在调用其中一个 exec 函数时打开。

    注意粗体部分:“相同的打开文件描述”。

    这意味着dup()'d 文件描述符与其dup()'d 来自的文件描述符共享相同的当前文件偏移量。

    您已经通过读取文件中的所有数据将该偏移量设置到文件末尾。

    【讨论】:

    • @AbhinavJain 这是dup() 的“功能”之一——它咬了你之后你永远不会忘记的那种功能......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多