本文最后更新于:2021年3月7日 晚上
概览:Linux IO
errno与输出
errno
:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
可以通过函数perror(const char* s)
来打印对应的错误描述。
1 2 3 4
| #include <stdio.h> void perror(const char *s);
|
open函数1
1 2 3 4 5 6
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
int open(const char *pathname, int flags);
|
参数:
- pathname:要打开的文件路径
- flags:对文件的操作权限设置还有其他的设置
- O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
- 返回值:返回一个新的文件描述符,如果调用失败,返回-1
- 详细的可通过 man 2 open查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> //close函数需要使用
int main(){
int fd = open("a.txt",O_RDONLY);
if(fd == -1){ perror("open a.txt"); }
close(fd);
return 0; }
|
open函数2
1 2 3 4 5
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
|
参数:
- pathname:要创建的文件的路径
- flags:对文件的操作权限和其他的设置
- 必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个之间是互斥的
- 可选项:O_CREAT 文件不存在,创建新文件,
- 可选项:O_APPEND,追加文件内容
- 可选项:O_TRUNC,文件截断
- 可选项:O_NONBLOCK,设置非阻塞。
- mode:八进制的数,表示创建出的新的文件的操作权限,比如:
0775
- 最终的权限是:mode & ~umask
- umask的作用就是抹去某些权限,一般情况下是
0002
,可直接在终端输入umask
查看,也有相关函数可修改其值,可通过man来查看。
- 例如 0777 & ~(0002) = 0775。这样文件的权限775,则文件的权限为
rwxrwxr-x
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h>
int main(){
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1){ perror("create file"); }
close(fd);
return 0; }
|
read、write函数
1 2 3
| #include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
|
参数:
1 2 3
| #include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
|
- 参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:要往磁盘写入的数据,数据
- count:要写的数据的实际的大小
- 返回值:
- 成功:实际写入的字节数
- 失败:返回-1,并设置errno
拷贝文件案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h>
int main(){
int srcfd = open("english.txt",O_RDONLY); if(srcfd == -1){ perror("open 1"); return -1; }
int destfd = open("copy.txt",O_WRONLY|O_CREAT,0777); if(destfd == -1){ perror("open 2"); return -1; }
char buffer[1024] = {0}; int len = 0;
while((len = read(srcfd,buffer,sizeof(buffer))) > 0){ write(destfd,buffer,len); }
close(destfd); close(srcfd);
return 0; }
|
lseek函数
1 2 3 4 5 6 7 8
| #include <stdio.h> int fseek(FILE *stream, long offset, int whence);
#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
|
参数:
whence:
- SEEK_SET 设置文件指针的偏移量
- SEEK_CUR 设置偏移量:当前位置 + 第二个参数offset的值
- SEEK_END 设置偏移量:文件大小 + 第二个参数offset的值
返回值:返回文件指针的位置
作用:
1.移动文件指针到文件头
2.获取当前文件指针的位置
1
| off_t off = lseek(fd, 0, SEEK_CUR);
|
3.获取文件长度
1
| off_t len = lseek(fd, 0, SEEK_END);
|
4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
1 2
| lseek(fd, 100, SEEK_END)
|
应用举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h>
int main(){
int fd = open("file.txt",O_RDWR); if(fd ==-1){ perror("open"); return -1; }
off_t lens = lseek(fd,0,SEEK_END);
printf("file.txt length: %ld \n",lens);
lseek(fd,100,SEEK_END);
write(fd," ",1);
close(fd);
return 0; }
|
stat、lstat函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
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; blksize_t st_blksize; blkcnt_t st_blocks; time_t st_atime; time_t st_mtime; time_t st_ctime; };
|
参数:
- pathname:操作的文件的路径
- statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
- 返回值:
stat查看文件信息也可以通过在终端中使用stat来查看。
1 2 3 4 5 6 7 8 9
| colourso@c:~/桌面/ch1linux/09io$ stat file.txt 文件:file.txt 大小:111 块:8 IO 块:4096 普通文件 设备:801h/2049d Inode:807444 硬链接:1 权限:(0664/-rw-rw-r--) Uid:( 1000/colourso) Gid:( 1000/colourso) 最近访问:2021-03-05 15:40:28.434659770 +0800 最近更改:2021-03-05 15:45:56.297896243 +0800 最近改动:2021-03-05 15:45:56.297896243 +0800 创建时间:-
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h>
int main(){
struct stat statbuf;
int ret = stat("file.txt",&statbuf);
if(ret == -1){ perror("stat"); return -1; }
printf("file.txt size: %ld\n",statbuf.st_size); return 0; }
|
案例:模仿ls -l命令查看文件信息
st_mode变量
:文件的类型和存取的权限.
- 下图中的数字均为8进制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <pwd.h> #include <grp.h> #include <time.h> #include <string.h>
int main(int argc, char * argv[]) {
if(argc < 2) { printf("%s filename\n", argv[0]); return -1; }
struct stat st; int ret = stat(argv[1], &st); if(ret == -1) { perror("stat"); return -1; }
char perms[11] = {0};
switch(st.st_mode & S_IFMT) { case S_IFLNK: perms[0] = 'l'; break; case S_IFDIR: perms[0] = 'd'; break; case S_IFREG: perms[0] = '-'; break; case S_IFBLK: perms[0] = 'b'; break; case S_IFCHR: perms[0] = 'c'; break; case S_IFSOCK: perms[0] = 's'; break; case S_IFIFO: perms[0] = 'p'; break; default: perms[0] = '?'; break; }
perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-'; perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-'; perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-'; perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-'; perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-'; perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-'; perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
int linkNum = st.st_nlink;
char * fileUser = getpwuid(st.st_uid)->pw_name;
char * fileGrp = getgrgid(st.st_gid)->gr_name;
long int fileSize = st.st_size;
char * time = ctime(&st.st_mtime);
char mtime[512] = {0}; strncpy(mtime, time, strlen(time) - 1);
char buf[1024]; sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
printf("%s\n", buf);
return 0; }
|
使用:
1 2 3 4 5 6 7
| colourso@c:~/桌面/ch1linux/09io$ gcc -o myls ls-l.c colourso@c:~/桌面/ch1linux/09io$ ./myls ./myls filename colourso@c:~/桌面/ch1linux/09io$ ./myls file.txt -rw-rw-r-- 1 colourso colourso 111 Fri Mar 5 15:45:56 2021 file.txt colourso@c:~/桌面/ch1linux/09io$ ls -l file.txt -rw-rw-r-- 1 colourso colourso 111 3月 5 15:45 file.txt
|
文件属性操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <unistd.h> int access(const char *pathname, int mode);
#include <sys/stat.h> int chmod(const char *pathname, mode_t mode);
#include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_t length);
|
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h>
int main(){
int ret = access("a.txt",F_OK); if(ret == -1){ perror("access"); return -1; }
printf("a.txt is exit!\n");
ret = chmod("a.txt",0777); if(ret == -1){ perror("chmod"); return -1; }
printf("chmod success\n");
struct stat st;
ret = stat("a.txt",&st);
if(ret == -1){ perror("stat1"); return -1; }
printf("a.txt size is %ld\n",st.st_size); ret = truncate("a.txt",20);
if(ret == -1){ perror("truncate"); return -1; }
ret = stat("a.txt",&st);
if(ret == -1){ perror("stat2"); return -1; }
printf("a.txt size is %ld\n",st.st_size);
return 0; }
|
除此之外:
/etc/passwd
下存储了许多的用户名、uid所在组等信息
/etc/group
下存储了许多的组、gid等信息
目录操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <sys/stat.h> #include <sys/types.h> int mkdir(const char *pathname, mode_t mode);
#include <sys/stat.h> #include <sys/types.h> int rmdir(const char *pathname);
#include <stdio.h> int rename(const char *oldpath, const char *newpath);
#include <unistd.h> int chdir(const char *path);
#include <unistd.h> char *getcwd(char *buf, size_t size);
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h>
int main(){
char buf[128]; getcwd(buf,sizeof(buf)); printf("now work path: %s\n",buf);
int ret = chdir("/home/colourso/桌面/ch1linux/09io");
if(ret == -1){ perror("chdir"); return -1; }
char buf1[128]; getcwd(buf1,sizeof(buf1)); printf("now work path: %s\n",buf1);
ret = mkdir("dir1",0777); if(ret == -1){ perror("mkdir"); return -1; }
printf("dir1 create success\n");
ret = rename("dir1","dir2");
if(ret == -1){ perror("rename"); return -1; }
printf("dir1 rename dir2\n");
return 0; }
|
目录遍历函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name);
#include <dirent.h> struct dirent *readdir(DIR *dirp);
#include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp);
|
struct dirent
结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| struct dirent { ino_t d_ino; off_t d_off; unsigned short int d_reclen; unsigned char d_type; char d_name[256]; };
d_type DT_BLK - 块设备 DT_CHR - 字符设备 DT_DIR - 目录 DT_LNK - 软连接 DT_FIFO - 管道 DT_REG - 普通文件 DT_SOCK - 套接字 DT_UNKNOWN - 未知
|
案例:读取某个目录下所有普通文件个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <string.h> #include <stdlib.h>
int getFileNum(const char* path){
DIR* dir = opendir(path);
if(dir == NULL){ perror("opendir"); exit(0); }
int total = 0;
struct dirent *dirp = NULL;
while((dirp = readdir(dir)) != NULL){ if(dirp->d_type == DT_DIR){ char * dirname = dirp->d_name;
if(strcmp(dirname,".") == 0 || strcpy(dirname,"..") == 0){ continue; }
char pathname[256]; sprintf(pathname,"%s/%s",path,dirname); total += getFileNum(pathname); }
if(dirp->d_type == DT_REG){ total++; }
}
closedir(dir);
return total; }
int main(int argc,char * argv[]){
if(argc <2){ printf("%s path\n",argv[0]); exit(0); }
int num = getFileNum(argv[1]);
printf("普通文件个数为%d\n",num);
return 0; }
|
dup、dup2函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <unistd.h> int dup(int oldfd);
#include <unistd.h> int dup2(int oldfd, int newfd);
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h>
int main(){
int fd = open("a.txt",O_RDWR);
if(fd == -1){ perror("open"); return -1; }
int newfd = dup(fd); if(newfd == -1){ perror("dup"); return -1; }
printf("fd: %d,newfd: %d\n",fd,newfd);
close(fd);
char* buf = "hello world!"; write(newfd,buf,strlen(buf));
close(newfd);
int fd1 = open("1.txt",O_RDWR|O_CREAT,0664); int fd2 = open("2.txt",O_RDWR|O_CREAT,0664);
if(fd1 == -1 || fd2 == -1){ perror("open2"); return -1; }
printf("fd1: %d,fd2: %d\n",fd1,fd2);
int fd3 = dup2(fd1,fd2); if(fd3 == -1){ perror("dup2"); return -1; }
printf("fd1: %d,fd2: %d,fd3: %d\n",fd1,fd2,fd3);
char *buffer1 = "hello world -- by fd1"; char *buffer2 = "hello world -- by fd2";
write(fd1,buffer1,strlen(buffer1)); write(fd2,buffer2,strlen(buffer2));
close(fd1); close(fd3);
return 0; }
|
执行结果:
1 2 3 4
| fd: 3,newfd: 4 fd1: 3,fd2: 4 fd1: 3,fd2: 4,fd3: 4
|
fctnl函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <unistd.h> #include <fcntl.h>
int fcntl(int fd, int cmd, ...);
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <string.h>
int main(){
int fd = open("a.txt",O_RDWR); if(fd == -1){ perror("open"); return -1; }
int flag = fcntl(fd,F_GETFL); if(flag == -1){ perror("fctnl"); return -1; }
flag = flag | O_APPEND;
int ret = fcntl(fd,F_SETFL,flag);
if(ret == -1){ perror("fctnl2"); return -1; }
char * buf = "\n你好世界";
ret = write(fd,buf,strlen(buf)); if(ret == -1){ perror("write"); return -1; }
close(fd);
return 0; }
|
标准库IO与Linux IO
一般情况下推荐使用标准库的io,在多平台容易同统一,并且标准IO库还有缓冲区的使用,效率比普通的系统IO高。
虚拟地址空间
上述是虚拟的32位的linux的虚拟空间。具体的细节可参看CSAPP。
linux每一个运行的程序:即进程。操作系统都会为其分配一个0-4G的地址空间,即虚拟地址空间。
文件描述符