Linux基础 IO
1.C文件IO操作
1.1 hello.c写文件
#include<stdio.h>
#include<string.h>
int main()
{
FILE* fp = fopen("myfile", "w");
if (fp == NULL)
{
printf("fopen error\n");
}
const char* msg = "hello LJH\n";
int count = 5;
while (count--)
{
fwrite(msg, strlen(msg), 1, fp);
}
fclose(fp);
return 0;
}
1.2 hello.c读文件
#include<stdio.h>
#include<string.h>
int main()
{
FILE* fp = fopen("myfile", "r");
if (!fp)
{
printf("fopen error!\n");
}
char buf[1024];
const char* msg = "hello LJH!\n";
while (1)
{
size_t s = fread(buf, 1, strlen(msg), fp);
if (s > 0)
{
buf[s] = 0;
printf("%s", buf);
}
if (feof(fp))
{
break;
}
}
fclose(fp);
return 0;
}
1.3 stdin&stdout&stderr
C默认会打开三个输入输出流:stdin,stdout,stderr
且三个流的类型都是FILE*
2.系统文件I/O
系统接口来访问文件
2.1 hello.c写文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
umask(0);
int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
if (fd < 0)
{
perror("open");
return 1;
}
int count = 5;
const char *msg = "hello LJH!\n";
int len = strlen(msg);
while (count--)
{
write(fd, msg, len);
}
close(fd);
return 0;
}
2.2 hello.c读文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("myfile", O_RDONLY);
if (fd < 0)
{
perror("open");
return 1;
}
const char *msg = "hello LJH\n";
char buf[1024];
while (1)
{
ssize_t s = read(fd, buf, strlen(msg));
if (s > 0)
{
printf("%s", buf);
}
else
{
break;
}
}
close(fd);
return 0;
}
2.3 open函数介绍
//所需要的头文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
//函数参数介绍
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
//pathname: 要打开或创建的目标文件
//flags标记位: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
//flags标记位参数:
//O_RDONLY: 只读打开
//O_WRONLY: 只写打开
//O_RDWR : 读,写打开
//这三个常量,必须指定一个且只能指定一个
//O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
//O_APPEND: 追加写
//返回值:
//成功:新打开的文件描述符
//失败:-1
2.4 文件描述符 fd
fd其实是文件描述符表的数组下标
Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
2.4.1 文件描述符的分配规则
在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
2.4.2 重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
本应该通过文件描述符输出到显示器上的内容,输出到文件myfile当中,fd=1,这就是文件重定向.
2.4.3 dup2系统调用
#include <unistd.h>
int dup2(int oldfd, int newfd);
测试代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("myfile", O_RDONLY);
if (fd < 0)
{
perror("open");
return 1;
}
dup2(fd, 0);
close(fd);
char buf[1024];
while (1)
{
if(fgets(buf,sizeof(buf),stdin)==NULL)
{
break;
}
printf("Read:%s",buf);
sleep(1);
}
return 0;
}
2.4.4 C文件结构体FILE
IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问
所以C库当中的FILE结构体内部,必定封装了fd。
2.4.5 C文件缓冲区
这里解释下语言层面的缓冲区
先看代码
#include <stdio.h>
#include <string.h>
int main()
{
const char *msg0="hello printf\n";
const char *msg1="hello fwrite\n";
const char *msg2="hello write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
这里进行./hello>file后,打印结果如下
hello write
hello printf
hello fwrite
hello printf
hello fwrite
原因是:
1.C库函数写入文件时是全缓冲,而写入显示器是行缓冲
2.当文件重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲
3.然而放在缓冲区的数据就不会被立即刷新,fork之后也是如此
4.但进程退出后,会统一刷新,写入文件中
5.但是fork之后,父子数据发生写时拷贝,父进程准备刷新的时候,子进程也就有了同样的一份数据,也就是两份数据
6.write系统调用,就没有所谓的缓冲区
3. 文件系统
权限 硬链接数 文件所有者 文件所属组 文件大小 文件最后修改时间 文件名
除了ls -l 查看之外,stat+文件名,能查看更多信息
从硬件开始了解文件系统
磁盘存储的逻辑抽象结构
将磁盘盘片想象成为线性空间
以上是按照扇区进行存取,也可以基于文件系统,按照文件块为单位进行数据存取,也就是LBA
一个磁盘都会进行分区管理,一般的磁盘500g内存,进行分区管理
然而管理磁盘内存是可以进行迁移的,要管好100g,也就只需要管好2g
保存文件属性是通过inode保存
inode节点表:存放文件属性:文件大小,所有者,最近修改时间等
数据区:存放文件内容
inode位图:每个bit表示一个inode是否空闲可用
块位图:记录data block中那个数据块已经被占用,那个数据块没有被占用
超级块:存放文件系统本身的结构信息,如果被破坏,整个文件系统瘫痪
3.1文件的软硬链接
操作系统在磁盘中找文件不是通过文件名,而是通过inode,Linux中可以让多个文件名对应同一个inode,也就是不同分区可能会有相同的inode
删除文件其实是由两步组成:
1.在目录中将文件和inode的映射关系删除
2.将文件的硬链接数减1,如果减到了0,则将对应的磁盘文件释放掉。
软链接是通过名字引用另一个文件,可以理解为windows中桌面快捷方式的创建,是一个独立的文件,而硬链接不是一个独立的文件,而是在指定目录内部的一组映射关系:文件名<—>inode的映射关系
3.1.1 软链接
在Linux中,创建软链接(符号链接)可以使用 ln 命令,配合 -s 参数。软链接是指向另一个文件或目录的引用。
ln -s [目标文件或目录] [链接名称]
软链接可以跨文件系统,而硬链接不能。
如果目标文件被删除,软链接将变为“悬挂”状态,仍然存在但指向无效。
软链接的名称可以是相对路径或绝对路径。
3.1.2 硬链接
在Linux中,创建硬链接可以使用 ln 命令,不需要任何额外的参数。硬链接是指向同一物理文件的不同目录项。
ln [目标文件] [链接名称]
硬链接不能跨文件系统。
硬链接与原文件共享相同的 inode,因此它们是同一个文件的不同表示。
删除原文件并不会影响硬链接,因为它们仍然指向相同的数据。
目录通常不允许创建硬链接,除非使用特权权限。
4. 动静态库
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。
静态库的本质是将库中的源代码直接翻译成为.o目标的二进制文件,然后打包在一起
静态库生成makefile文件参考
static-lib=libmymath.a
$(static-lib):Add.o Div.o Mul.o Sub.o
ar -rc $@ $^
%.o:%.c
gcc -c $<
#Test:Add.o Div.o Mul.o Sub.o TestMain.o
# gcc -o $@ $^
.PHONY:output
output:
mkdir -p mymath_lib/include
mkdir -p mymath_lib/lib
cp -f *.h mymath_lib/include
cp -f *.a mymath_lib/lib
.PHONY:clean
clean:
rm -rf *.o *.a mymath_lib
动态库生成Makefile文件参考
dy-lib=libmymath.so
$(dy-lib):Add.o Div.o Mul.o Sub.o
gcc -shared -o $@ $^
%.o:%.c
gcc -fPIC -c $<
.PHONY:output
output:
mkdir -p mymath_lib/include
mkdir -p mymath_lib/lib
cp -f *.h mymath_lib/include
cp -f *.so mymath_lib/lib
.PHONY:clean
clean:
rm -rf *.o *.so mymath_lib
gcc默认是动态链接的,但个别库,如果你只提供.a,gcc也是没有办法,只能局部性的把你指定的.a,进行静态链接,其他库正常动态链接,如果加上-static,就必须要.a
运行动态库的方法:
1.直接安装到系统中
2.通过软连接,查找动态库
3.LD_LIBRARY_PATH,使用环境变量的方式,让系统找到自己的动态库
4.直接更改系统关于动态库的配置文件
动态库加载的原理
gcc -fPIC -c XXX.c