Итак, я читал о файловом вводе-выводе в Linux и думал поиграть с этим. Однако я обнаружил в коде два странных поведения, и я изо всех сил пытаюсь выяснить их причину.
/*
* 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 для вышеупомянутого в надежде узнать, что происходит, но все, что я мог видеть, было это ->
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 */
Это заголовочный файл, который для моего удобства содержит некоторые из заранее написанных функций. Как err_sys
в примере кода. Поскольку мы можем видеть системные вызовы в strace, мы можем с уверенностью предположить, что связывание и все остальное в порядке.
Хорошо, но это создает препятствие для людей, пытающихся скомпилировать ваш код и попробовать его на себе.
Точка. Здесь я редко задаю вопросы, я забыл о лучших практиках. Добавил файл.
К тому времени, как вы дойдете до dup
, ваш первый цикл закончится.
Почему это закончилось? Потому что вы все читаете. fd
находится в конце файла.
Затем вы дублируете это. Теперь у вас есть 2 fd
, ссылающихся на один и тот же файловый объект («описание открытого файла» в терминологии POSIX). Это оба в конце файла.
Вам нужно перемотать (lseek(dup_fd, 0, SEEK_SET)
), чтобы прочитать файл еще раз. И имейте в виду, что поиск - это операция над файловым объектом, поэтому он влияет на оба fd
. Вы можете найти fd
в позиции 0, а затем прочитать из dup_fd
, и он снова прочитает содержимое.
Вы даже можете чередовать вызовы read(fd,...)
и read(dup_fd,...)
, и вы все равно получите упорядоченное содержимое файла, если просто распечатываете каждый фрагмент по мере его чтения.
Это ожидаемое поведение. Документация POSIX для dup()
заявляет:
The
dup()
function provides an alternative interface to the service provided byfcntl()
using theF_DUPFD
command. The calldup(fildes)
shall be equivalent to:fcntl(fildes, F_DUPFD, 0);
Документация fcntl()
утверждает:
F_DUPFD
Return a new file descriptor which shall be allocated as described in File Descriptor Allocation, except that it shall be the lowest numbered available file descriptor greater than or equal to the third argument,
arg
, taken as an integer of typeint
. The new file descriptor shall refer to the same open file description as the original file descriptor, and shall share any locks. TheFD_CLOEXEC
flag associated with the new file descriptor shall be cleared to keep the file open across calls to one of the exec functions.
Обратите внимание на выделенную жирным шрифтом часть: «то же описание открытого файла».
Это означает, что файловый дескриптор dup()
имеет то же текущее смещение файла, что и тот, из которого он dup()
.
И вы уже установили это смещение до конца файла, прочитав все данные в нем.
@AbhinavJain Это одна из "особенностей" dup()
- особенность, которую вы никогда не забудете, когда она вас укусит ...
"creep.h"
? ...