C语言中:fopen() fread() fwrite() fclose() fseek() ——库函数
Linux: open() read() write() close() lseek() stat() ——系统调用函数
1.库函数的系统调用函数的区别
系统调用函数是操作系统为内核代码提供给上层使用的函数接口,它的调用在用户态,可由应用程序调用,执行在内核态,由操作系统内核实现,有操作系统内核权限
库函数是语言自身带的库文件,其中是特定功能的实现,由编译器实现封装成的。在用户态调用,在用户态执行,但有些库函数需要转调系统调用函数。
库函数不能直接去操纵物理硬件,必须经过操作系统,所以有些库函数要转调系统调用函数才可以(库函数中只要需要底层支持的都绕不开操作系统,绕不过操作系统就要调用系统调用函数)
所以printf和scanf需要底层支持(一个从界面获取数据,一个将数据打印到界面),而strlen和strcpy则不需要
2.二者效率
(1)库函数有时效率高于系统调用函数,原因:
a.系统调用函数的需要从用户态切换到内核态,而库函数则不需要切换
b.用库函数申请内存时,可能会多分配一些内存,但使用系统调用函数申请时,不会多分配,你申请多少他只会给你分配多少,这在你还需要更大一些内存的情况下,库函数效率高一些
(2)系统调用函数有时效率高于库函数,原因:
有些库函数的调用还需转调系统调用函数
3.Linux文件操作函数
包含的头文件:
#include<sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
(1)int open(const char* filename,int flag,/*int mode*/)
返回值:<0 出错
>=0 正确,该值是文件描述符fd,打开的的文件标识 read write close lseek
参数解释:
filename:要打开的文件名(只给文件名就只会在当前路径下搜索)
flag:打开的方式 读O_RDONLY 写O_WRONLY 读写O_RDWR 追加O_APPEND
O_CREAT 创建(文件存在的话加这个选项没有意义,若文件不存在设置该参数时会创建一个新的文件,若文件不存在又没设置该参数,则打开失败报错,返回-1)
mode:只有在创建文件时指定文件的权限,nnnn,一般第一个n为0
(2)int read(int fd,void *buf,size_t size)
返回值:出错,返回-1
成功,返回读到的数据字节数
参数解释:
fd:读取的文件由open的返回值指定
buf:指定读取的数据存储的起始位置
size:指定一次最多读取的字节数(一般情况下<=缓冲区的大小,缓冲区的大小-1,预留‘\0’的的位置,不能大于缓冲区大小,防止越界)
(3)int write(int fd,void *buf,size_t size)
返回值:出错,返回-1
成功,返回已经写入成功的数据的长度(字节数)(正写时可能会出错)
参数解释:
fd:写入对象(open打开的灯文件)
buf:写入的数据的起始位置
size:写入数据的长度(字节),并不是buff空间的大小
char *buff[128]=”hello world”
write(fd,buff,strlen(buff));//长度是11
当写入时,文件不存在,O_WRONLY | O_CREAT
int fd=open(“./a.txt”,O_WRONLY | O_CREAT,0664);
(4)int close(int fd)关闭打开的文件
返回值:出错,返回-1
成功,返回0
参数解释:
fd:指定文件描述符
(5)int lseek(int fd,int size int flag) 移动读写偏移量
返回值:出错,返回-1
文件读写指针距文件开头的字节大小
参数解释:
fd:操作的文件
size:移动的大小(字节)
flag:移动标记,移动的起始位置:SEEK_SET(文件开头) SEEK_CUR(文件当前位置) SEEK_END(文件结尾)
(6)stat获取文件的属性信息
#include <sys/stat.h>
int stat(const char *pathname, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
int fstat(int filedes, struct stat *buf);
函数功能:获取文件信息并保存在buf所指的结构体stat中。
一旦给出pathname, stat函数就返回与此命名文件有关的信息结构。lstat 函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用文件的信息。fstat函数获取已在描述符filedes上打开文件的有关信息。
第二个参数buf是指针,
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
在Linux命令中,使用到stat()函数最多的可能是ls -1命令,用它可以获得有关一个文件的所有信息。
一个练习题,通过系统调用函数open,read等,完成文件的拷贝cp,代码:
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fdr=open("main.c",O_RDONLY);
assert(fdr!=-1);
int fdw=open("test.c",O_WRONLY | O_CREAT,0664);
assert(fdw!=-1);
char buff[128]={0};
while(1)
{
char buuff[128]={0};
int n=read(fdr,buff,127);
if(n<=0)
{
break;
}
write(fdw,buff,n);
}
close(fdr);
close(fdw);
}
三、以open函数为例,描述用户态到内核态的切换过程
描述:当在用户态调用open()的时候,首先会发生这三件事,
(1)保存程序上下文,当前程序的运行状态
(2)将函数对应的系统调用号保存到eax寄存器中
(3)触发0x80中断(系统调用专有中断),中断处理程序(内核中)
当触发了0x80中断,内核态的中断处理程序工作,在系统调用表中找到寄存器存放的系统调用号对应的函数,调用该函数,,此处调用的是sys_open(),在函数执行完成后返回时,用eax寄存器将返回值带回到用户态。这一过程完成。