1、库函数和系统调用函数
Linux上可以用C语言的文件操作函数(fopen fread fwrite fclose fseek)(库函数)
但是Linux系统有自己的文件操作函数(open read write close lseek stat)(系统调用函数)
库函数:把功能进行封装,封装成库函数,用不同的语言库就不一样。(eg:libc pritf函数,libstdc,stl),在用户态调用,在用户态执行,但是有些库函数需要转系统调用函数。
系统调用:操作系统内核代码提供给用户的接口,调用在用户态,执行在内核态。
应用程序既可以用库函数,也可以用系统调用函数。
2、Linux文件操作函数
在Linux上查看函数的说明文档用man命令
man 1 ls 查看命令
man 2 open 查看系统调用
man 3 fopen 查看库函数
(1)文件操作函数头文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
(2)open函数,打开文件
int open(const char *filename, int flags, mode_t mode);返回值:小于0出错(一般为-1),大于等于零为文件描述符fd
filename:文件名(路径)
flags打开方式,有O_RDONLY(只读), O_WRONLY(只写), O_RDWR(读写),O_APPEND(追加),O_CREAT(创建,如果文件存在无作用,如果不存在就创建新文件,如果不设置该方式没有文件返回失败),组合使用O_RDWR|O_CREAT
mode:创建文件时指定文件的权限。
(3)read函数,读数据
int read(int fd, void *buf, size_t count);
fd:文件描述符,由open返回值指定
buf:指定读取数据存储的起始地址,逻辑地址
count:一次读取的字节数(小于缓冲区大下)
返回值:大于0表示读取的字节数,等于0表示读取完,小于0表示读取失败 (一般为-1)
(4)write函数,写入数据
int write(int fd, const void *buf, size_t count);
fd:文件描述符,由open返回值指定;
buf:写入数据的起始地址。
count:写入数据的长度,字节数。ssize_t有符号整数;size_t无符号整数
返回值:大于表示写入的字节数,0表示没有写入任何数据,小于0表示写入失败。(一般为-1)
(5)lseek函数,移动文件指针
int lseek(int fd, int size, int flag);
fd:文件描述符
size:移动偏移量,移动大小
flag:移动标记,移动读写偏移量。SEEK_SET文件开始位置,SEEK_CUR表示指针当前位置,SEEK_END文件结束位置
返回值:大于等于0表示离文件开始处的偏移量,小于0表示写入失败。(一般为-1)
附:该函数可用于扩容,比如 lseek(fd,100,SEEK_END);表示扩容100个字节,但是要执行一次写操作扩容才生效。
(6)close函数,关闭打开的文件
int close(int fd);
fd:指定文件描述符
返回值:-1出错,0成功。
(7)stat函数,获取文件属性函数
int stat(const char *path, struct stat *buf); //path文件路径;buf传出参数,获取的文件信息就在buf里面
int fstat(int fd, struct stat *buf); //fd文件描述符
int lstat(const char *path, struct stat *buf); //lstat和stat的区别只在于符号链接文件,lstat读取符号链接文件,stat读取符号链接对应的源文件
返回值:成功返回0;失败返回-1
eg:创建一个文件a.txt,写入hello world,再读出hello world。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
int fd = open("./a.txt",O_RDWR|O_CREAT,0664);//open后文件指针都在起始位置
assert(fd!=-1);
char buff[128]="hello world";
write(fd,buff,strlen(buff));
lseek(fd,0,SEEK_SET);//从起始位置移动0个字节,移到文件开始
char readbuff[128]={0};
read(fd,readbuff,127);
printf("readbuff: %s\n",readbuff);
close(fd);
exit(0);
}
每一个系统调用函数都有一个系统调用号。
当我们调用一个函数的时候会发生几件事:
1、触发0X80中断,中断处理程序在内核中执行,通过中断陷入内核。
内核中有一个系统调用表,用于系统中断处理程序,作为跳转表。
在系统调用表中可以找到系统调用函数,系统调用函数接收参数,操作系统底层实现函数内容。
2、保存当前进程运行状态
3、将系统调用号保存到eax寄存器中,eax寄存器是一个通用寄存器。
4、系统返回的值由eax寄存器带回。