Linux----基础IO

系统文件I/O

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问:

int open(const char* pathname, int flags, mode_ _t mode)
pathname:待打开文件的路径+文件名称
flags:以何种方式打开
mode:对于新创建出来的文件,设置文件权限若文件不存在, 给新创建的文件设置权限返回值:返回文件描述符操作文件 句柄

ssize_t write(int fd, const void* buf, size_ _t count)
fd :文件描述符,在哪里进行写
buf:写入的数据
count:写入数据的大小

ssize_ t read(int fd, void* buf, size_ _t count)
fd:文件描述符,从哪里进行读
buf:读到哪里去
count:最大可以读多少字节,注意:需要预留\0的位置

off_ _t Iseek(int fd, off _t offset, int whence)fd:需要操作的文件描述符
offset:偏移量
whence:偏移到哪里去

close(int fd);


#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main()
{
    //flags:
    //  以下三个选项当中是必选项,且三者当中只能选择一个
    //  O_RDONLY:以只读方式打开
    //  O_WRONLY:以只写方式打开
    //  O_RDWR : 可读可写方式打开
    
    //以下为可选项,可以选择多个
    //  O_CREAT:如果不存在则创建文件
    //  O_TRUNC:截断文件(清空文件)
    //  O_APPEND:已追加方式打开文件
    int fd = open("./tmp_file", O_RDWR | O_CREAT, 0664);
    if(fd < 0)
    {
        perror("open");
        return 0;
    }
    printf("fd= %d\n", fd);
    
    int writesize = write(fd, "linux!!!", 8);
    if(writesize < 0)
    {
        perror("write");
        return 0;
    }
    printf("writesize = %d\n", writesize);

    lseek(fd, 0, SEEK_SET);

    char buf[1024];
    memset(buf, '\0', sizeof(buf));
    int readsize = read(fd, buf, sizeof(buf) - 1);
    if(readsize < 0)
    {
        perror("readsize");
        return 0;
    }
    printf("readsize = %d\n", readsize);
    printf("read buf is %s\n", buf);

    close(fd);
    return 0;
}

文件描述符fd

通过对open函数的学习,我们知道了文件描述符就是一个小整数

在这里插入图片描述

在 PCB中有一个 struct files_struct* file指针,
该指针指向一个struct files_struct结构体,
该结构体包含一个 struct file* fd_arrayp[NR_OPEN_DEFAULT] 指针数组,
该指针数组则存放则结构体指针 struct file* 指向对应的文件(struct file)

通过分析我们得知:
文件描述符的实质就是数组下标

文件描述符遵循 最小未占中,即每次分配最小的未被占用的数组下标

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	close(0);
	//close(2);
	int fd = open("myfile", O_RDONLY);
	if(fd < 0){
		perror("open");
		return 1;
	}
	printf("fd: %d\n", fd);
	close(fd);
	return 0;
}

发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

创建一个新进程的时候,默认打开三个文件描述符,分别为
0 (标准输入),1 (标准输出),2 (标准错误)文件描述符其实是内核当中维护的数组fd.
array的下标,下标从0开始,所以文件描述符是一个正整数 ;

重定向

#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。这种现象叫做输出重定向
在这里插入图片描述
使用 dup2 系统调用
函数原型如下

#include <unistd.h>
int dup2(int oldfd, int newfd);

重定向的本质:

将newfd文件描述符拷贝oldfd文件描述符
int dup2(int oldfd, int newfdj]oldfd和newfd都是文件描述符

1.newfd(标准输出)拷贝oldfd(tmpfile的文件描述符)
2.当拷贝成功,则关闭newfd(标准输出)

如果拷贝的oldfd是一个无效的文件描述符,则dup2什么事情都没有干,切记,没有关闭newfd

如果拷贝的newfd和oldfd具有相同的值,则dup2什么事情都不干, 没有关闭newfd

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
	int fd = open("./log", O_CREAT | O_RDWR);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	close(1);
	dup2(fd, 1);
	for (;;) {
		char buf[1024] = {0};
		ssize_t read_size = read(0, buf, sizeof(buf) - 1);
		if (read_size < 0) {
			perror("read");
			break;
		}
	printf("%s", buf);
	fflush(stdout);
	}
	return 0;
}

printf是C库当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1下标所表示内容,已经变成了myfile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写
入,进而完成输出重定向

FILE

因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
所以C库当中的FILE结构体内部,必定封装了fd

struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //封装的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

动态库和静态库

静态库

1.win环境下的静态库的后缀是.lib ,在win环境下编译一个依赖库 文件的库执行程序,编译阶段依赖静态库( .lib ) ,程序运行阶段,依赖是动态库( .dlI )

2.静态库会将编译的代码当中所有的函数全部编译到静态库当中去;静态链接指的是gcc或者g+ +的命令行参数;

静态库是一个库文件;

3.-static :如果程序是静态链接生产的可执行程序,会将静态库当中的所有代码全部编译到可执行程序当中去,当.

程序执行的时候,是不需要依赖静态库的了;

4.linux环境下,后缀为.a,前缀lib; eg:libctemplate.a ,名称是去掉前缀和后缀剩下的就是库文件名称

5.静态库的生成与使用

静态库的生成:需要注意的是,生成静态库的时候,是使用.o文件来进行编译生成的

ar -rc lib[库文件名称].a [xxx].o [xxx].o

6.静态库的使用

-L [path]指定链接的库文件路径; -|库文件名称(去了前缀和后缀的名称)] :指定链接库文件名称eg : gcc main.c -o main -L [path] -|[库文件名称]

[huang@localhost libstatic]$ ls
main.c print.c print.h

[huang@localhost libstatic]$ gcc -c print.c -o print.o
[huang@localhost libstatic]$ ls
main.c print.c print.h print.o
[huang@localhost libstatic]$ ar -rc libprint.a print.o // 必须是 .o文件
[huang@localhost libstatic]$ ls
libprint.a main.c print.c print.h print.o
[huang@localhost libstatic]$ gcc main.c -o main -L . -lprint // .当前路径
[huang@localhost libstatic]$ ls
libprint.a main main.c print.c print.h print.o // 使用静态库生成main

动态库

1.win环境下动态库的后缀是.dIl

2.linux环境下动态库的后缀是.so前缀是Iib3.动态库的生成与使用

动态库的生成

-shared:生成共享库的命令行参数-fPIC :产生位置无关的代码

动态库的使用

-L [path]指定链接的库文件路径; -|[库文件名称 (去了前缀和后缀的名称)] :指定链接库文件名称

[huang@localhost libstatic]$ ls
main.c print.c print.h
[huang@localhost libstatic]$ gcc -shared -fPIC print.c -o libprint.so
[huang@localhost libstatic]$ ls
libprint.so main.c print.c print.h
[huang@localhost libstatic]$ gcc main.c -o main -L . -lprint
[huang@localhost libstatic]$ ls
libprint.so main main.c print.c print.h

问题:如何让程序可以找到依赖的动态库

1.将动态库放到当前可执行程序的目录下

2.在环境变量当中设置动态库的搜索路径,设置环境变量LD_ LIBRARY_ PATH

3.不推荐:直接将自己写的库或者第三方库文件和操作系统库文件放到一-起/usr/lib64

理解文件系统

Linux ext2文件系统,为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设
定block大小为1024、2048或4096字节。
Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相
同的结构组成。政府管理各区的例子
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,
未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的
时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个
文件系统结构就被破坏了
GDT,Group Descriptor Table:块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
数据区:存放文件内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值