这部分内容学了好长时间了,几乎都要忘光了,所以拿出来复习一下,顺便做下笔记,而且要每天都要看一遍,不能再丢下了,如果总是这样就总在不断地学习这一个知识点,争取做到,学一个就会一个,不能学一个就扔一个,这样每次都能只是0-60分之间,永远能提升不了。闲话少说,开始写。
linux下一般都会有如下几个目录:
/bin 用于存放普通户用执行的命令,任何用户都可以执行该目录的命令,如ls、cd等。
/boot 存放linux内核及启动系统是时所需文件,为保证启动文件更加可靠,通常把该目录放在独立分区上。
/dev 设备文件的存目录。
/etc 用于存放系统的配置文件,如用户账号和密码存放在/etc/password 和 /etc/shadow中。
/home 普通用户主目录,每个用户在该目录下都有一个与用户名同名的目录。
/lib 用于存放各种库文件
/proc 是一个虚拟文件系统,只有系统运行是才存在,可以通过访问该目录中的文件。获取系统状态的状态信息,并修改某些系统配置信息。
/root 超级用户的主目录。
/sbin 存放用于管理系统的命令
/tmp 临时文件目录。
/usr 用于存放系统应用程序及相关文档,如说明文档,帮助文件等。
/var 用于存放系统中经常变化的文件,如日志文件和邮件等。
在linux下对物理磁盘的访问,都是通过设备驱动程序进行的,对设备驱动程序的访问有两种方式,一:通过设备本身提供的接口,这种方式会绕过文件系统直接读写磁盘上的内容。一般不建议使用这种方法。二:通过虚拟文件系统(VFS):不存在的,只存在内存当中,将不同的文件系统整合在一起,提供统一的编程接口(API)以供使用。
linux下文件的分类:
普通文件:最常见,包含了某种形式的数据。
目录文件:就是目录,有相关属性,其内容就是目录下的文件和目录。
字符特殊文件:用于表示系统中字符类型设备,如鼠标、键盘等。
块特殊文件:用于表示系统中块类型的设备,如硬盘、光驱等,对这些设备上的数据访问都通常以块的方式进行,一次最少读一个块。
FIFO:这种文件用于进程间通信。也称命名管道。
套接字(SOCKET):用于网络通信,也可用于进程通信。
符号链接文件:只想另外一个文件,是另一个文件的引用。
在介绍相关函数操作函数之前,必须介绍一下系统调用和库函数的区别:
系统调用:creat open close read write lseek
c语言标准库函数:fopen fclose fread fwrite fseek
c语言标准库函数都是调用系统调用来实现的,如果要书写跨平台的代码,尽量使用库函数,毕竟文件描述符是linux特有的东西。在0-NR_open之间,NR_open的值为255,
文件描述符0,表示标准输入:一般指键盘,文件描述符1,表示标准输出:一般指显示器,文件描述符3表示标准出错:一般也指显示器,打开一个文件这三个都是默认打开的。
下面开始介绍文件操作的函数:用man page都可以查看原型,但是如果这个函数即使一个命令也是一个函数的话就需要看man page的第一页,如:man 2 chmod
1、chmod/fchmod——对文件的访问权限进行修改()
函数原型:
int chmod (const char * path, mode _t mode);
int fchmod(int file, mode_t mode);
这两个函数都是改变文件的修改权限,区别是chmod以文件名作为第一个数的参数。而fchmod是以文件描述符作为第一个参数的。第二个参数就是需要修改的权限。
成功返回0,失败返回-1,并设置相应的errno。
2、open close read write lseek
open:打开一个文件,返回一个文件的文件描述符
int open(const char * pathname, int flag);
int open(const char * pathname, int flag, mode_t mode);
第一个参数是打开文件的带路径的文件名,可以使相对路径也可以使绝对路径。第二个参数打开的方式:
O_RDONLY只读打开 O_WRITE只写方式打开 O_RDWR以可读可写的方式打开。这三种的互斥的,打开一个文件三种方式必选其一,而且只能选一个。
还有其他的方式, 下面的这些方式可以和上面的方式做或操作:
O_CREAT若文件不存在则创建,只有当用到此选项时,参会用到第三个参数,表示创建文件的访问权限。
O_APPEND已追加的方式打开文件,填入的信息都会添加到文件末尾
O_TRUNC:若文件已存在,则截断。
O_SYNC:一同步的方式打开一个文件,所有的修改都会被阻塞,直到物理磁盘的数据同步以后才返回。
close:关闭文件
int close (int fd);
成功返回0, 失败返回-1;
read:从打开的文件中读数据
ssize_t read(int fd, void * buf, size_t count);
从fd所指向的文件中读取 count个字节,放在buf所指向的地址中。
成功返回实际读取的字节数,失败返回-1.设置相应的errno
write:从文件中读取数据
ssize_t write(int fd, void * buf, size_t count);
将buf所致单元中的 count个字节的数据写入fd所指向的文件中。
函数调用成功返回实际写入的字节数,失败返回-1,设置相应的errno。
read和write操作,都会引起文件读写指针的移动。
lseek:文件读写指针的移动
off_t lseek(int files, off_t offset, int when);
每个打开的文件都会有一个读写位置,lseek用来控制文件的读写位置。第一个参数:打开的文件描述符,第二个参数是表示:移动多少个字节,第三个参数表示:从哪里开始移动
SEEK_SET:从文件开头开始移动;
SEEK_CUR:从当前位置开始移动;
SEEK_END:从文件末尾开始移动;
值得注意的是,lseek可以将指针移动到EOF之后,如果不进行写操作,文件的大小事不会改变的。
lseek的常用法:
lseek(int file , 0, SEEK_STE);将读写文件移动到开头。
lseek(int file , 0, SEEK_CUR);获取文件指针的当前值。
lseek(int file , 0, SEEK_END);将文件指针移动到文件末尾。
下面上一个例程,是对上面的函数的应用,只有会用了才是真正的学到了。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int my_err(const char * err_string, int line)
{
fprintf(stderr, "line:%d", line);
perror(err_string);
exit(1);
}
int my_read(int fd)
{
int len, ret, i;
char read_buff[64];
if(lseek(fd, 0, SEEK_END) == -1){
my_err("lseek", __LINE__);
}
if((len = lseek(fd, 0, SEEK_CUR)) == -1){
my_err("lseek", __LINE__);
}
if(lseek(fd, 0, SEEK_SET) == -1){
my_err("lseek", __LINE__);
}
printf("len = %d\n", len);
if((ret = read(fd, read_buff, len)) < 0){
my_err("read", __LINE__);
}
for(i = 0; i< len; i++){
printf("%c", read_buff[i]);
}
printf("\n");
return ret;
}
int main(void)
{
int fd;
char write_buf[32] = "Hello world!";
// if((fd = creat("test.c", S_IRWXU)) == -1){
if((fd = open("test.c",O_RDWR)) == -1){
my_err("open", __LINE__);
}
else{
printf("the file %d open success\n", fd);
}
if(write(fd, write_buf, strlen(write_buf)) != strlen(write_buf)){
my_err("write", __LINE__);
}
my_read(fd);
printf("/*******************************************************/\n");
if(lseek(fd, 10, SEEK_END) == -1){
my_err("lseek", __LINE__);
}
if(write(fd, write_buf, strlen(write_buf)) != strlen(write_buf)){
my_err("write", __LINE__);
}
my_read(fd);
close(fd);
return 0;
}
__LINE__是预编译器内置的宏,表示行数,类似的宏还有__TIME__,__FUNCTION__,__FILE__分别表示时间,函数名,和文件名,利用这些信息可以帮助我们调试代码。
在终端输入od -c test.c就会发现,当文件指针移动到EOF之后和在写入的数据之间使用\0填充的。如果不进行写数据,文件的大小是不会改变的。