Linux 系统编程Day01
文章目录
1.文件编程概述
为什么会用到文件操作?
- 账单
- 游戏进度读取
- 配置文件等
以上操作在实际的应用中都会用得到
由此我们引出了如何实现文件的创建、打开编辑等的自动化执行操作
Windows手动编辑文件(以world为例):
1.打开/创建文档
2.编辑文档
3.保存文档
4.关闭文档
Linux手动同样类似
计算机如何帮我们自动化完成上述操作?
操作系统提供了一系列的API(以Linux系统为例):
open打开
read/write读写
lseek光标定位
close关闭
1.1 文件的打开及创建
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(cost char *pathname, int flags);
int open(cost char *pathname, int flags,mode_t mode);
int creat(const cahr *pathname, ,ode_t mode)
1.1.1 参数说明
pathname:要打开的文件名(含路径,缺省为当前路径)
flags:O_RDONLY(只读打开)、O_WRONLY(只写打开)、O_RDWR(可读可写打开)
权限选定以后我们打开的文件只能以选定的权限进行操作;边三个常数只能选择一个。
下面是可选的常数:
O_CREAT:若文件不存在则创建它,同时使用时常数的第三个参数mode也需要被说明,用来指示被创建文件的权限。
O_EXCL:在使用时如果同时使用了O_CREAT ,而文件已经存在,则会 报0错。
O_APPEND:每次写时都加到文件的末端。
O_TRUNC:使用该属性时,若文件有内容且权限为只读或只写成功打开,则将文件长度截断为0.
- Mode一定是在使用了O_CREAT时,用mode记录待创建文件权限。
Creat创建文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "you are very preety!";
fd = creat("./lianxi2",O_RDWR);
return 0;
}
1.1.2 文件写入操作示例
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("./20230619",O_RDWR);
printf("fd =%d",fd);
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("./20230619",O_RDWR);
if(fd == -1){
printf("file create faild\n");
fd = open("./file1",O_RDWR|O_CREAT,06000);
if(fd > 0){
printf("file create succeed!\n");
}
}
printf("fd =%d\n",fd);
return 0;
}
1.1.3 文件的权限
- 可读:r->4
- 可写:w->2
- 执行:x->1
0600:可读可写
0:
6:4+2
0:同组用户
0:其他组
用以给文件所有者权限
2.文件操作
Create函数:
int create (const *filename, mode_mode)
flie:要创建的文件名(包含路径、缺省为当前路径)
mode:创建模式 ///可读可写可执行
常见创建模式:、
- S_IRSUR: 1 可读
- S_IWUSR:2 可写
- S_IXUSR: 1 可执行
- S_IRWXU:7 可读、可写、可执行
2.1 写入文件
write:
#include <unistd.h>
write(int fd,const void *buf, size_t count)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd;
char *buf = "you are very pretty~";
fd = open("./lianxi",O_RDWR);//打开lainxi文件,若不存在就返回负数
if(fd == -1){
printf("open file faild \n");//不存在就创建文件
fd=open("./lianxi",O_RDWR|O_CREAT,0600);
if(fd>0){
printf("creat success!\n");
}
}
printf("fd = %d\n",fd);
//write(fd,buf,sizeof(buf));
write(fd,buf,strlen(buf));//写入操作
close(fd);
return 0;
}
2.2 文件读取操作
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "you are very preety!";
fd = open("./lianxi",O_RDWR);
if(fd == -1){
printf("open file faild \n");
fd=open("./lianxi",O_RDWR|O_CREAT,0600);
if(fd>0){
printf("creat success!\n");
}
}
printf("fd = %d\n",fd);
//write(fd,buf,strlen(buf));
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to \n",n_write);
}
close(fd);//由于文件的光标此时在最后所以,读取到的结果也是0,由此我们用关闭文件重新打开的方式读取文件
fd =open("./lianxi",O_RDWR);
char *redBuf;
redBuf =(char *)malloc( sizeof(char)*n_write + 1 );
int n_read = read(fd,redBuf,n_write);
printf("read %d, context:%s\n",n_read,redBuf);
close(fd);
return 0;
}
2.3 文件的光标移动操作
在上边的例子中我们遇到了写入数据后进行读取操作时,读取不到文件的问题。我们采取的方式是关闭文件后再重新打开的方式;
由上我们引入了lseek函数
- off_t lseek(int fd, off_t offset,int whence)
- fd:文件描述符
- offset:偏移值
- whence:固定点的位置
- seek_SET:指向文件头
- seek_END:指向文件尾部
- seek_CUR:指向当前光标位置
改进例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "you are very preety!";
fd = open("./lianxi",O_RDWR);
if(fd == -1){
printf("open file faild \n");
fd=open("./lianxi",O_RDWR|O_CREAT,0600);
if(fd>0){
printf("creat success!\n");
}
}
printf("fd = %d\n",fd);
//write(fd,buf,strlen(buf));
int n_write = write(fd,buf,strlen(buf));
if(n_write != -1){
printf("write %d byte to \n",n_write);
}
//close(fd);//由于文件的光标此时在最后所以,读取到的结果也是0,由此我们用关闭文件重新打开的方式读取文件
fd =open("./lianxi",O_RDWR);
char *redBuf;
redBuf =(char *)malloc( sizeof(char)*n_write + 1 );
//读取之前重新定位光标
lseek(fd,0,SEEK_SET);
int n_read = read(fd,redBuf,n_write);
printf("read %d, context:%s\n",n_read,redBuf);
close(fd);
return 0;
}
注:关于偏移值,负数向前正数向后。
计算文件大小:这里我们需要知道的是,lseek成功执行后的的返回值是偏移值
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char *buf = "you are very preety!";
fd =open("./lianxi",O_RDWR);
int file_size = lseek(fd,0,SEEK_END);
printf("file's size is :%d\n",file_size);
close(fd);
return 0;
}
3.文件操作原理简述
3.1文件描述符
-
对于内核而言,所有打开文件都由文件描述符引用
-
文件描述符是一个非负整数
当打开一个文件或创建一个新文件时,内核向进程返回一个文件描述符,当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write.
按照惯例:
- UNIX shell使用文件描述符0与进程的标准输入相结合;
- 文件描述符1与标准输出相结合;
- 文件描述符2与标准错误输出相结合。
STDIN_FILRNO、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0、1、2这几个数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd;
char readBuf[128];
int n_read = read(0,readBuf,5);
int n_write = write(1,readBuf,strlen(readBuf));
return 0;
}
文件描述符这个数字在一个进程之中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用一个数字作为这个文件的带表(文件描述符)这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件只需要用这个文件描述符区分。
-
文件描述符只在当前进程有效
-
open函数打开文件打开成功返回一个文件描述符,打开错误返回-1
3.2 Linux文件操作流程
1、在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。
2、强调一点:我们对文件进行操作时,一定要先打开文件,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,一定要关闭文件,否则会造成文件损坏。
3、文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。
4、打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的睁态文件。
5、为什么这么设计,不直接对块设备直接操作。
块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。