Problem 16. What’s the difference between soft link and hard link file?
Ans:
Hard link file is nothing but a regular file which pair its name with an inode which is pointed by other files(hard link file or other regular file) too. Accessing a hard link file only takes one time to access the file pointed by inode number.
Soft link file is a file whose data chunk contains nothing but an another file’s pathname. So when accessing the file through a soft link, you need to access the file system two times. One for reading the content of the soft link file and the other for reading the file pointed by the soft link.
Problem 17. what’s the differences among real uid, effective uid, a saved uid, and a filesystem uid?
Ans:
real uid: is always that of the user who started the process(开启进程).
effective uid: may change under various rules to allow a process to execute with
the rights of different users(运行进程).
saved uid : stores the original effective uid; its value is used in deciding what effective uid values the user may switch to(保存当前的有效用户ID).
filesystem uid: is usually equal to the effective uid, used to verify file access.
Problem 18. A common mistake in checking errno is to forget that any library or system call can modify it.
Ans:
For example, this code is buggy:
if (fsync (fd) == -1) {
fprintf (stderr, "fsync failed!/n");
if (errno == EIO)
fprintf (stderr, "I/O error on %d!/n", fd);
}
If you need to preserve the value of errno across function invocations, save it:
if (fsync (fd) == -1) {
int err = errno;
fprintf (stderr, "fsync failed: %s/n", strerror (errno));
if (err == EIO) {
/* if the error is I/O-related, jump ship */
fprintf (stderr, "I/O error on %d!/n", fd);
exit (EXIT_FAILURE);
}
}
PS: errno是一个全局变量,任何函数调用之后都有可能修改了它的值,所以连续调用两个函数时,必须保存每次函数调用后的errno值,以便能进行相应的错误检测。
In single-threaded programs, errno is a global variable. In multithreaded programs, however, errno is stored per-thread, and is thus thread-safe.
Problem 19. How to use the read() function safely?
Ans:
ssize_t ret;
while (len != 0 && (ret = read (fd, buf, len)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror ("read");
break;
}
len -= ret;
buf += ret;
}
PS:
Read函数调用后的产生的结果有以下几种:
1. The call returns a value equal to len. All len read bytes are stored in buf. The
results are as intended.
2. The call returns a value less than len, but greater than zero. This can occur because a signal interrupted the read midway, an error occurred in the middle of the read, more than zero, but less than len bytes’ worth of data was available, or EOF was reached before len bytes were read. Reissuing the read (with correspondingly updated buf and len values) will read the remaining bytes into the rest of the buffer, or indicate the cause of the problem.
3. The call returns 0. This indicates EOF. There is nothing to read.
4. The call returns 0. This indicates EOF. There is nothing to read.
5. The call returns -1, and errno is set to EINTR. This indicates that a signal was
received before any bytes were read. The call can be reissued.
6. The call returns -1, and errno is set to EAGAIN. This indicates that the read would block because no data is currently available, and that the request should be reissued later. This happens only in nonblocking mode.
7. The call returns -1, and errno is set to a value other than EINTR or EAGAIN. This
indicates a more serious error.
An Example for Write():
ssize_t ret, nr;
while (len != 0 && (ret = write (fd, buf, len)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror ("write");
break;
}
len -= ret;
buf += ret;
}
Problem 20. How to use select(), pselect(), poll, ppoll() and epoll()?
Ans:
1. select() example:
#include
#include
#include
int select (int n,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
#include
#include
#include
#include
#define TIMEOUT 5 /* select timeout in seconds */
#define BUF_LEN 1024 /* read buffer in bytes */
int main (void)
{
struct timeval tv;
fd_set readfds;
int ret;
/* Wait on stdin for input. */
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
/* Wait up to five seconds. */
tv.tv_sec = TIMEOUT;
tv.tv_usec = 0;
/* All right, now block! */
ret = select (STDIN_FILENO + 1,
&readfds,
NULL,
NULL,
&tv);
if (ret == -1) {
perror ("select");
return 1;
} else if (!ret) {
printf ("%d seconds elapsed./n", TIMEOUT);
return 0;
}
/*
* Is our file descriptor ready to read?
* (It must be, as it was the only fd that
* we provided and the call returned
* nonzero, but we will humor ourselves.)
*/
if (FD_ISSET(STDIN_FILENO, &readfds)) {
char buf[BUF_LEN+1];
int len;
/* guaranteed to not block */
len = read (STDIN_FILENO, buf, BUF_LEN);
if (len == -1) {
perror ("read");
return 1;
}
if (len) {
buf[len] = '/0';
printf ("read: %s/n", buf);
}
return 0;
}
fprintf (stderr, "This should not happen!/n");
return 1;
}
PS:Portable sleeping with select( )
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500;
/* sleep for 500 microseconds */
select (0, NULL, NULL, NULL, &tv);
There are three differences between pselect( ) and select( ):
1. pselect( ) uses the timespec structure, not the timeval structure, for its timeout
parameter. The timespec structure uses seconds and nanoseconds, not seconds
and microseconds, providing theoretically superior timeout resolution. In prac-
tice, however, neither call reliably provides even microsecond resolution.
2. A call to pselect( ) does not modify the timeout parameter. Consequently, this
parameter does not need to be reinitialized on subsequent invocations.
3. The select( ) system call does not have the sigmask parameter. With respect to
signals, when this parameter is set to NULL, pselect( ) behaves like select( ).
PS:
Until the 2.6.16 kernel, the Linux implementation of pselect( ) was not a system
call, but a simple wrapper around select( ), provided by glibc. This wrapper mini-
mized—but did not totally eliminate—the risk of this race condition occurring. With the introduction of a true system call, the race is gone.
2. poll() example
#include
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
#include
#include
#include
#define TIMEOUT 5 /* poll timeout, in seconds */
int main (void)
{
struct pollfd fds[2];
int ret;
/* watch stdin for input */
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
/* watch stdout for ability to write (almost always true) */
fds[1].fd = STDOUT_FILENO;
fds[1].events = POLLOUT;
/* All set, block! */
ret = poll (fds, 2, TIMEOUT * 1000);
if (ret == -1) {
perror ("poll");
return 1;
}
if (!ret) {
printf ("%d seconds elapsed./n", TIMEOUT);
return 0;
}
if (fds[0].revents & POLLIN)
printf ("stdin is readable/n");
if (fds[1].revents & POLLOUT)
printf ("stdout is writable/n");
return 0;
}
ppoll( )
Linux provides a ppoll( ) cousin to poll( ), in the same vein as pselect( ). Unlike
pselect( ), however, ppoll( ) is a Linux-specific interface:
#define _GNU_SOURCE
#include
int ppoll (struct pollfd *fds,
nfds_t nfds,
const struct timespec *timeout,
const sigset_t *sigmask);
As with pselect( ), the timeout parameter specifies a timeout value in seconds and
nanoseconds, and the sigmask parameter provides a set of signals for which to wait.
epoll()
An epoll context is created via epoll_create( ):
#include
int epoll_create (int size)
The epoll_ctl( ) system call can be used to add file descriptors to and remove file
descriptors from a given epoll context:
#include
int epoll_ctl (int epfd,
int op,
int fd,
struct epoll_event *event);
The header
struct epoll_event {
_ _u32 events; /* events */
union {
void *ptr;
int fd;
_ _u32 u32;
_ _u64 u64;
} data;
};
The system call epoll_wait( ) waits for events on the file descriptors associated with the given epoll instance:
#include
int epoll_wait (int epfd,
struct epoll_event *events,
int maxevents,
int timeout);
ps:如果timeout为0,函数立即返回,如果timeout为-1,仅在检测到事件发生时,函数才会返回。
example:
int epfd;
epfd = epoll_create (100); /* plan to watch ~100 fds */
if (epfd < 0)
perror ("epoll_create");
/*add a new watch on the file associated with fd to the epoll instance epfd,*/
struct epoll_event event;
int ret;
event.data.fd = fd; /* return the fd to us later */
event.events = EPOLLIN | EPOLLOUT;
ret = epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &event);
if (ret)
perror ("epoll_ctl");
/*modify an existing event on the file associated with fd on the epoll instance epfd*/
struct epoll_event event;
int ret;
event.data.fd = fd; /* return the fd to us later */
event.events = EPOLLIN;
ret = epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &event);
if (ret)
perror ("epoll_ctl");
/*remove an existing event on the file associated with fd from the epoll
instance epfd*/
struct epoll_event event;
int ret;
ret = epoll_ctl (epfd, EPOLL_CTL_DEL, fd, &event);
if (ret)
perror ("epoll_ctl");
PS:此时event参数可以为NULL,但在2.6.9之前,此函数要求不管在什么情况下,event都不能为空,所以为了保持兼容性,传递一个非空的event参数。
/*A full epoll_wait( ) example*/
#define MAX_EVENTS 64
struct epoll_event *events;
int nr_events, i, epfd;
events = malloc (sizeof (struct epoll_event) * MAX_EVENTS);
if (!events) {
perror ("malloc");
return 1;
}
nr_events = epoll_wait (epfd, events, MAX_EVENTS, -1);
if (nr_events < 0) {
perror ("epoll_wait");
free (events);
return 1;
}
for (i = 0; i < nr_events; i++) {
printf ("event=%ld on fd=%d/n",
events[i].events,
events[i].data.fd);
/*
* We now can, per events[i].events, operate on
* events[i].data.fd without blocking.
*/
}
free (events);
PS:
poll( ) Versus select( )
Although they perform the same basic job, the poll( ) system call is superior to
select( ) for a handful of reasons:
• poll( ) does not require that the user calculate and pass in as a parameter the
value of the highest-numbered file descriptor plus one.
• poll( ) is more efficient for large-valued file descriptors. Imagine watching a sin-
gle file descriptor with the value 900 via select( )—the kernel would have to
check each bit of each passed-in set, up to the 900th bit.
• select( )’s file descriptor sets are statically sized, introducing a tradeoff: they are
small, limiting the maximum file descriptor that select( ) can watch, or they are
inefficient. Operations on large bitmasks are not efficient, especially if it is not
known whether they are sparsely populated.* With poll( ), one can create an
array of exactly the right size. Only watching one item? Just pass in a single
structure.
• With select( ), the file descriptor sets are reconstructed on return, so each sub-
sequent call must reinitialize them. The poll( ) system call separates the input
(events field) from the output (revents field), allowing the array to be reused
without change.
• The timeout parameter to select( ) is undefined on return. Portable code needs
to reinitialize it. This is not an issue with pselect( ), however.
The select( ) system call does have a few things going for it, though:
• select( ) is more portable, as some Unix systems do not support poll( ).
• select( ) provides better timeout resolution: down to the microsecond. Both
ppoll( ) and pselect( ) theoretically provide nanosecond resolution, but in prac-
tice, none of these calls reliably provides even microsecond resolution.
Superior to both poll( ) and select( ) is the epoll interface, a Linux-specific multi-
plexing I/O solution
PS:how to safely use rewind() function?
errno = 0;
rewind (stream);
if (errno)
/* error */
由于rewind()函数没有返回值,所以不能通过返回值来进行错误处理,必须通过全局变量errno,所以调用之前必须置errno为0.
为了确保数据流中的数据在文件关闭之前确实写入到了磁盘中,必须调用以下函数:
fflush();//确保用户程序缓冲区的数据写入内核中
fsync();//确保内核缓冲区的数据写入到磁盘中。