文件I/O操作(1)
linux系统调用和用户编程接口(api)
系统调用是指在操作系统提供给用户程序调用的一组“特殊”的接口,用户程序可以通过这组特殊的接口来获取操作系统内核提供的服务,例如用户可以通过进程控制相关的系统调用来创建进程,实现进程调度,进程管理
为什么用户不能直接访问系统内核提供的服务?这是因为在linux中,为了更好地保护内核空间,将程序运行的空间分为内核空间和用户空间(也就是常称为的内核态和用户态)
内核态和用户态运行在不同的级别上,在逻辑上也是相互隔离的,新词用户进程在通常情况下是不允许范围内核数据,也无法使用内核的函数,他们只能在用户空间操作用户数据,调用用户空间的函数
但是,在有些情况下,用户空间的进程需要获得一定的系统服务,调用内核空间程序,这时操作系统就必须利用系统提供给用户的特殊接口,系统调用规定用户进程进入内核空间的具体位置,进行系统调用的时,程序从用户空间进入内核空间,处理完后在返回用户空间
linux系统调用部分是非常精简的系统调用(只有250)他继承了unix系统调用最基本和最有用的部分,这些系统调用按照功能和逻辑大致可以分为
进程控制,进程间通信,文件系统控制,系统控制,存储管理,网络管理,socket控制,用户管理
用户编程的api
前面讲到的系统调用便不是直接与程序员进行交互,它仅仅是一个软中断机制向内核提出请求,以获取内核服务的接口,在实际使用中,程序员调用的是用户编程对应的接口--api
linux中文件及文件描述符概述
在linux中对目录和设备的操作都等同于对文件的操作,因此大大简化了系统对不同设备的处理,linux中的文件主要分为4种,普通文件,目录文件,链接文件和设备文件,对于linux而言,所有对设备和文件的操作都是使用文件描述符来进行的
文件描述符是一个非负整数,他是一个索引值,并指向内核中每个进程打开文件的记录表,当打开一个现存文件,或者是创建一个新的文件时,内核就像进程返回一个文件描述符,但需要写入文件时,也需要吧文件描述符作为参数传递给相应的函数
通常一个进程启动时,都会打开三个文件,标准输入,标准输出,标准出错处理,对应文件描述符为0,1,2。
基于文件描述符的I/0操作虽然不能移植到类linux以外的系统上去(如windows)但它往往是实现某些I/0操作的唯一途径,基于文件描述符的I/O操作时linux中对常用的操作之一
底层文件I/O操作
这里主要是5个函数open(),read(),write(),lseek(),和close()这些函数的特点是不带缓存,直接对文件(包括设备)进行读写操作
open()函数用于打开或创建文件,在打开或创建文件时,可以指定文件的属性和用户的权限等各种参数。
close()是一个用于关闭一个被打开的文件。当一个进程终止时,所有被他打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示的关闭一个文件
read()函数用于将从指定的文件描述符中读取数据放到缓存区,并返回实际读入的字节数,若返回0,则表示没有数据可读,即已经达到文件尾,读操作文件从文件的当前指针位置开始,当从终端设备文件中读取数据时,通常一次最多读取一行
write()函数用于向打开的文件中写入数据,写操作从文件的当前指针位置开始,对磁盘文件进行写操作,若磁盘已经写满,或者超出文件的长度,则write()函数返回失败
lseek()函数用于在指定的文件描述符中将文件的指针定位到相应的位置,它只能用在可定位(可随机访问)文件操作中,管道,套接字,和大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek
open函数的原型 int open(const char *pathname ,int flags);pathname是指文件的路径名和名称,可以是绝对路径或者是相对路径,flag 是指定了文件的打开方式
O_RDONLY 以只读方式,要求文件存在
O_WRONLY以只写的方式打开,要求文件存在
O_RDWR可读可写的方式打开,要求文件存在
O_CREAT 创建文件
O_EXCL创建互斥
O_APPEND 以追加的方式写入首先要有写权限
O_TRUNC 清空原文件内容
返回值 为文件描述符,非负整数,文件后续操作的标志
open的另外一种原型为int open(const char *pathname,int flags,mode_t mode)前面的参数和一样,mode是指创建文件时的权限0x777 0x644 0x664等等
打开文件失败的原因分析:1文件不存在时指定可读,2文件所在目录无操作权限 3其他原因,文件操作权限,文件是否可用
close函数 函数原型为int close (int fd);
fd为open返回值的文件描述符,关闭文件后,该文件描述符将不可再用,返回值表示关闭是否成功,但我们编程时不处理该返回值,打开文件必须关闭,否则会造成文件描述符的泄漏,特别是异常情况,可以关闭0,1,2;
示例代码:
read 函数原型为 int read(int fd, void *buf,int count);fd为文件描述符(注意此处不能是文件名),buf为存储读取的内容的内存地址(指针),一定不能是野指针或者是空指针,count是本次读取的字节数,要求count要小于等于buf的大小,如果存储字符串时,返回值为成功读取的字节数,如果读取失败,返回小于0的值,返回值为0表示读取到文件末尾
示例代码:
#include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> int main(void) { int fd1,size; char buf[128]; fd1 = open("/etc/net/works",O_RDONLY); if(fd1<0) { printf("open fail\n"); exit(0); } while(1) { size = read(fd1,buf,64); if(size<0) { printf("read error\n"); exit(1); } if(size ==0) break; buf[size+1] = '\0'; printf("%s",buf); } close(fd1); return 0; }
memset用于在某段内存中填充那一个数,函数包含的头文件是#include<string.h>函数原型为
void *memset(void *s,int c,size_t n);例如memset(buf,0,,128);
注意 :read函数会自动对文件的操作位置进行偏移,读完几个字节,之后就偏移几个字节,自动偏移,如果到达文件末尾,不能再次偏移
如果读取失败的原因:
1文件的读取权限问题
2,文件没有内容,需要注意,如果使用O_TRUNC时,文件内容已经被清楚
3:如果该处出现段错误,这主要考虑内存指针是否为野指针或者是否越界
write函数;函数原型为 int write(int fd, void *buf,int count);
fd为写入的文件描述符,buf为需要写入的内容的指针,count是写入的字节数,这里的buf要小于buf的有效值,返回值为成功写入的字节数,返回值小于0表示写入失败
write也会对文件的操作位置进行偏移,如果为追加方式时,从文件的末尾开始写入,如果写入失败,如果写入失败,这分析原因为:1)文件是否有写权限,2)文件写入多错误时,可以查看内存是否越界,3)注意使用)O_TRUNC会清空原内容
#include<stdio.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<string.h> #include<unistd.h> int main(void) { int fd1,fd2,size,f3; char buf[128]; fd1 = open("/etc/xinetd.d/daytime",O_RDONLY); if(fd1<0) { printf("open fail\n"); exit(1); } fd2 = open(""daytime1",O_RDWR|O_CREAT,777); if(fd2<0) { 1,1 Top printf("create daytime1 fail\n"); exit(1); } while(1) { memset(buf,0,127); size = read(fd1,buf,127); if(size<0) { printf("read fail\n"); exit(1); } if(size==0) break; buf[128]='\0'; printf("%s",buf); f3 = write(fd2,buf,size); if(f3<0) { printf("write fail\n"); exit(1); } } close(fd1); close(fd2); return 0; }
版权所有,转载请注明转载地址:http://www.cnblogs.com/fengdashen/p/3300614.html