【www.bbyears.com--linux】
1.
对于文件操作,虽然都是通过函数调用的方式实现,却还是能分为两类:系统调用和库函数。
2.
在Linux中,几乎一切都可以看做是文件 。
这就意味着,普通程序完全可以像使用文件(普通定义)那样使用磁盘文件、串行口、打印机和其他设备
3.
库函数调用最终也是通过系统调用实现的。可认为库函数调用是对系统调出于效率考虑而做出的优化。
系统调用实际上就是指最底层的一个调用,在linux程序设计里面就是底层调用的意思。面向的是硬件。而库函数调用则面向的是应用开发的,相当于应用程序的api,采用这样的方式有很多种原因,第一:双缓冲技术的实现。第二,可移植性。第三,底层调用本身的一些性能方面的缺陷。第四:让api也可以有了级别和专门的工作面向。
4.
系统调用
系统调用提供的函数如open, close, read, write, ioctl等,需包含头文件unistd.h。以write为例:其函数原型为 size_t write(int fd, const void *buf, size_t nbytes),
(。Linux系统默认分配了3个文件描述符值:0-standard input,1-standard output,2-standard error。)
系统调用是操作系统相关的,因此一般没有跨操作系统的可移植性。
5.
库函数调用
标准C库函数提供的文件操作函数如fopen, fread, fwrite, fclose, fflush, fseek等,需包含头文件stdio.h。以fwrite为例,其函数原型为size_t fwrite(const void *buffer, size_t size, size_t item_num, FILE *pf),
(同样有相应的预定义的FILE指针:stdin-standard input,stdout-standard output,stderr-standard error。)
库函数调用是系统无关的,因此可移植性好。
6.
一个文件ID用于在系统中唯一的标识文件。文件描述符的总数也就是系统可以打开文件的最多个数,这取决于系统的配置情况。
当开始运行程序时,也就是系统开始运行时,它一般会有三个已经打开的文件描述符。他们是:
0:标准输入
1:标准输出
2:标准错误
其它文件的文件描述符,在调用文件打开函数open时返回。这就是说,每个设备对应着一个文件描述符。文件描述符由操作系统分配,每次分配最小的。
---------------------------------------------------------------
7.1
write,就是把缓冲区的数据写入文件中。注意,这里的文件时广泛意义的文件,比如写入磁盘、写入打印机等等。
Linux 中write()的函数原型:
size_t write(int fildes, const void *buf, size_t nbytes);
7.2
Linux中read的函数原型:
size_t read(int fildes, void *buf, size_t nbytes);
7.3
系统调用open的作用是打开一个文件,并返回这个文件的描述符。
简单地说,open建立了一条到文件或设备的访问路径。如果操作成功,它将返回一个文件描述符
这个文件描述符是唯一的,他不会和任何其他运行中的进程共享。如果两个程序同时打开一个文件,会得到两个不同的问价描述符。如果
同时对两个文件进行操作,他们各自操作,互补影响,彼此相互覆盖(后写入的覆盖先写入的)为了防止文件按读写冲突,可以使用文件锁的功能。
Linux中open的函数原型有两个:
int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode );
7.4
close系统调用用于“关闭”一个文件,close调用终止一个文件描述符fildes以其文件之间的关联。文件描述符被释放,并能够重新使用。
close成功返回1,出错返回-1.
#Include
int close(int fildes);
7.5
ioctl系统调用
ioctl提供了一个用于控制设备及其描述符行为和配置底层服务的接口。终端、文件描述符、甚至磁带机都可以又为他们定义的ioctl,具体
细节可以参考特定设备的使用手册。
下面是ioctl 的函数原型
#include
int ioctl(int fildes, int cmd,,,,,,);
ioctl对描述符fildes指定的对象执行cmd 参数中所给出的操作。
7.6
其他和文件管理有关的系统调用
还有许多其他的系统调用能对文件进行操作。
几个常用的如:lseek()对文件描述符fildes指定文件的读写指针进行设置,也就是说,它可以设置文件的下一个读写位置。
fstat,stat,lstat 是和文件描述符相关的函数操作,这里就不做介绍。
dup,dup2系统调用。dup提供了复制文件描述符的方法,使我们能够通过两个或者更多个不同的文件描述符来访问同一个文件。这可以用于
在文件的不同位置对数据进行读写。
---------------------------------------------------------------
8.
标准I/O库及其头文件
如果需要对设备的行为进行明确的控制,最好使用底层系统调用,因为这可以避免使用库函数带来的一些非预期的副作用,如输入/输出缓冲。
FILE *fopen(const char *filename, const char *mode);
size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);
size_t fwrite(const coid *ptr, size_t size , size_t nitimes, FILE *stream);
int fclose(FILE *stream);
fflush函数的作用是把文件流中所有未写出的数据全部写出。 处于效率考虑,在使用库函数的时候会使用数据缓冲区,当缓冲区满的时候才进行写操作。使用fflush函数
可以将缓冲区的数据全部写出,而不关心缓冲区是否满。fclose的执行隐含调用了fflush函数,所以不必再fclose执行之前调用fflush。
int fflush(FILE *stream);
linux学习之IO操作,文件IO总结
文件IO不带缓存,每个read和write都调用内核中的相应系统调用。
文件IO常用函数:
open,close,read,write,lseek
对于内核而言,所有打开文件都有文件描述符引用。
文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,你诶和向进程返回一个文件描述符。
当读、写一个文件时,用open返回的文件描述符标识该文件,将其作为参数传给read或write。
1.open(被打开的文件名(可包含文件路径),int flag, mode)
falg : O_RDONLY,O_WDONLY,O_RDWR,O_CREAT,O_EXCL(如果存在返回错误信息)
O_TRUNC(如果已存在,则删除文件中数据)
2.read(fd,buf,size_t count)
调用成功返回读取的字节数。
如果返回0,表示到达文件的末尾。
如果返回-1,表示出错,通过errno设置错误码。
3.write()
4.lseek(fd,offset,whence)
5.打开文件目录opendir()
6.获取文件属性函数:这组函数还蛮重要的。
stat()获取一个于此命名文件有关的信息结构
fstat()获得已在描述符filedes上打开的文件的有关信息
lstat()返回该符号链接的有关信息,而不是有该符号链接引用的文件信息
stat内结构体中参数:st_mode ,st_mode 是被打开文件的属性描述。
S_IFMT : 0x070000
switch(st.st_mode & S_IFMT) //判断是何种类型的文件
{
case S_IFREG: printf("-"); break;
case S_IFDIR: printf("d"); break;
case S_IFLNK: printf("l"); break;
case S_IFBLK: printf("b"); break;
case S_IFCHR: printf("c"); break;
case S_IFIFO: printf("p"); break;
case S_IFSOCK: printf("s"); break;
}
S_...是内部定义的一些宏,具体man一下看看
7.getopt函数查一下自己理解,getopt的作用是识别以“-”开头的字符。
while ((ch = getopt(argc, argv, "la")) != EOF)
{
switch ( ch )
{
case 'a' :
printf("option a is set\n");
aflag = 1;
break;
case 'l' :
printf("option l is set\n");
lflag = 1;
break;
default :
printf("wrong option %c is set\n", optopt);
}
}
以及getpwuid(st.st_uid);getgrgid(st.st_gid);函数,分别是获得当前用户名,用户组名
8.strtok()函数,是截断字符串的操作。以下的例子是利用空格来截断。
这个函数比较怪异,第一次赋值的时候必须是buf,而以后的第一个参数是NULL,这个比较恶心....
arg[i++] = strtok(buf, " ");
do
{
arg[i++] = strtok(NULL, " ");
}while(arg[i-1] != NULL);
9.关于文件夹操作的函数: DIR *opendir();struct dirent * readdir():内部有指针,可以自动移动文件夹中下一个文件,直到为空)
例子:利用他们来完成文件的拷贝
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fd1, fd2;
char buf[1024];
int nbyte;
if(argc != 3)
{
printf("Using : %s srcfilename decfilename\n", argv[0]);
return -1;
}
if((fd1 = open(argv[1], O_RDONLY)) < 0)
{
perror("open");
return -1;
}
if((fd2 = open(argv[2], O_WRONLY| O_CREAT | O_TRUNC, 0666)) < 0)
{
perror("open");
return -1;
}
while((nbyte = read(fd1, buf, sizeof(buf))) > 0)
{
write(fd2, buf, nbyte);
}
close(fd1);
close(fd2);
return 0;
}
打开文件目录opendir()
#include
#include
#include
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *dirent;
dir = opendir(argv[1]);
while((dirent = readdir(dir)) != NULL)
{
printf("%s\n", dirent->d_name);
}
}
获取文件属性函数:
stat()获取一个于此命名文件有关的信息结构
fstat()获得已在描述符filedes上打开的文件的有关信息
lstat()返回该符号链接的有关信息,而不是有该符号链接引用的文件信息
利用stat实现ls的基本功能:
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
struct stat st;
int i;
struct passwd *pw;
struct group *gr;
struct tm *tm;
stat(argv[1], &st);
switch(st.st_mode & S_IFMT) //判断是何种类型的文件
{
case S_IFREG: printf("-"); break;
case S_IFDIR: printf("d"); break;
case S_IFLNK: printf("l"); break;
case S_IFBLK: printf("b"); break;
case S_IFCHR: printf("c"); break;
case S_IFIFO: printf("p"); break;
case S_IFSOCK: printf("s"); break;
}
for(i = 8; i >= 0; i--)
{
if(st.st_mode & (1 << i)) //st.st_mode后8位,判断文件权限
{
switch(i%3)
{
case 2: printf("r"); break;
case 1: printf("w"); break;
case 0: printf("x"); break;
}
}
else
printf("-");
}
pw = getpwuid(st.st_uid);
gr = getgrgid(st.st_gid);
printf("%2d %s %s %4ld", st.st_nlink, pw->pw_name, gr->gr_name, st.st_size);
tm = localtime(&st.st_ctime);
printf(" %04d-%02d-%02d %02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
printf(" %s\n", argv[1]);
return 0;
}