这 2 本书的内容类似,第一本对知识点有更细致的描述,适合初学者;第二 本比较直接,一上来就是各种函数的介绍,适合当作字典,不懂时就去翻看一下。 做纯 Linux 应用的入门,看这 2 本书就可以了,我们的侧重于“嵌入式 Linux”。
一、文件IO
1.什么是文件:
Linux 的文件既可以是真实保存到存储介质的文件也可以 是自身内核提供的虚拟文件,还可以是设备节点。
2.怎么知道这些函数的用法?
Linux 下有 3 大帮助方法:help、man、info。
想查看某个命令的用法时,比如查看 ls 命令的用法,可以执行:
ls --help
help 只能用于查看某个命令的用法,而 man 手册既可以查看命令的用法,还可以查看函数的详细介绍等等。它含有 9 大分类,如下:
1 Executable programs or shell commands // 命令 2 System calls (functions provided by the kernel) // 系统调用,比如 man 2 open 3 Library calls (functions within program libraries) // 函数库调用 4 Special files (usually found in /dev) // 特殊文件, 比如 man 4 tty 5 File formats and conventions eg /etc/passwd // 文件格式和约定, 比如 man 5 passwd 6 Games // 游戏 7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) //杂项 8 System administration commands (usually only for root) // 系统管理命令 9 Kernel routines [Non standard] // 内核例程
比如想查看 open 函数的用法时,可以直接执行“man open”,发现这不是 想要内容时再执行“man 2 open”。
在 man 命令中可以及时按“h”查看帮助信息了解快捷键。常用的快捷键是:
f 往前翻一页 b 往后翻一页 /patten 往前搜 ?patten 往后搜
就内容来说,info 手册比 man 手册编写得要更全面,但 man 手册使用起来 更容易些。
以书来形容 info 手册和 man 手册的话,info 手册相当于一章,里面含有若干节,阅读时你需要掌握如果从这一节跳到下一节;而 man 手册只相当于一节, 阅读起来当然更容易。
3.文件IO的分类
标准IO: fopen/fread/fwrite/fseek/fflush/fclose
系统IO: open/read /write/lseek/close
这2种IO函数的区别:
二、使用open函数打开文件
1.用man命令进行查看文件使用方法
man 2 open
函数作用:
作用:打开或者创建一个文件
pathname: 文件路径名,或者文件名
flags:表示打开文件所采用的操作
O_RDONLY:只读模式
O_WRONLY:只写模式
O_RDWR:可读可写
O_APPEND:表示追加,如果原来文件里面有内容,则这次写入会写在文件的最末尾。
O_REAT:表示如果指定文件不存在,则创建这个文件
O_EXCL:表示如果要创建的文件已存在,则出错,同时返回--1,并且修改·errno·的值。
O_TRUNC:表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为0。“
2.open.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> /* *./open_ 1.txt *argc = 2 *argv[0] = "./open" *argv[1] = "1.txt" */ int main(int argc, char **argv) { int fd; if(argc != 2) { printf("Uage:%s <file>\n",argv[0]); return -1; } fd = open(argv[1],O_RDWR); if (fd < 0) { printf("can not open file %s\n",argv[1]); printf("errno = %d\n",errno); printf("err: %s\n",strerror(errno)); perror("open"); } else { printf("fd = %d\n",fd); } while(1) { sleep(10); } close(fd); return 0; }
三、使用open函数创建文件
设置创建文件并开启权限
fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0644);
create.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> /* *./create 1.txt *argc = 2 *argv[0] = "./create" *argv[1] = "1.txt" */ int main(int argc, char **argv) { int fd; if(argc != 2) { printf("Uage:%s <file>\n",argv[0]); return -1; } fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0644); if (fd < 0) { printf("can not open file %s\n",argv[1]); printf("errno = %d\n",errno); printf("err: %s\n",strerror(errno)); perror("open"); } else { printf("fd = %d\n",fd); } while(1) { sleep(10); } close(fd); return 0; }
四、使用write函数写文件
1.用man命令进行查看文件使用方法
2 .write.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> /* *./write 1.txt str1 str2 *argc >= 3 *argv[0] = "./write" *argv[1] = "1.txt" */ int main(int argc, char **argv) { int fd; int i; int len; if(argc < 3) { printf("Uage:%s <file> <string1> <string2> ...\n",argv[0]); return -1; } fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0644); if (fd < 0) { printf("can not open file %s\n",argv[1]); printf("errno = %d\n",errno); printf("err: %s\n",strerror(errno)); perror("open"); } else { printf("fd = %d\n",fd); } for(i = 2; i < argc; i ++) { len = write(fd,argv[i],strlen(argv[i])); if(len != strlen(argv[i])) { perror("write"); break; } write(fd,"\r\n",2); } close(fd); return 0; }
五、lseek中间插入
1.用man命令进行查看文件使用方法
2 .write_in_poc.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> /* *./write 1.txt str1 str2 *argc >= 3 *argv[0] = "./write" *argv[1] = "1.txt" */ int main(int argc, char **argv) { int fd; int i; int len; if(argc != 2) { printf("Uage:%s <file>\n",argv[0]); return -1; } fd = open(argv[1],O_RDWR | O_CREAT,0644); if (fd < 0) { printf("can not open file %s\n",argv[1]); printf("errno = %d\n",errno); printf("err: %s\n",strerror(errno)); perror("open"); } else { printf("fd = %d\n",fd); } printf("lseek to offset 3 from file head\n"); lseek(fd,3,SEEK_SET); write(fd,"123",3); close(fd); return 0; }
六、读写文件
1.通用的 IO 模型:open/read/write/lseek/close
copy.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> /* * ./copy 1.txt 2.txt * argc = 3 * argv[0] = "./copy" * argv[1] = "1.txt" * argv[2] = "2.txt" */ int main(int argc, char **argv) { int fd_old, fd_new; char buf[1024]; int len; /* 1. 判断参数 */ if (argc != 3) { printf("Usage: %s <old-file> <new-file>\n", argv[0]); return -1; } /* 2. 打开老文件 */ fd_old = open(argv[1], O_RDONLY); if (fd_old == -1) { printf("can not open file %s\n", argv[1]); return -1; } /* 3. 创建新文件 */ fd_new = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd_new == -1) { printf("can not creat file %s\n", argv[2]); return -1; } /* 4. 循环: 读老文件-写新文件 */ while ((len = read(fd_old, buf, 1024)) > 0) { if (write(fd_new, buf, len) != len) { printf("can not write %s\n", argv[2]); return -1; } } /* 5. 关闭文件 */ close(fd_old); close(fd_new); return 0; }
1. book@100ask:~/source/06_fileio$ gcc -o copy copy.c 2. book@100ask:~/source/06_fileio$ ./copy copy.c new.c
2.不是通用的函数:ioctl/mmap
copy_mmap.c 源码如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <sys/mman.h> /* * ./copy 1.txt 2.txt * argc = 3 * argv[0] = "./copy" * argv[1] = "1.txt" * argv[2] = "2.txt" */ int main(int argc, char **argv) { int fd_old, fd_new; struct stat stat; char *buf; /* 1. 判断参数 */ if (argc != 3) { printf("Usage: %s <old-file> <new-file>\n", argv[0]); return -1; } /* 2. 打开老文件 */ fd_old = open(argv[1], O_RDONLY); if (fd_old == -1) { printf("can not open file %s\n", argv[1]); return -1; } /* 3. 确定老文件的大小 */ if (fstat(fd_old, &stat) == -1) { printf("can not get stat of file %s\n", argv[1]); return -1; } /* 4. 映射老文件 */ buf = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd_old, 0); if (buf == MAP_FAILED) { printf("can not mmap file %s\n", argv[1]); return -1; } /* 5. 创建新文件 */ fd_new = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd_new == -1) { printf("can not creat file %s\n", argv[2]); return -1; } /* 6. 写新文件 */ if (write(fd_new, buf, stat.st_size) != stat.st_size) { printf("can not write %s\n", argv[2]); return -1; } /* 5. 关闭文件 */ close(fd_old); close(fd_new); return 0; }
1. book@100ask:~/source/06_fileio$ gcc -o copy_mmap copy_mmap.c 2. book@100ask:~/source/06_fileio$ ./copy_mmap copy_mmap.c new2.c
七、内核接口
1.系统调用函数怎么进入内核?
以 open/read 为例,从用户态调用 API 触发异常进入内核的过程,最后调用的 sys_call_table 的函数指针数组如下:
2.内核的 sys_open、sys_read 会做什么?
进入内核后,sys_read/open 会首先根据参数判断文 件的类型,然后根据不同的文件类型去找不同的设备驱动,继而进行读写或者输 入输出控制。