2008年12月3日星期三

APUE(1.1)

1.3 登录
    当我们登录的时候,我们输入用户名和密码,系统这时候会查找/etc/passwd这个文件,比对用户名,然后会接着查找/etc/shadow中的密码。
/etc/passwd文件由冒号分隔的7个部分组成,下面是一个例子:
sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh
其含义为:
用户名:加密后的密码:数字用户ID:数字组ID:注释字段:主目录:所用SHELL类型
    由于/etc/passwd权限为-rw-r--r--,也就是说所有人均为可读,故密码不能放在这里,都放在了/etc/shadow中了,shadow中的每一项有9个字段,同样由冒号分开,下面是shadow中的一例:
beinan:$1$VE.Mq2Xf$2c9Qi7EQ9JP8GKF8gH7PB1:13072:0:99999:7:::
其含义为:
用户名:密码:上次修改口令时间:两次修改口令的最小间隔天数:两次修改最多的间隔天数:提前警告用户密码要过期的天数:口令过期后多少天禁用此用户:用户过期日期:保留扩展位
详细的解释可以看这里:用户(User)和用户组(Group)配置文件详解

1.4 文件和目录
程序1.3. 列出一个目录中左右的文件(这里我将程序改写了,没有用apue.h,可以熟悉头文件)
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    DIR             *dp;
    struct dirent    *dir;
    if (argc != 2) {
        fprintf(stderr, "usage: myls directory_name\n");
        return 1;
    }
    if ((dp = opendir(argv[1])) == NULL)
        fprintf(stderr, "can't open %s\n", argv[1]);
    while ((dir = readdir(dp)) != NULL)
        printf("%s\n", dir->d_name);
    closedir(dp);
    exit(0);
}
注意顺序:opendir()-->readdir()-->closedir()

相关理解:
1.这里在while循环中使用readdir()函数,没有类似累加的动作,因此应该是readdir()函数自己保存一个指针,下一次调用的时候会累加一个偏移量,直到NULL.
2.关于struct dirent的定义在/usr/include/bits/dirent.h中:
struct dirent
  {
#ifndef __USE_FILE_OFFSET64
    __ino_t d_ino;
    __off_t d_off;
#else
    __ino64_t d_ino;
    __off64_t d_off;
#endif
    unsigned short int d_reclen;
    unsigned char d_type;
    char d_name[256];        /* We must not include limits.h! */
  };
#ifdef __USE_LARGEFILE64
struct dirent64
  {
    __ino64_t d_ino;
    __off64_t d_off;
    unsigned short int d_reclen;
    unsigned char d_type;
    char d_name[256];        /* We must not include limits.h! */
  };
#endif
其中d_type可能的取值为:
                   DT_UNKNOWN,未知的类型
                   DT_REG,普通文件
                   DT_DIR,普通目录
                   DT_FIFO,命名管道或FIFO
                   DT_SOCK,本地套接口
                   DT_CHR,字符设备文件
                   DT_BLK,块设备文件
知道了具体的定义就可以随便得到自己想要的信息了。
3.关于readdir的具体实现可以参考glibc-2.7/sysdeps/unix/readdir.c文件:
/* Read a directory entry from DIRP.  */
DIRENT_TYPE *
__READDIR (DIR *dirp)
{
  DIRENT_TYPE *dp;
  int saved_errno = errno;
#ifndef NOT_IN_libc
  __libc_lock_lock (dirp->lock);
#endif
  do
    {
      size_t reclen;
      if (dirp->offset >= dirp->size)
    {
      /* We've emptied out our buffer.  Refill it.  */
      size_t maxread;
      ssize_t bytes;
#ifndef _DIRENT_HAVE_D_RECLEN
      /* Fixed-size struct; must read one at a time (see below).  */
      maxread = sizeof *dp;
#else
      maxread = dirp->allocation;
#endif
      bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
      if (bytes <= 0)
        {
          /* On some systems getdents fails with ENOENT when the
         open directory has been rmdir'd already.  POSIX.1
         requires that we treat this condition like normal EOF.  */
          if (bytes < 0 && errno == ENOENT)
        bytes = 0;
          /* Don't modifiy errno when reaching EOF.  */
          if (bytes == 0)
        __set_errno (saved_errno);
          dp = NULL;
          break;
        }
      dirp->size = (size_t) bytes;
      /* Reset the offset into the buffer.  */
      dirp->offset = 0;
    }
      dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
#ifdef _DIRENT_HAVE_D_RECLEN
      reclen = dp->d_reclen;
#else
      /* The only version of `struct dirent*' that lacks `d_reclen'
     is fixed-size.  */
      assert (sizeof dp->d_name > 1);
      reclen = sizeof *dp;
      /* The name is not terminated if it is the largest possible size.
     Clobber the following byte to ensure proper null termination.  We
     read jst one entry at a time above so we know that byte will not
     be used later.  */
      dp->d_name[sizeof dp->d_name] = '\0';
#endif
      dirp->offset += reclen;
#ifdef _DIRENT_HAVE_D_OFF
      dirp->filepos = dp->d_off;
#else
      dirp->filepos += reclen;
#endif
      /* Skip deleted files.  */
    } while (dp->d_ino == 0);
#ifndef NOT_IN_libc
  __libc_lock_unlock (dirp->lock);
#endif
  return dp;
}
可以看到每次在do-while循环中先给dp赋值,然后每次dp都要步进一次,故能得到这个dirent中保存的信息。

关于opendir(),每次会有一个alloc的动作来分给一段内存来放置当前dir的内容,以便进行操作,源文件glibc-2.7/sysdeps/unix/opendir.c.
关于closedir(),每次会一个free的动作,释放opendir()申请的内存,故要成对使用,源文件glibc-2.7/sysdeps/unix/closedir.c.

总结:
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
int readdir(unsigned int fd, struct dirent *dirp, unsigned int count);
int closedir(DIR *dir);
opendir()-->readdir()-->closedir()

没有评论: