首先,抽象地来说 , 我们的计算机就是这个东西:
这个大的矩形表示计算机的内存空间,其中的小矩形代表进程,左下角的圆形表示磁盘,右下角的图形表示一些输入输出设备 , 比如鼠标键盘显示器等等 。另外,注意到内存空间被划分为了两块,上半部分表示用户空间 , 下半部分表示内核空间。
用户空间装着用户进程需要使用的资源,比如你在程序代码里开一个数组,这个数组肯定存在用户空间;内核空间存放内核进程需要加载的系统资源,这一些资源一般是不允许用户访问的 。但是注意有的用户进程会共享一些内核空间的资源,比如一些动态链接库等等 。
我们用 C 语言写一个 hello 程序,编译后得到一个可执行文件,在命令行运行就可以打印出一句 hello world,然后程序退出 。在操作系统层面 , 就是新建了一个进程,这个进程将我们编译出来的可执行文件读入内存空间,然后执行,最后退出 。
你编译好的那个可执行程序只是一个文件 , 不是进程 , 可执行文件必须要载入内存,包装成一个进程才能真正跑起来 。进程是要依靠操作系统创建的,每个进程都有它的固有属性,比如进程号(PID)、进程状态、打开的文件等等,进程创建好之后 , 读入你的程序,你的程序才被系统执行 。
那么,操作系统是如何创建进程的呢?对于操作系统,进程就是一个数据结构,我们直接来看 Linux 的源码:
task_struct 就是 Linux 内核对于一个进程的描述,也可以称为「进程描述符」 。源码比较复杂,我这里就截取了一小部分比较常见的 。
我们主要聊聊 mm 指针和 files 指针 。mm 指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方; files 指针指向一个数组,这个数组里装着所有该进程打开的文件的指针 。
先说 files,它是一个文件指针数组 。一般来说,一个进程会从 files[0] 读取输入,将输出写入 files[1] ,将错误信息写入 files[2]。
举个例子,以我们的角度 C 语言的 printf 函数是向命令行打印字符 , 但是从进程的角度来看,就是向 files[1] 写入数据;同理 , scanf 函数就是进程试图从 files[0] 这个文件中读取数据 。
每个进程被创建时, files 的前三位被填入默认值,分别指向标准输入流、标准输出流、标准错误流 。我们常说的「文件描述符」就是指这个文件指针数组的索引,所以程序的文件描述符默认情况下 0 是输入,1 是输出,2 是错误 。
我们可以重新画一幅图:
对于一般的计算机,输入流是键盘,输出流是显示器,错误流也是显示器,所以现在这个进程和内核连了三根线 。因为硬件都是由内核管理的,我们的进程需要通过「系统调用」让内核进程访问硬件资源 。
PS:不要忘了 , Linux 中一切都被抽象成文件,设备也是文件,可以进行读和写 。
如果我们写的程序需要其他资源,比如打开一个文件进行读写,这也很简单,进行系统调用,让内核把文件打开,这个文件就会被放到 files 的第 4 个位置,对应文件描述符 3:
明白了这个原理,输入重定向就很好理解了,程序想读取数据的时候就会去 files[0] 读?。?所以我们只要把 files[0] 指向一个文件 , 那么程序就会从这个文件中读取数据 , 而不是从键盘:
同理,输出重定向就是把 files[1] 指向一个文件,那么程序的输出就不会写入到显示器,而是写入到这个文件中:
错误重定向也是一样的,就不再赘述 。
推荐阅读
- 商业拍摄题材是什么意思,商业拍摄题材是什么意思呀
- csqlserver操作系统,sqlserver osql
- postgresql卸载ubuntu,ubuntu卸载pip3
- 快手网红大麦直播的产品,快手网红大麦真实名字叫什么
- php数据库公告 php中数据库怎么设计
- 姜云升粉丝站微信公众号,姜云升粉丝群群规是什么
- 电脑电子稿怎么打印,怎样在电脑上打印稿子
- 做ppt买个什么电脑,做ppt买个什么电脑比较好
- java围棋代码用户登录 js围棋