目录
通用IO模型:open、read、write、lseek、close
举例1
举例2
非通用函数:ioctl/mmap
mmap
ioctl
函数用法帮助命令
help
man
info
系统调用函数怎么进入内核?
内核的sys_open、sys_read会做什么?
通用IO模型:open、read、write、lseek、close
举例1
#include <stdio.h>
#include <sys/types.h> // open
#include <sys/stat.h> // open
#include <fcntl.h> // O_RDONLY...
#include <unistd.h> // close
/*
* ./read 1.txt
* argc = 2
* argv[0] = "./copy"
* argv[1] = "1.txt"
*/
int main(int argc, char **argv)
{
int fd = open(argv[1], O_RDONLY);
if(fd == -1)
{
printf("open error\n");
return -1;
}
char buf[3] = {0};
int ret;
ret = read(fd, buf, 3);
printf("buf = %s\n", buf);
printf("ret = %d\n", ret);
//指针自动会移动,会从第四个字符开始读取文件内容
ret = read(fd, buf, 3);
printf("buf = %s\n", buf);
printf("ret = %d\n", ret);
close(fd);
return 0;
}
举例2
#include <stdio.h>
#include <sys/types.h> // open
#include <sys/stat.h> // open
#include <fcntl.h> // O_CREAT...
#include <unistd.h> // close
/*
* ./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, 0666);
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;
}
非通用函数:ioctl/mmap
mmap
在Linux中,还可以把一个文件的所有内容映射到内存,然后直接读写内存就可以读写文件。
mmap:memory map,内存映射。将一个文件或其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系。
实现这种映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面(有修改过的页面)到对应的文件磁盘上,即完成了对文件的操作而不必再调用read、write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.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, 0666);
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;
}
ioctl
函数用法帮助命令
help
help只能用于查看某个命令的用法。比如:
ls --help
man
man手册既可以查看命令的用法,也可以查看函数的详细介绍等。man手册分九大类。
类别 | 描述 |
Executable programs or shell commands | shell命令或可执行程序 |
System calls(functions provided by the kernel) | 系统调用(内核提供的函数),比如man 2 open |
Library calls(functions within program libraries) | 函数库调用(函数库的函数) |
Special files(usually found in /dev) | 特殊文件(通常在/dev中找到),比如man 4 tty |
File formats and conventions eg /etc/passwd | 文件格式和约定,比如man 5 passwd |
Games | 游戏 |
Miscellaneous(including macro packages and conventions),e.g. man(7),groff(7) | 杂项(包括宏包和约定) |
System administration commands(usually only for root) | 系统管理命令(通常只有root) |
Kernel routines [Non standard] | 内核例程 |
比如想查看open函数的用法时,可以直接执行“man open”,发现这不是想要的内容时再执行“man 2 open”。
在man命令中可以按“h”查看帮助信息了解快捷键。常用的快捷键如下:
f 往前翻一页
b 往后翻一页
/patten 往前搜
?patten 往后搜
info
info手册比man手册编写得更全面,但man手册使用起来容易。
可以直接执行“info”命令,按“H”快捷键查看帮助信息了解快捷键。常用的快捷键如下:
系统调用函数怎么进入内核?
以open/read为例,从用户态调用API触发异常进入内核的过程如上图所示。最后调用的sys_call_table的函数指针数组如下:
CALL(sys_restart_syscall) /* 0 */
CALL(sys_exit)
CALL(sys_fork)
CALL(sys_read)
CALL(sys_write)
CALL(sys_open) /* 5 */
CALL(sys_close)
CALL(sys_ni_syscall)
CALL(sys_creat)
CALL(sys_link)
CALL(sys_unlink) /* 10 */
CALL(sys_execve)
CALL(sys_chdir)
CALL(OBSULETE(sys_time))
内核的sys_open、sys_read会做什么?
进入内核后,sys_read/open会首先根据参数判断文件的类型,然后根据不同的文件类型去找不同的设备驱动,继而进行读写或输入输出控制。