嵌入式成长轨迹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:对文件进行操作时,如何确定当前读写位置?