重复系统调用的奇怪行为

时间:2018-03-29 16:08:02

标签: c systems-programming dup

所以,我正在阅读关于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

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

我为上述事情做了一个strace,希望能找到正在发生的事情,但我能看到的就是这个 - &gt;

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函数给出了编号最小的可用文件描述符,它与原始文件描述符重复。在Process Table Entry中的文件指针指向文件描述符的同一文件表的意义上重复。

为什么我无法使用重复的文件描述符读取文件。有什么我做错了吗?

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 */

2 个答案:

答案 0 :(得分:2)

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

为什么结束?因为你读了一切。 fd位于文件的末尾。

然后你复制它。现在您有2 fd个引用相同的文件对象(&#34;打开文件描述&#34;在POSIX术语中)。他们在文件末尾

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

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

答案 1 :(得分: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函数时打开。

请注意粗体部分:&#34;相同的打开文件说明&#34;。

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

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