文件操作之系统调用

一、Linux文件系统

Linux系统提供的文件系统,是树形层次结构的系统,最顶层是根“/”,然后在下层创建其他目录,所有的文件最终都归属到最顶层的根目录“/”,Linux支持多种文件系统。
每个文件都具有特定的属性,Linux系统的文件属性比较复杂,主要包括文件类型和文件权限两个方面。
在Linux中,最常见的文件类型有五种,他们是普通文件,目录文件,衔接文件,管道文件,和设备文件

点击打开链接http://blog.csdn.net/muge0913/article/details/7339671博文中由对此五种文件类型的讲解。

1、Linux文件权限

对于Linux系统中的文件来说,它的权限可以分为 四种:可读,可写,可执行和无权限,分别用“r”,“ w”, “x”, “-”表示。

在Linux中,对于任意一个文件来说,它都有一个特定的所有者,就是这个文件的创造者,同时,Linux系统是按照组分类的,一个用户属于一个或多个组,文件所有者意外的用户,又可以分为文件所有者的同组用户和其他用户,因此Linux按照文件所有者,文件所有者同组用户和其他用户三类规定不同的文件访问权限。

在查看文件的所有详细信息时,可以看到文件显示的作为文件权限的10个字符,可分为四部分:

clx@think:/etc$ ls -al
drwxr-xr-x   2 root    root    12288 2013-05-09 23:37 alternatives
-rw-r--r--   1 root    root      395 2010-03-05 10:29 anacrontab
lrwxrwxrwx   1 root    root       15 2013-04-29 23:18 blkid.tab -> /dev/.blkid.tab

第一位:表示文件类型.(“-” 普通文件,“l” 链接文件 “b”设备文件 “p” 管道文件,“d” 目录文件) 
第一位到第四位:(rwx)表示文件所有者的访问权限。
第五位到第七位:(rwx)表示文件所有者同组用户的访问权限。
第八位到第十位:(rwx)表示文件所有者同组用户的访问权限。

2 、文件的相关信息

在Linux系统中,与文件相关的信息有三项,他们是:文件的目录结构,索引节点和数据本身
1.文件的目录结构:
系统中的每一个目录都处于一定的目录结构中,该结构含有目录中所有的目录项的列表,每一个目录项都含有一个名称和索引节点,借助于名称可以访问目录项的内容,而索引节点号则提供了所需引用文件自身的信息。
2.索引节点
在Linux系统中,所有的文件都有一个与之相连的索引节点(inode),索引节点是用来保存文件信息的,它包含以下信息:
文件使用的设备号;
索引节点号;
文件访问权限;
衔接到此文件的目录树;
所有者用户识别号;
组识别号;
设备文件的设备号;
以字节为单位的文件容量
包含该文件的磁盘块的大小;
该文件所占的磁盘块数;
最后一次访问该文件的时间;
最后一次修改该文件的时间;
最后一次改变了文件状态的时间。
在系统中定义了stat结构体来存放这些信息,stat结构的定义如下:
Struct stat{
  dev_t st_dev;  /*文件使用的设备号*/
  ino_t st_ino;   /*节点号 */
   mode_t st_mode;  /*文件权限*/
   nlink_t st_nlink;  /*衔接到此文件的目录树*/
   size_t st_size   /*文件大小*/
   uid_t st_uid;  /*用户ID*/
   gid_t st_gid;  /*组ID*/
   dev_t st_rdev;  /*设备类型*/
   off_t st_pff;   /*文件字节数*/
   unsigned long st_blksize;  /*块大小*/
  unsigned long st_blocks;  /*块数*/ 
  time_t st_atime;  /*最后一次访问时间*/
  time_t st_mtime;  /*最后一次修改时间*/
  time_t st_ctime;  /*最后一次属性改变时间*/
}

二、Linux中文件编程

Linux中文件编程可用两种方法:Linux系统调用和C语言库函数。前者依赖于Linux系统,后招与操作系统是相互独立的,在任何操作系统下,使用C语言函数库函数操作文件的方法都是相同的。

1、系统调用

在进程里调用内核中用于实现各种功能的内核函数的通信方式就叫系统调用。所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。 

Linux系统调用部分是非常精简的系统调用(只有 250个左右) ,它继承了 UNIX系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket 控制、用户管理等几类。 

Linux下的每一个程序其真正的实现都是通过系统调用实现的。那么我们程序该如何使用系统调用呢?其实很简单,内核给我们提供了API,让我们可以向调用普通函数那样调用系统调用。唯一的区别就是系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态

1.1、文件描述符

在Linux环境下,内核如何区分和引用的特定文件呢?这里面就用到一个重要概念,文件描述符,对于Linux而言,所有的设备和文件的操作都是使用文件描述符来进行的,文件描述符是一个非负的整数,它是一个索引的值,并指向内核中每个进程打开文件的记录表,当打开一个现存或创建一个文件时,内核就返回一个文件描述符,当需要读写文件时,就需要把文件描述符作为参数传递给相应的函数。

一个进程启动时,默认打开了3个文件,标准输入、标准输出、标准错误,对应文件描述符是0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO),这些常量定义在unistd.h头文件中。

1.2、底层文件访问

在Linux应用开发中,有着著名的五大系统调用:这五大系统调用是学习Linux应用程序开发的根基。他们是open,close ,read,write 和ioctl!。这些函数的特点是不带缓存,直接对文件(包括设备)进行读写操作,这些不带缓存的I/O函数不是ANSI C的组成部分,但是是POSIX.1和XPG3的组成部分。

(1)open系统调用

#include <sys/types.h> //提供类型p_id的定义
#include <sys/stat.h> //提供文件属性的定义
#include <fcntl.h> //提供文件控制的相关定义

int open(const char *pathname,int flag)

int open(const char *pathname,int flag,mode_t mode)

函数参数

path :要打开的文件名(包含路径,缺省为当前路径,是一个字符串)
flag :打开方式,必须制定文件访问方式

O_RDONLY 只读方式打开
O_WRONLY 可写方式打开
O_RDWR 读写方式打开
O_CREAT 如果文件不存在,那么创建这个文件,并且用第三个参数为其设置权限
O_EXCL        如果文件使用O_CREAT时文件存在,则可返回错误消息,这一参数可用于测试文件是否存在
O_NOCTTY 如果文件为终端,那么终端不可以作为调用open系用调用的那个进程的控制终端
O_TRUNC 如果文件已存在,并且以只读或只写方式成功打开,那么会先删除文件中原有的数据
O_APPEND 以添加方式打开,在打开文件的同时,文件指针指向文件的末尾
前三个方式必须制定,可通过“|”组合与后面的可选模式组合

mode :用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限

S_IRUSR  文件所有者的读权限位
S_IWUSR 文件所有者的写权限位
S_IXUSR  文件所有者的执行权限位
S_IRWXU S_IRUSR | S_IWUSR | S_IXUSR
S_IRGRP 文件用户组的读权限位
S_IWGRP 文件用户组的写权限位
S_IXGRP  文件用户组的执行权限位
S_IRWXG S_IRGRP | S_IWGRP | S_IXGRP
S_IROTH  文件其他用户的读权限位
S_IWOTH 文件其他用户的写权限位
S_IXOTH  文件其他用户的执行权限位
S_IRWXO S_IROTH | S_IWOTH | S_IXOTH
也可用二进制的形式表示

返回值 :打开成功,返回文件描述符;打开失败,返回-1

(2)、close系统调用

为了重新利用文件描述符,用close()系统调用释放打开的文件描述符 

#include <unistd.h>

int close(int fd);
函数参数:
fd :要关闭的文件的文件描述符
返回值:如果出现错误,返回-1;调用成功返回0

(3)、read系统调用

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

参数:
fd :想要读的文件的文件描述符
buf : 指向内存块的指针,从文件中读取来的字节放到这个内存块中
count : 从该文件复制到buf中的字节个数
返回值:
如果出现错误,返回-1;读文件结束,返回0;否则返回从该文件复制到规定的缓冲区中的字节数

read()函数用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际读入的字节数。若返回 0,则表示没有数据可读,即已达到文件尾;返回-1,表示read调用出现错误。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次最多读一行。 

(4)、write系统调用

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

函数参数:
fd:要写入的文件的文件描述符
buf: 指向内存块的指针,从这个内存块中读取数据写入 到文件中
count: 要写入文件的字节个数
返回值:如果出现错误,返回-1;如果写入成功,则返回写入到文件中的字节个数

write()函数用于向打开的文件写数据,写操作从文件的当前指针位置开始。对磁盘文件进行写操作,若磁盘已满或超出该文件的长度,则 write()函数返回失败。 

(5)、ioctl系统调用

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
fd是某个设备的文件描述符。request是ioctl的命令,可变参数取决于request,通常是一个指向变量或结构体的指针。若出错则返回-1,若成功则返回其他值,返回值也是取决于request。

此函数在LCD背光应用程序中有使用ioctl(fd, turn)来给引脚制定参数。根据特定设备所支持操作的不通,他可能还有可选的第三个参数。

2、系统调用综合实例

文件拷贝程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h> 


#define BUFFER_SIZE 1024 


int main(int argc,char **argv) 


int from_fd,to_fd; 
int bytes_read,bytes_write; 
char buffer[BUFFER_SIZE]; 
char *ptr; 

if(argc!=3) 

fprintf(stderr,"Usage:%s fromfile tofile/n/a",argv[0]); 
exit(1); 


/* 打开源文件 */ 
if((from_fd=open(argv[1],O_RDONLY))==-1) //argv[1]表示要打开的文件

fprintf(stderr,"Open %s Error:%s/n",argv[1],strerror(errno)); 
exit(1); //无条件地停止剩下的所有操作,参数1表示出现错误进程非正常结束


/* 创建目的文件 */ 
if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1) 

fprintf(stderr,"Open %s Error:%s/n",argv[2],strerror(errno)); 
exit(1); 


/* 以下代码是一个经典的拷贝文件的代码 */ 
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE)) 

/* 一个致命的错误发生了 */ 
if((bytes_read==-1)&&(errno!=EINTR)) break; 
else if(bytes_read>0) 

ptr=buffer; 
while(bytes_write=write(to_fd,ptr,bytes_read)) 

/* 一个致命错误发生了 */ 
if((bytes_write==-1)&&(errno!=EINTR))break; 
/* 写完了所有读的字节 */ 
else if(bytes_write==bytes_read) break; //没有读完跳出本while程序,转而继续执行while循环后的语句,即继续上面的while循环内的判断
/* 只写了一部分,继续写 */ 
else if(bytes_write>0) 

ptr+=bytes_write; 
bytes_read-=bytes_write; 


/* 写的时候发生的致命错误 */ 
if(bytes_write==-1)break; 



close(from_fd); 
close(to_fd); 
exit(0); //无条件地停止剩下的所有操作,参数,0表示正常结束

编译测试:

clx@think:/work/armlinux/codetest$ gcc -o file_cp file_cp.c

clx@think:/work/armlinux/codetest$ ./file_cp backlight_test.c backlight_test1.c
clx@think:/work/armlinux/codetest$ ls
backlight_test  backlight_test1.c  backlight_test.c  file_cp  file_cp.c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章