------------->【Linux系统编程/网路编程】(学习目录汇总)
目录
虚拟地址空间是一个十分具象的概念,先按照字面意思进行解释:
虚拟地址空间的大小也由操作系统决定,32位的操作系统虚拟地址空间的大小为232字节,也就是4G查看linux是什么系统,64位的操作系统虚拟地址空间大小为264字节,这是一个特别大的数linux 文件描述符,感兴趣可以自己估算一下。当我们运行c盘上一个可执行程序,都会得到一个进程,内核会给每一个运行的进程创建一块属于自己的虚拟地址空间kali linux,并将应用程序数据装载到虚拟地址空间对应的地址上。
进程在运行过程中,程序内部所有的指令都是通过CPU处理完成的,CPU只进行数据运算并不具备数据储存的能力,其处理的数据都加载自化学显存,这么进程中的数据是怎样进出入到化学显存中的呢?虽然是通过CPU中的显存管理单元MMU(MemoryManagementUnit)从进程的虚拟地址空间中映射过去的。
1.1存在的意义
通过上面的介绍你们会觉得到一头雾水,为何操作系统不直接将数据加载到化学显存中而是将数据加载到虚拟地址空间中,在通过CPU的MMU映射到化学显存中呢?
先来看一下假如直接将数据加载到化学显存会发生哪些事情:
假定计算机的数学显存大小为1G,进程A须要100M显存因而直接在数学显存上从0地址开始分配100M,进程B启动须要250M显存,因而继续在数学显存上为其分配250M显存,但是进程A和进程B占用的显存是连续的。以后再启动其他进程继续依照这些方式进行化学显存的分配。。。
使用这些方法分配显存会有如下几个问题:
1.每位进程的地址不隔离,有安全风险。
因为程序都是直接访问数学显存,所以恶意程序可以通过显存轮询随便更改别的进程对应的显存数据,以达到破坏的目的。似乎有些时侯是非恶意的,而且有些存在bug的程序可能不留神更改了其它程序的显存数据,都会造成其它程序的运行出现异常。
2.显存效率低。
倘若直接使用化学显存的话,一个进程对应的显存块就是作为一个整体操作的,假若出现化学显存不够用的时侯,我们通常的办法是将不常用的进程拷贝到c盘的交换分区(虚拟显存)中,便于腾出显存,因而就须要将整个进程一起拷走,假如数据量大,在显存和c盘之间拷贝时间都会很长,效率低下。
3.进程中数据的地址不确定,每次就会发生变化。
因为化学显存的使用情况仍然在动态的变化,我们难以确定显存现今使用到那里了,若果直接将程序数据加载到化学显存,显存中每次储存数据的起始地址都是不一样的,这样数据的加载都须要使用相对地址,加载效率低(静态库是使用绝对地址加载的)。
有了虚拟地址空间以后就可以完美的解决上面提及的所有问题了,虚拟地址空间就是一个中间层,相当于在程序和数学显存之间设置了一个屏障,将两者隔离开来。程序中访问的显存地址不再是实际的化学显存地址,而是一个虚拟地址,之后由操作系统将这个虚拟地址映射到适当的数学内存地址上。这样,只要操作系统处理好虚拟地址到化学显存地址的映射,就可以保证不同的程序最终访问的显存地址坐落不同的区域,彼此没有重叠,就可以达到显存地址空间隔离的疗效。
1.2分区
从操作系统层级上看,虚拟地址空间主要分为两个部份内核区和用户区。
用户区:储存用户程序运行中用到的各类数据。
我们先来看一下进程对应的虚拟地址空间的各个分区,再来详尽介绍用户区的组成(以32位系统的虚拟地址空间为例)。
每位进程的虚拟地址空间都是从0地址开始的,我们在程序中复印的变量地址也其在虚拟地址空间中的地址,程序是难以直接访问数学显存的。虚拟地址空间中用户区地址范围是0~3G,里面分为多个区块:
显存映射区(mmap):作为显存映射区加载c盘文件,或则加载程序运作过程中须要调用的动态库。栈(stack):储存函数内部申明的非静态局部变量linux 文件描述符,函数参数,函数返回地址等信息,栈显存由编译器手动分配释放。栈和堆相反地址“向下生长”,分配的显存是连续的。命令行参数:储存进程执行的时侯传递给main()函数的参数,argc,argv[]环境变量:储存和进程相关的环境变量,例如:工作路径,进程所有者等信息2.文件描述符2.1文件描述符
在Linux操作系统中的一切都被具象成了文件,这么一个打开的文件是怎样与应用程序进行对应呢?解决方案是使用文件描述符(filedescriptor,简称fd),当在进程中打开一个现有文件或则创建一个新文件时,内核向该进程返回一个文件描述符,用于对应这个打开/新建的文件。这种文件描述符都储存在内核为每位进程维护的一个文件描述符表中。
在程序设计中,一些涉及底层的程序编撰常常会围绕着文件描述符展开。并且文件描述符这一概念常常只适用于UNIX、Linux这样的操作系统。
在Linux系统中一切皆文件,系统中一切都被具象成了文件。对那些文件的读写都须要通过文件描述符来完成。标准C库的文件IO函数使用的文件表针FILE*在Linux中也须要通过文件描述符的辅助就能完成读写操作。FILE虽然是一个结构体,其内部有一个成员就是文件描述符(下边结构体的第25行)。
FILE结构体在Linux头文件中的定义
// linux c FILE结构体定义: /usr/include/libio.h
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
};
// 在文件: /usr/include/stdio.h
typedef struct _IO_FILE FILE;
2.2文件描述符表
后面提到启动一个进程都会得到一个对应的虚拟地址空间,这个虚拟地址空间分为两大部份,在内核区有专门用于进程管理的模块。Linux的进程控制块PCB(processcontrolblock)本质是一个称作task_struct的结构体,里面包括管理进程所需的各类信息,其中有一个结构体称作file,我们将它称作文件描述符表,上面有一个整形索引表,用于储存文件描述符。
内核为每一个进程维护了一个文件描述符表,索引表中的值都是从0开始的,所以在不同的进程中你会看见相同的文件描述符,而且它们指向的不一定是同一个c盘文件。
知识小科普:
Linux中用户操作的每位终端都被视作一个设备文件,当前操作的终端文件可以使用/dev/tty表示。
给新打开的文件分配文件描述符
总结: