问题集锦(16-20)

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;

}

 

PSPortable 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 defines the epoll_event structure as:

    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:如果timeout0,函数立即返回,如果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

 

PShow to safely use rewind() function?

 

errno = 0;

rewind (stream);

if (errno)

        /* error */

由于rewind()函数没有返回值,所以不能通过返回值来进行错误处理,必须通过全局变量errno,所以调用之前必须置errno0.

 

为了确保数据流中的数据在文件关闭之前确实写入到了磁盘中,必须调用以下函数:

fflush();//确保用户程序缓冲区的数据写入内核中

fsync();//确保内核缓冲区的数据写入到磁盘中。

你可能感兴趣的:(问题总结)