zoukankan      html  css  js  c++  java
  • 嵌入式成长轨迹17 【Linux应用编程强化】【Linux下的C编程 下】【文件系统编程】

    一 Linux文件系统简介
    文件系统主要体现在对文件和目录的组织上。Linux系统使用标准的树型目录结构,在系统安装时,安装程序就已经为用户创建了文件系统和完整而固定的目录结构,并指定了每个目录的作用和其中的文件类型。目录的最上层为根目录,其他所有目录都是从根目录出发而生成的子目录,根目录下的子目录可以任意嵌套。

    1、 Linux系统调用
    所谓系统调用是指操作系统提供给用户程序的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的特殊服务。

    在linux中用户程序不能直接访问内核提供的服务。为了更好的保护内核空间,将程序的运行空间分为内核空间和用户空间,他们运行在不同的级别上,在逻辑上是相互隔离的。

    2 文件I/O介绍
    可用的文件I / O函数——打开文件、读文件、写文件等等。大多数linux文件I / O只需用到5个函数:open、read、write、lseek 以及close。

     不带缓存指的是每个r e a d和w r i t e都调用内核中的一个系统调用。这些不带缓存的I / O函数不是ANSI C的组成部分,但是P O S I X 组成部分。

    3 文件描述符
     对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用o p e n或c r e a t返回的文件描述符标识该文件,将其作为参数传送给r e a d或w r i t e。

    二 文件的基本操作
    1 创建/打开文件
    创建/打开一个文件可以使用open函数,它有两种形式。
    #include <fcntl.h>

    #include <sys/stat.h>

    #include <sys/types.h>
    int open(const char *filename, int flags);
    int open(const char *filename, int flags, mode_t mode);

    flags取值
    O_RDONLY、O_WRONLY、O_RDWR、O_APPEND、O_CREAT、O_EXEC/O_EXCL、O_NOBLOCK、O_TRUNC

    S_IRUSR
      O_RDONLY 只读打开。
      O_WRONLY 只写打开。
      O_RDWR 读、写打开。
      O_APPEND 每次写时都加到文件的尾端。
      O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
      O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
      O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
      O_NOCTTY 如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。
      O_NONBLOCK 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。
      O_SYNC 使每次w r i t e都等到物理I / O操作完成。

    对于open函数而言,仅当创建新文件时才使用第三个参数。
    mod取值
    S_IWUSR、S_IXUSR、S_IRWXU
    S_IRGRP、S_IWGRP、S_IXGRP、S_IRWXG
    S_IROTH、S_IWOTH、S_IXOTH、S_IRWXO
    S_ISUID、S_ISGID

     1 /* example1.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <fcntl.h>
    5 #include <sys/stat.h>
    6 #include <sys/types.h>
    7 int main()
    8 {
    9 int fd; /* 定义文件描述符 */
    10 /* 打开文件,如果文件不存在,则创建一个新文件,并用后面的选项设置其访问权限 */
    11 fd = open("test", O_CREAT, S_IRWXU | S_IRGRP | S_IXGRP );
    12 printf("fd = %d\n", fd1);
    13 return 0;
    14 }
     1 /* example2.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <error.h>
    5 #include <fcntl.h>
    6 #include <sys/stat.h>
    7 #include <sys/types.h>
    8 int main()
    9 {
    10 int fd; /* 定义文件描述符 */
    11 fd = open("test", O_CREAT | O_EXCL, 00750);
    12 if (fd == -1)
    13 {
    14 perror("open error"); /* 如果文件打开失败,调用perror函数输出错误信息 */
    15 exit(1);
    16 }
    17 //
    18 return 0;
    19 }

    2 关闭文件
    int close(int fd);

    3 读写文件
    文件打开之后,就可以对文件进行读写操作了。

    1).read函数

       ssize_t read(int fd, void *buffer, size_t count);
    有多种情况可使实际读到的字节数少于要求读字节数:
     读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有3 0个字节,而要求读1 0 0个字节,则r e a d返回3 0,下一次再调用r e a d时,它将返回0 (文件尾端)。
     当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
     当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
     某些面向记录的设备,例如磁带,一次最多返回一个记录。
        
    读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。


    2).write函数

       ssize_t write(int fd, const void *buffer,
    size_t count);
    返回:若成功为已写的字节数,若出错为- 1
    其返回值通常与参数count的值不同,否则表示出错。w r i t e出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。

     对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O _ A P P E N D选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。

    3).lseek函数
       off_t lseek(int fd, off_t offset, int whence);

    whence取值
      SEEK_SET、SEEK_CUR、SEEK_END

     
      lseek(int fd, 0, SEEK_SET);
      lseek(int fd, 0, SEEK_END);
      lseek(int fd, 0, SEEK_CUR);

    复制一个文件1到另一个文件2,若文件2不存在则将其创建

     1 /* example3.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 #include <error.h>
    6 #include <fcntl.h>
    7 #include <sys/stat.h>
    8 #include <sys/types.h>
    9 #define BUFFER_SIZE 256 /* 缓冲区大小,这里设置为256,主要是为了使程序进行多次循环 */
    10 int main(int argc, char **argv)
    11 {
    12 int fd1, fd2; /* 定义文件描述符 */
    13 int cntr, cntw;
    14 char buffer[BUFFER_SIZE]; /* 定义缓冲区 */
    15 char *ptr;
    16 if(argc != 3) /* 检查命令行参数个数 */
    17 {
    18 printf("too few arguments.\n");
    19 exit(1);
    20 }
    21 fd1 = open(argv[1], O_RDONLY); /* 打开源文件 */
    22 if (fd1 == -1) /* 如果源文件打开失败,输出错误信息并退出 */
    23 {
    24 perror("open file1 failed ");
    25 exit(1);
    26 }
    27 fd2 = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); /* 创建/打开目标文件 */
    28 if (fd2 == -1) /* 如果目标文件创建/打开失败,输出错误信息并退出 */
    29 {
    30 perror("open file2 failed ");
    31 exit(1);
    32 }
    33 while((cntr = read(fd1, buffer, BUFFER_SIZE)) != 0) /* 进行文件拷贝,首先从源文件中读取数据 */
    34 {
    35 if(cntr == -1) /* 读取过程中有错误发生 */
    36 {
    37 perror("read error");
    38 break;
    39 }
    40 else if(cntr > 0) /* 读取成功,数据的长度为cntr */
    41 {
    42 ptr = buffer;
    43 while((cntw = write(fd2, ptr, cntr)) != 0) /* 将读取的数据写入到目标文件之中 */
    44 {
    45 if(cntw == -1) /* 写入过程中有错误发生,跳出内层循环*/
    46 {
    47 perror("write error");
    48 break;
    49 }
    50 else if(cntw == cntr) /* 写入字节数与读取字节数相同,数据全部写入 */
    51 break;
    52 else if(cntw > 0) /* 写入字节数小于读取字节数,只有部分数据写入 */
    53 {
    54 ptr += cntw; /* 指向未写入的数据,接下来再次执行写入操作 */
    55 cntr -= cntw;
    56 }
    57 }
    58 if(cntw == -1) /* 写入过程中有错误发生,跳出外层循环 */
    59 break;
    60 }
    61 }
    62 close(fd1); /* 关闭源文件 */
    63 close(fd2); /* 关闭目标文件 */
    64 printf(“Done!\n”);
    65 return 0;
    66 }
     1 /* example4.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 #include <string.h>
    6 #include <error.h>
    7 #include <fcntl.h>
    8 #include <sys/stat.h>
    9 #include <sys/types.h>
    10 #define NAME_LEN 16
    11 #define STUDENT_NUM 32
    12 struct student
    13 {
    14 unsigned int id; /* 学号 */
    15 char name[NAME_LEN]; /* 姓名 */
    16 };
    17 int main(int argc, char **argv)
    18 {
    19 int fd; /* 定义文件描述符 */
    20 struct student std;
    21 int id;
    22 char name[NAME_LEN];
    23 if(argc != 2) /* 检查命令行参数个数 */
    24 {
    25 printf("too few arguments.\n");
    26 exit(1);
    27 }
    28 fd = open(argv[1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); /* 创建文件 */
    29 if (fd == -1) /* 如果文件创建失败,输出错误信息并退出 */
    30 {
    31 perror("open failed ");
    32 exit(1);
    33 }
    34 printf("Please input the student information.\n");
    35 for(;;) /* 循环写入学生信息 */
    36 {
    37 printf("ID : ");
    38 scanf("%d", &id); /* 从键盘输入学生的学号 */
    39 if(id < 0) /* 如果输入的学号为负值,退出程序 */
    40 {
    41 printf("Complete!\n");
    42 exit(0);
    43 }
    44 else
    45 {
    46 printf("Name : ");
    47 scanf("%16s", name); /* 从键盘输入学生的姓名 */
    48 std.id = id;
    49 bzero(std.name, NAME_LEN); /* 将std结构中的name清零 */
    50 strcpy(std.name, name); /* 将输入的姓名复制到std结构中 */
    51 lseek(fd, id*sizeof(std), SEEK_SET); /* 根据学生的学号设置文件的写入位置 */
    52 write(fd, (char *)&std, sizeof(std)); /* 将学号,姓名写入到文件中 */
    53 }
    54 }
    55 close(fd); /* 关闭文件 */
    56 return 0;
    57 }

    4 文件的其他操作
    1).dup函数
       int dup(int oldfd);

    2).symlink函数 创建符号链接

       int symlink(const char * oldpath,const char *newpath);3).link函数 创建硬链接

       int link(const char * oldpath,const char *newpath);

    4).unlink函数 删除链接   int unlink(const char *pathname);

     1 /* example5.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 #include <error.h>
    6 int main(int argc, char **argv)
    7 {
    8 int ret;
    9 if(argc != 3) /* 检查命令行参数个数 */
    10 {
    11 printf("too few arguments\n");
    12 exit(1);
    13 }
    14 ret = symlink(argv[1], argv[2]); /* 创建文件的硬连接 */
    15 if(ret == -1) /* 如果硬连接创建失败,输出错误信息并退出 */
    16 {
    17 perror("symlink failed");
    18 exit(1);
    19 }
    20 printf("Done!\n");
    21 return 0;
    22 }



    三  文件的属性
    文件有很多属性,除了上面介绍的访问权限外,还有文件的大小、创建时间、访问时间等。Linux系统下提供了一些函数,可以获取和设置文件的属性

    1 获取文件的属性
    1).stat函数
       int stat(const char *file_name, struct stat *buf);
    struct stat {
     
       dev_t st_dev;
       ino_t st_ino;
       mode_t st_mode;
       nlink_t st_nlink;
       uid_t st_uid;
       gid_t st_gid;
       dev_t st_rdev;
       off_t st_size;
       unsigned long st_blksize;

       unsigned long st_blocks;   time_t st_atime;
       time_t st_mtime;
       time_t st_ctime;
    };

    S_IFSOCK

    S_IFLNKS_IFREG

    S_IFBLK

    S_IFDIR
    S_IFCHR
    S_IFIFO

    S_ISSOCK(st_mode)

    S_ISLNK(st_mode)

    S_ISREG(st_mode)

    S_ISBLK(st_mode)

    S_ISDIR(st_mode)

    S_ISCHR(st_mode)

    S_ISBLK(st_mode)

    获取文件属性并输出类型

     1 /* example6.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 #include <sys/stat.h>
    6 int main(int argc, char **argv)
    7 {
    8 struct stat st;
    9 if(argc != 2) /* 检查命令行参数个数 */
    10 {
    11 printf("too few arguments.\n");
    12 exit(1);
    13 }
    14 stat(argv[1], &st); /* 获取文件的属性 */
    15 printf("File Size: %ld Byte\n", st.st_size); /* 输出文件的大小 */
    16 printf("File Type : ");
    17 switch(st.st_mode & S_IFMT) /* 判断文件的类型 */
    18 {
    19 case S_IFSOCK: /* 套接字 */
    20 printf("Socket\n");
    21 break;
    22 case S_IFLNK: /* 符号连接 */
    23 printf("Symbolic link\n");
    24 break;
    25 case S_IFREG: /* 普通文件 */
    26 printf("Regular file\n");
    27 break;
    28 case S_IFBLK: /* 块设备文件 */
    29 printf("Block device\n");
    30 break;
    31 case S_IFDIR: /* 目录文件 */
    32 printf("Directory\n");
    33 break;
    34 case S_IFCHR: /* 字符设备文件 */
    35 printf("Character device\n");
    36 break;
    37 case S_IFIFO: /* 管道文件 */
    38 printf("FIFO\n");
    39 break;
    40 default:
    41 ;
    42 }
    43 return 0;
    44 }

    2).fstat函数

       int fstat(int fd, struct stat *buf);


    3).access函数 用来更改正在使用的文件属性

       int access(const char *pathname, int mode);   R_OK、 W_OK、 X_OK、 F_OK

     1 /* example7.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 int main(int argc, char **argv)
    6 {
    7 if(argc != 2) /* 检查命令行参数个数 */
    8 {
    9 printf("too few arguments.\n");
    10 exit(1);
    11 }
    12 if(access(argv[1], F_OK) != -1) /* 判断文件是否存在 */
    13 {
    14 if(access(argv[1], R_OK) != -1) /* 判断文件是否可读 */
    15 printf("read\n");
    16 if(access(argv[1], W_OK) != -1) /* 判断文件是否可写 */
    17 printf("write\n");
    18 if(access(argv[1], X_OK) != -1) /* 判断文件是否可执行 */
    19 printf("execute\n");
    20 }
    21 else
    22 {
    23 printf("%s does not exist\n", argv[1]); /* 文件不存在 */
    24 }
    25 return 0;
    26 }

    2 设置文件的属性
    设置文件属性可以使用的函数主要有:chmod、chown、utime以及fcntl等。
    1).chmod函数

      int chmod(const char *path, mode_t mode);

     1 /* example8.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <error.h>
    5 #include <sys/types.h>
    6 #include <sys/stat.h>
    7 int main(int argc, char **argv)
    8 {
    9 int ret;
    10 if(argc != 2) /* 检查命令行参数个数 */
    11 {
    12 printf("too few arguments\n");
    13 exit(1);
    14 }
    15 ret = chmod(argv[1], S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 修改文件的访问权限 */
    16 if(ret == -1) /* 如果文件权限修改失败,输出错误信息并退出 */
    17 {
    18 perror("chmod failed");
    19 exit(1);
    20 }
    21 printf("Done!\n");
    22 return 0;
    23 }

    2).chown函数

      int chown(const char *path, uid_t owner,gid_t group);
    3).utime函数

      int utime(const char *filename, struct utimbuf *buf);

    4).fcntl函数

      int fcntl(int fd , int cmd);
      int fcntl(int fd, int cmd, long arg);

      int fcntl(int fd, int cmd, struct flock *lock);


      F_DUPFD、F_SETFD、F_SETFL

    #include <sys/types.h>
    #include <unistd.h>
    #include <fcnt1.h>
    int fcnt1(int filedes, int cmd,... struct flock flockptr ) ;

     所希望的锁类型:F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或F_UNLCK(解锁一个区域)
     要加锁或解锁的区域的起始地址,由l_start和l_whence两者决定。l_stat是相对位移量(字节),l_whence则决定了相对位移量的起点。
     区域的长度,由l_len表示。  

    关于加锁和解锁区域的说明还要注意下列各点:
     该区域可以在当前文件尾端处开始或越过其尾端处开始,但是不能在文件起始位置之前开始或越过该起始位置。
     如若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能位置为止。也就是不管添写到该文件中多少数据,它都处于锁的范围。
     为了锁整个文件,通常的方法是将l_start说明为0,l_whence说明为SEEK_SET,l_len说明为0。

     1 //fcntl.c
    2 #include <sys/types.h>
    3 #include <sys/socket.h>
    4 #include <sys/wait.h>
    5 #include <stdio.h>
    6 #include <stdlib.h>
    7 #include <errno.h>
    8 #include <string.h>
    9 #include <sys/un.h>
    10 #include <sys/time.h>
    11 #include <sys/ioctl.h>
    12 #include <unistd.h>
    13 #include <netinet/in.h>
    14 #include <fcntl.h>
    15 #include <unistd.h>
    16
    17 #define SERVPORT 3333
    18 #define BACKLOG 10
    19 #define MAX_CONNECTED_NO 10
    20 #define MAXDATASIZE 100
    21
    22 int main()
    23 {
    24 struct sockaddr_in server_sockaddr,client_sockaddr;
    25 int sin_size,recvbytes,flags;
    26 int sockfd,client_fd;
    27 char buf[MAXDATASIZE];
    28 if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
    29 perror("socket");
    30 exit(1);
    31 }
    32 printf("socket success!,sockfd=%d\n",sockfd);
    33 server_sockaddr.sin_family=AF_INET;
    34 server_sockaddr.sin_port=htons(SERVPORT);
    35 server_sockaddr.sin_addr.s_addr=INADDR_ANY;
    36 bzero(&(server_sockaddr.sin_zero),8);
    37 if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){
    38 perror("bind");
    39 exit(1);
    40 }
    41 printf("bind success!\n");
    42 if(listen(sockfd,BACKLOG)==-1){
    43 perror("listen");
    44 exit(1);
    45 }
    46 printf("listening....\n");
    47 if((flags=fcntl( sockfd, F_SETFL, 0))<0)
    48 perror("fcntl F_SETFL");
    49 flags |= O_NONBLOCK;
    50 if(fcntl( sockfd, F_SETFL,flags)<0)
    51 perror("fcntl");
    52 while(1){
    53 sin_size=sizeof(struct sockaddr_in);
    54 if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))==-1){
    55 perror("accept");
    56 exit(1);
    57 }
    58 if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){
    59 perror("recv");
    60 exit(1);
    61 }
    62 if(read(client_fd,buf,MAXDATASIZE)<0){
    63 perror("read");
    64 exit(1);
    65 }
    66 printf("received a connection :%s",buf);
    67 close(client_fd);
    68 exit(1);
    69 }/*while*/
    70 }
    71
    72

    四 目录文件的操作
    1.创建目录文件
      int mkdir(canst char *pathname, mode_t mode);

     1 /* example9.c */
    2 #include <stdlib.h>
    3 #include <stdio.h>
    4 #include <unistd.h>
    5 #include <error.h>
    6 #include <fcntl.h>
    7 #include <sys/stat.h>
    8 #include <sys/types.h>
    9 int main(int argc, char **argv)
    10 {
    11 int ret;
    12 if(argc != 2) /* 检查命令行参数个数 */
    13 {
    14 printf("too few arguments\n");
    15 exit(1);
    16 }
    17 ret = mkdir(argv[1], S_IRUSR | S_IXUSR); /* 创建目录文件 */
    18 if(ret == -1) /* 如果目录文件创建失败,输出错误信息并退出 */
    19 {
    20 perror("mkdir failed");
    21 exit(1);
    22 }
    23 printf("Done!\n");
    24 return 0;
    25 }

    2. 打开目录文件
     
      DIR *opendir(const char *name);

    3. 关闭目录文件
     
      int closedir(DIR *dir);

     1 /* example10.c */
    2 #include<stdlib.h>
    3 #include<stdio.h>
    4 #include<unistd.h>
    5 #include<string.h>
    6 #include<sys/types.h>
    7 #include<dirent.h>
    8 int main(int argc, char **argv)
    9 {
    10 DIR *dp;
    11 struct dirent *dirp;
    12 if(argc != 2) /* 检查命令行参数个数 */
    13 {
    14 printf("too few arguments\n");
    15 exit(1);
    16 }
    17 dp = opendir(argv[1]); /* 打开目录文件 */
    18 if(dp == NULL) /* 如果目录文件打开失败,输出错误信息并退出 */
    19 {
    20 perror("opendir error");
    21 exit(1);
    22 }
    23 while((dirp = readdir(dp)) != NULL) /* 读取目录文件 */
    24 {
    25 if((strcmp(dirp->d_name, ".") == 0) || (strcmp(dirp->d_name, "..") == 0)) /* 跳过当前目录和父文件 */
    26 continue;
    27 if(dirp->d_type == DT_DIR) /* 子目录 */
    28 {
    29 printf("%s <directory>\n", dirp->d_name);
    30 }
    31 else /* 普通文件 */
    32 printf("%s\n", dirp->d_name);
    33 }
    34 closedir(dp); /* 关闭目录文件 */
    35 return 0;
    36 }



    4. 读取目录文件
     
      struct dirent *readdir(DIR * dir);
      
    struct dirent {

        ino_t d_ino;
        ff_t d_off;
         signed short int d_reclen;
      
      unsigned char d_type;
        char d_name[256];
    };


    5. 获取当前工作目录
     
      char *getcwd(char *buf, size_t size);
    6. 更改当前工作目录
      int chdir(const char *path);

     1 /* example11.c */
    2 #include<stdlib.h>
    3 #include<stdio.h>
    4 #include<unistd.h>
    5 #include<error.h>
    6 #define BUFFER_SIZE 64
    7 int main()
    8 {
    9 int ret;
    10 char buf[BUFFER_SIZE];
    11 getcwd(buf, sizeof(buf)); /* 获取当前工作目录 */
    12 printf("%s\n", buf);
    13 ret = chdir(".."); /* 更改工作目录 */
    14 if(ret == -1) /* 如果目录更改失败,输出错误信息并退出 */
    15 {
    16 perror("chdir error");
    17 exit(1);
    18 }
    19 getcwd(buf, sizeof(buf)); /* 获取更改后的工作目录 */
    20 printf("%s\n", buf);
    21 return 0;
    22 }


    五 select 实现I/O复用
    1、I/O处理的五种模型
    1) 阻塞I/O模型:若所调用的I/O函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回。如:终端、网络设备的访问。
    2) 非阻塞模型:当请求的I/O操作不能完成时,则不让进程休眠,而且返回一个错误。如:open、read、write访问。
    3)I/O多路转接模型:如果请求的I/O 操作阻塞,且他不是真正阻塞I/O,而且让其中的一个函数等待,在这期间, I/O还能进行其他操作。如:select函数。
    4) 信号驱动I/O模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。
    5) 异步I/O模型:在这种模型下,当一个描述符已准备好,可以启动I/O时,进程会通知内核。由内核进行后续处理,这种用法现在较少。

    2、select函数
    传向select的参数告诉内核:
    (1) 我们所关心的描述符。
    (2) 对于每个描述符我们所关心的条件(是否读一个给定的描述符?是否想写一个给定的描述符?是否关心一个描述符的异常条件?)。
    (3) 希望等待多长时间(可以永远等待,等待一个固定量时间,或完全不等待)。
          从s e l e c t返回时,内核告诉我们:
    (1) 已准备好的描述符的数量。
    (2) 哪一个描述符已准备好读、写或异常条件。

    #include <sys/types.h>/* fd_set data type */
    #include <sys/time.h> /* struct timeval */
    #include <unistd.h> /* function prototype might be here */
    int select (int numfds, fd_set *readfds,fd_set *writefds, fd_set *exceptfds,struct timeval * timeout) ;
    返回:准备就绪的描述符数,若超时则为0,若出错则为- 1。

    timeout值:
     NULL:永远等待,直到捕捉到信号或文件描述符已准备好为止;
     具体值: struct timeval 类型的指针,若等待为timeout时间还没有文件描述符准备好,就立即返回;
     0:从不等待,测试所有指定 的描述符并立即返回;

         先说明最后一个参数,它指定愿意等待的时间:

    struct timeval
    {
        long tv_sec; /* seconds */
        long tv_usec; /* and microseconds */
    };

          select函数根据希望进行的文件操作对文件描述符进行分类处理,这里,对文件描述符的处理主要设计4个宏函数:

    FD_ZERO(fd_set *set) 清除一个文件描述符集;
    FD_SET(int fd, fd_set *set) 将一个文件描述符加入文件描述符集中;
    FD_CLR(int fd, fd_set *set) 将一个文件描述符从文件描述符集中清除;
    FD_ISSET(int fd, fd_set *set)  测试该集中的一个给定位是否有变化;

        在使用select函数之前,首先使用FD_ZERO和FD_SET来初始化文件描述符集,并使用select函数时,可循环使用FD_ISSET测试描述符集, 在执行完成对相关的文件描述符后, 使用FD_CLR来清除描述符集

     1 //select.c
    2 #include <sys/types.h>
    3 #include <sys/socket.h>
    4 #include <sys/wait.h>
    5 #include <stdio.h>
    6 #include <stdlib.h>
    7 #include <errno.h>
    8 #include <string.h>
    9 #include <sys/un.h>
    10 #include <sys/time.h>
    11 #include <sys/ioctl.h>
    12 #include <unistd.h>
    13 #include <netinet/in.h>
    14 #define SERVPORT 3333
    15 #define BACKLOG 10
    16 #define MAX_CONNECTED_NO 10
    17 #define MAXDATASIZE 100
    18 int main()
    19 {
    20 struct sockaddr_in server_sockaddr,client_sockaddr;
    21 int sin_size,recvbytes;
    22 fd_set readfd;
    23 fd_set writefd;
    24 int sockfd,client_fd;
    25 char buf[MAXDATASIZE];
    26 if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
    27 perror("socket");
    28 exit(1);
    29 }
    30 printf("socket success!,sockfd=%d\n",sockfd);
    31 server_sockaddr.sin_family=AF_INET;
    32 server_sockaddr.sin_port=htons(SERVPORT);
    33 server_sockaddr.sin_addr.s_addr=INADDR_ANY;
    34 bzero(&(server_sockaddr.sin_zero),8);
    35 if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){
    36 perror("bind");
    37 exit(1);
    38 }
    39 printf("bind success!\n");
    40 if(listen(sockfd,BACKLOG)==-1){
    41 perror("listen");
    42 exit(1);
    43 }
    44 printf("listening....\n");
    45 FD_ZERO(&readfd);
    46 FD_SET(sockfd,&readfd);
    47 while(1){
    48 sin_size=sizeof(struct sockaddr_in);
    49 if(select(MAX_CONNECTED_NO,&readfd,NULL,NULL,(struct timeval *)0)>0){
    50 if(FD_ISSET(sockfd,&readfd)>0){
    51 if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){
    52 perror("accept");
    53 exit(1);
    54 }
    55 if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){
    56 perror("recv");
    57 exit(1);
    58 }
    59 if(read(client_fd,buf,MAXDATASIZE)<0){
    60 perror("read");
    61 exit(1);
    62 }
    63 printf("received a connection :%s",buf);
    64 }/*if*/
    65 close(client_fd);
    66 }/*select*/
    67 }/*while*/
    68 }
    69
    70

    六 常见面试题
    常见面试题1:什么是超级块和索引节点,他们的作用分别是什么?


    常见面试题2:对文件进行操作时,如何确定当前读写位置?

  • 相关阅读:
    极客教学笔记---Java实现简单聊天客户端模拟
    java单例模式四模板
    关于在命令行进行文件输入输出重定向的小笔记
    Python爬虫入门之爬取图片
    Python爬虫入门之查询ip地址
    Python爬虫入门之get网页信息并作为文本输出
    Checker
    Manacher模板
    POJ3974——Palindrome
    Seek the Name, Seek the Fame
  • 原文地址:https://www.cnblogs.com/zeedmood/p/2417011.html
Copyright © 2011-2022 走看看