Linux C 高级编程之文件操作
——系统调用
简介:
库函数是一些完成特定功能的函数,一般由某个标准组织制作发布,并形成一定的标准。使
用库函数编写的函数一般可以应用于不同的平台而不需要做任何修改,具有很好的可移植
性。
系统调用函数与操作系统直接相关,不同的操作系统所使用的系统调用可能不太一样,因此,
如果两个操作系统差异很大,系统调用函数的可移植性就不高。例如 windows 采用的系统调
用的应用程序不能直接在 Linux 下编译运行。
之所以使用系统调用是因为系统资源的有限性以及内核管理的方便,系统调用将上层内的应
用开发与底层的硬件实现分开,上层应用不需要关注底层硬件的具体实现。Linux 的系统调
用使用软中断实现,使用系统调用后,该程序的状态将从用户态切换到内核态。库函数实现
最终也要调用系统调用函数,但它封装了系统调用操作,从而增加了代码的可移植性。
1 open()函数
——用于打开或者创建一个文件
(1)函数原型:
#include
#include
#include
int open(const char* pathname,int flags,...)
(2)参数
pathname:要创建或者打开的文件名
flags: 指定文件的打开模式、标志等信息
必须指定一个:O_RDONLY ——只读 O_WRONLY——只写 O_RDWR——读写
可选标志(按位或):O_APPEND——追加
O_TRUNC——若文件存在,读写方式打开或只写打开,则文件长度为 0
O_CREAT——若文件不存在,则创建文件,此时,open 需要第三个参数,用于指定该
文件的访问权限(umask 可以看掩码)
O_EXCL——若同时指定为 O_CREAT 标志,而文件已经存在,则会出错,可用于文件
是否存在
O_NONBLOCK 对于设备文件,以 O_NONBLOCK 方式打开可以做非阻塞 I/O
(3)返回值
整数类型——成功时,返回文件描述符;出错时,返回-1
(4)文件描述符
(文件描述符——已打开文件的索引——通过索引找到已打开文件)
文件描述符是一个非负的整数
文件描述符 0,1,2 分别表示标准输入,标准输出,标准错误输出,在进程创建时,已经
打开,open 返回的文件描述符一定是该进程尚未使用的最小描述符
(5)出错处理
errno.h 头文件中,定义了 errno,当 API 调用出错时,errno 说明出错的具体原因
可简单的将 errno 理解成整型数据
出错信息转换成可读字符串
#include
char *strerror(int errno);
perror 函数根据当前的 errno,输出一条出错信息(void perror(const char* msg))
例如:
#include
#include
#include
#include
#include
int main(int argc ,char *argv[]){
int fd;
if(argc<2){
puts("please input the open file pathname!\n");
exit(1);
}
//如果 flag 参数里有 O_CREAT 表示,该文件如果不存在,系统则会创建该文件,该文
件的权限由第三个参数决定,此处为 0755
//如果 flah 参数里没有 O_CREAT 参数,则第三个参数不起作用.此时,如果要打开的
文件不存在,则会报错.
//所以 fd=open(argv[1],O_RDWR),仅仅只是打开指定文件
if((fd=open(argv[1],O_CREAT|O_RDWR,0755))<0){
perror("open file failure!\n");
exit(1);
}else{
printf("open file %d
success!\n",fd);
}
close(fd);
exit(0);
}
2 creat()函数
——用于创建一个新文件
(1)函数原型
int creat(const char *pathname,mode_t mode)
(2)参数
pathname:要创建的文件名(包括路径信息)
mode:同 open 的第二个参数,讨论文件的访问权限位时分析:
S_IRUSR——可读 S_IWUSR——可写 S_IXUSR——可执行
S_IRWXU——可读、写、
执行
除了用上述宏之外,还可以用数字来表示文件的权限:
可读——4 可写——2 可执行——1 无任何权限——0
(3)返回值
成功返回只写打开的文件描述符,出错返回-1
(4)creat 函数缺点:它以只写方式打开创建的文件。若要创建一个临时文件,并先
写该文件,然后又读该文件,则必须先调用 creat,close,然后再 open.简便方法:
open(pathname,O_RDWR | O_CREAT | O_TRUNC,mode);
例如:
#include
#include
#include
#include
#include
void create_file(char *filename){
/*创建的文件具有什么样的属性?*/
if(creat(filename,0755)<0){
printf("create file %s failure!\n",filename);
exit(EXIT_FAILURE);
}else{
printf("create file %s success!\n",filename);
}
}
int main(int argc,char *argv[]){
int i;
if(argc<2){
perror("you haven't input the filename,please try again!\n");
exit(EXIT_FAILURE);
}
for(i=1;i
#include
off_t lseek(int filedes,off_t offset,int whence)
(2) 参数
第一个参数 filedes:open/creat 函数返回的文件描述符
第二个参数 offset:
相对偏移量:需结合 whence 才能计算出真正的偏移量
类型 off_t:通常情况下是 32 为数据类型
第三个参数 whence:该参数取值是三个常量
SEEK_SET:当前文件偏移量为——距文件开始出的 offset 个字节
SEEK_CUR:当前文件偏移量 + offset(可正可负)
SEEK_END:当前文件长度 + offset(可正可负)
(3)返回值:
成功返回新的文件偏移量,失败返回-1
(4)获取当前的偏移量:
Off_t current_position;
Current_position = lseek(fd,0,SEEK_CUR);
lseek 操作并不引起任何 I/O 操作,只是修改内核中的记录(并不引起磁盘的访问
操作)
(5)空洞文件
——使用 lseek 修改文件偏移量后,当前文件偏移量有可能大于文件的长度
——在这种情况下,对该文件的下一次写操作,将加长该文件
——这样文件中形成了一个空洞。对空洞区域进行读,均返回 0
4 read 函数
——用于从文件中读出数据
(1)函数原型
#include
ssize_t read(int fd,void *buff,size_t nbytes)
(2)参数
第一个参数 fd:文件描述符
第二个参数 buff:指向缓冲区,用于存放从文件读出的数据
第三个参数 nbytes:unsigned int;需要从文件中读出的字节数
——缓冲区的大小 >= nbytes
(3)返回值
返回值类型:ssize_t,即 int
出错返回-1 成功:返回从文件中实际读到的字节数,当文件读到结尾时,则返回 0
(4)很多情况下,read 实际读出的字节数都小于要求读出的字节数
——读普通文件,在读到要求的字节数之前,就到达了文件尾端
——当从终端设备读时,通常一次最多读一行
——当从网络读时,网络中的缓冲机构可能造成 read 函数返回值小于所要求读出
的字节数
——某些面向记录的设备,如磁盘,一次最多返回一个记录
#include
#include
#include
#include
#define LENGTH 100
main()
{
int fd, len;
char str[LENGTH];
fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /* 创建并
打开文件 */
if (fd)
{
write(fd, "Hello, Software Weekly", strlen("Hello, software weekly"));
/* 写入 Hello, software weekly 字符串 */
close(fd);
}
fd = open("hello.txt", O_RDWR);
len = read(fd, str, LENGTH); /* 读取文件内容 */
str[len] = '\0';
printf("%s\n", str);
close(fd);
}
5 write 函数
——用于向文件里面写入数据
(1) 函数原型
#include
ssize_t write(int fd,const void *buff,size_t nbytes)
(2) 参数
第一个参数 fd:文件描述符
第二个参数 buff:指向缓冲区,存放了需要写入文件的数据
第三个参数 nbytes:需要写入文件的字节数
(3) 返回值
返回值类型:ssize_t,即 int
出错返回-1,成功返回实际写入文件的字节数
(4) write 出错的原因
——磁盘满
——没有访问权限
——超过了给定进程的文件长度限制
6 close 函数
——用于关闭一个已打开的文件
(1)函数原型
int close(int filedes)
(2) 返回值
成功返回 0,出错返回-1
(3)参数
filedes:文件描述符
(4)当 close 函数关闭文件时,会释放进程加在该文件上的所有记录锁
内核会对进程打开文件表,文件对象,索引节点表项等结构进行修改,释放相关的
资源
当进程退出时,会关闭当前所有已打开的文件描述符
7 ioctl 函数
——用于向设备发控制和配置命令(这些数据不能用 read/write 读写)
(1) 函数原型
#include
int ioctl(int d,int request,...)
(2) 参数
第一个参数 d:某个设备的文件描述符
第二个参数 request:是 ioctl 的命令,可变参数取决于 request,通常是是一个
变量或者结构体的指针
指向
(3)返回值
若出错返回-1,成功返回其他值,取决于 request
ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)——获得终端设备的窗口大小
man ioctl_list——可以看 require 的各种参数
8 mmap 函数
——可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存
地址,对文件的读写可以直接用指针来做而不需要 read/write 函数
(1) 函数原型
#include
void *mmap(void *addr,size_t len,int prot,int flag,int filedes,off_t off)
(2) 参数
addr:NULL——内核会自己在进程地址空间中选择合适的地址建立映射
len:需要映射的那部分文件的长度
off:从文件的什么位置开始映射,必须是页大小的整数倍(32 位——4K)
filedes:该文件的文件描述符
prot:
PROT_EXEC——映射的这一段可执行,例如映射的共享库
PROT_READ——映射的这一段可读
PROT_WRITE——映射的这一段可写
PROT_NONE——映射的这一段不可访问
flag:
MAP_SHARED——多个进程对同一文件的映射是共享的,一个进程对映射的内存
做了修改,另一个进程也会看到这种变化。
MAP_PRIVATE——多个进程对同一文件的映射不是共享的,一个进程对映射的
内存做了修改,另一个进程不会看到这种变化,也不会真的写到文件中去。
(3)返回值
成功则返回映射的首地址,出错返回常数 MAP_FAILED。
9 access 函数
——判断文件是否可以进行某种操作
(1)函数原型
int access(const char *pathname,int mode)
(2)参数
pathname:文件名
mode:判断的访问权限:R_OK:文件可读 W_OK:文件可写
X_OK:文件可执行 F_OK:文件存在
(3)返回值
成功时,返回 0;如果一个条件不符合时,返回-1
小项目:用系统调用函数实现文件拷贝的功能:
/*************************************************************************
> File Name: copy.c
> Author: libang
> Mail: 1838039453@qq.com
> Created Time: 2014 年 07 月 16 日 星期三 00 时 15 分 12 秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
int copy_file(int src_fd,int dest_fd);
int main(int argc,char *argv[])//注意先搭建主框架
{
if(argc < 3)
{
printf("input error!\n");
exit(1);
}
int src_fd,dest_fd;
src_fd = open(argv[1],O_RDONLY);
dest_fd = open(argv[2],O_WRONLY | O_CREAT | O_TRUNC,0666);
if(src_fd < 0 | dest_fd < 0)
{
printf("file open fail!\n");
exit(0);
}