Linux C高级编程——文件操作之系统调用
Linux C高级编程文件操作之系统调用
宗旨:技术的学习是有限的,分享的精神是无限的。
库函数是一些完毕特定功能的函数。一般由某个标准组织制作公布,并形成一定的标准。使用库函数编写的函数一般能够应用于不同的平台而不须要做不论什么改动,具有非常好的可移植性。
系统调用函数与操作系统直接相关,不同的操作系统所使用的系统调用可能不太一样,因此,假设两个操作系统差异非常大,系统调用函数的可移植性就不高。
比如windows採用的系统调用的应用程序不能直接在Linux下编译运行。
之所以使用系统调用是由于系统资源的有限性以及内核管理的方便。系统调用将上层内的应用开发与底层的硬件实现分开,上层应用不须要关注底层硬件的详细实现。
Linux的系统调用使用软中断实现,使用系统调用后。该程序的状态将从用户态切换到内核态。库函数实现终于也要调用系统调用函数。但它封装了系统调用操作,从而添加了代码的可移植性。
1、open()函数
——用于打开或者创建一个文件
(1)函数原型:
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int open(constchar* 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<string.h>
char* strerror(int errno);
perror函数依据当前的errno,输出一条出错信息(void perror(constchar* msg))
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main(int argc, char *argv[]) { int fd; if(argc < 2) { puts("please input the open filepathname!\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 filefailure!\n"); exit(1); } else { printf("open file %d success!\n", fd); } close(fd); return 0; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
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<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> void create_file(char *filename) { /*创建的文件具有什么样的属性?*/ if(creat(filename, 0755) < 0) { printf("create file %sfailure!\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 thefilename,please try again!\n"); exit(EXIT_FAILURE); } for(i = 1; i < argc; i++) { create_file(argv[i]); } return 0; }
3、lseek函数(和fseek相似)
——用于改动当前文件偏移量
(当前文件偏移量的作用:规定了从文件什么地方開始进行读、写操作)
——通常,读写操作结束时,会使文件偏移量添加读写的字节数
——当打开一个文件时,除非指定了O_APPEND标志,否则偏移量被设置为0
(1) 函数原型
#include<sys/types.h> #include<unistd.h> 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_tcurrent_position;
Current_position= lseek(fd,0,SEEK_CUR);
lseek操作并不引起不论什么I/O操作,仅仅是改动内核中的记录(并不引起磁盘的訪问 操作)
(5)空洞文件
——使用lseek改动文件偏移量后。当前文件偏移量有可能大于文件的长度
——在这样的情况下。对该文件的下一次写操作,将加长该文件
——这样文件里形成了一个空洞。对空洞区域进行读,均返回0
4、read函数
——用于从文件里读出数据
(1)函数原型
#include<unistd.h> 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<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #define LENGTH 100 int main(void) { int fd, len; char str[LENGTH]; /* 创建并打开文件 */ fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if(fd) { /* 写入 Hello, software weekly字符串 */ write(fd, "Hello,Software Weekly", strlen("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); return 0; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
5、write函数
——用于向文件里面写入数据
(1) 函数原型
#include<unistd.h>
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 <sys/ioctl.h> int ioctl(int d, int request, ...)
(2) 參数
第一个參数d:某个设备的文件描写叙述符
第二个參数request:是ioctl的命令,可变參数取决于request。一般是是一个指向 变量或者结构体的指针
(3)返回值
若出错返回-1,成功返回其它值。取决于request
ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)——获得终端设备的窗体大小
manioctl_list——能够看require的各种參数
8、mmap函数
——能够把磁盘文件的一部分直接映射到内存,这样文件里的位置直接就有相应的内存地址。对文件的读写能够直接用指针来做而不须要read/write函数
(1) 函数原型
#include<sys/mman.h>
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
10 dup函数
——拷贝文件描写叙述符
(1) 函数原型
#include <unistd.h> int dup(int oldfd);
(2) 參数
oldfd:待复制的文件描写叙述符
(3)返回值
成功返回新的文件描写叙述符;失败返回-1
11 dup2函数
——拷贝文件描写叙述符
(1) 函数原型
#include <unistd.h> int dup2(int oldfd, int newfd);
(2) 參数
oldfd:待复制的文件描写叙述符
newfd:新的文件描写叙述符
(3) 返回值
成功返回新的文件描写叙述符;失败返回-1
//小项目:用系统调用函数实现文件拷贝的功能: /************************************************************************* > File Name: copy.c > Author: libang > Mail: 18211438613@163.com > Created Time: 2014年07月16日 星期三 00时15分12秒 ************************************************************************/ #include<stdio.h> #include<string.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> int copy_file(intsrc_fd, int dest_fd); int main(intargc, char *argv[]) //注意先搭建主框架 { if(argc < 3) { printf("inputerror!\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 openfail!\n"); exit(0); } copy_file(src_fd, dest_fd); printf("success!\n"); close(src_fd); close(dest_fd); return 0; } int copy_file(intsrc, int dest) { char buf[128]; int r_ret, w_ret; char *tmp; //memset(buf,0,128); printf("success!\n"); while((r_ret = read(src, buf, 128)) > 0) //经典的拷贝算法 { tmp = buf; while(r_ret) { w_ret = write(dest, tmp, r_ret); r_ret = r_ret - w_ret; printf("%d\n", r_ret); tmp += w_ret; } //memset(buf,0,128); } printf("success!\n"); return 0; }