操作系统学习笔记（4）—— 操作系统的特征
操作系统的特征从总体上看，操作系统具有五个方面的特征：虚拟化 (Virtualization)、并发性 (Concurrency)、异步性、共享性和持久性 (Persistency)。操作系统的虚拟化可以理解为它对内存、CPU 的抽象和处理；并发性和共享性可以理解为操作系统支持多个应用程序“同时”运行；异步性可以从操作系统调度、中断处理对应用程序执行造成的影响等几个方面来理解；持久性则可以从操作系统中的文件系统支持把数据方便地从磁盘等存储介质上存入和取出来理解。虚拟性内存虚拟化程序员在写应用程序的时候，不用考虑其程序的起始内存地址要放到计算机内存的具体某个位置，而是用字符串符号定义了各种变量和函数，直接在代码中便捷地使用这些符号就行了。是由于操作系统建立了一个 地址固定 ， 空间巨大 的虚拟内存给应用程序来运行，这是 内存虚拟化 ，其实是一种 “空间虚拟化” ， 可进一步细分为 内存地址虚拟化 和 内存大小虚拟化 。这里的每个符号在运行时是要对应到具体的内存地址的。这些内存地址的具体数值是什么？程序员不用关心。为什么？因为编译器会自动帮我们把这些符号翻译成地址，形成可执行程序。程序使用的内存是否占得太大了？在一般情况下，程序员也不用关心。[scode type="blue" size=""]实际上，编译器 (Compiler，比如 gcc) 和链接器 (linker，比如 ld) 也不知道程序每个符号对应的地址应该放在未来程序运行时的哪个物理内存地址中。所以，编译器的一个简单处理办法就是，设定一个固定地址（比如 0x10000）作为起始地址开始存放代码，代码之后是数据，所有变量和函数的符号都在这个起始地址之后的某个固定偏移位置。假定程序每次运行都是位于一个不会变化的起始地址。这里的变量指的是全局变量，其地址在编译链接后会确定不变。但局部变量是放在堆栈中的，会随着堆栈大小的动态变化而变化。这里编译器产生的地址就是虚拟地址。这里，编译器和链接器图省事，找了一个适合它们的解决办法。当程序要运行的时候，这个符号所对应的虚拟内存地址到计算机的物理内存地址的映射必须要解决了，于是这个任务便推到了操作系统身上。操作系统会把编译器和链接器生成的执行代码和数据放到空闲的物理内存中，并建立虚拟地址到物理地址的映射关系。由于物理内存中的空闲区域是动态变化的，这导致虚拟地址到物理地址的映射关系也是动态变化的，需要操作系统来维护好可变的映射关系，确保编译器“固定起始地址”的假设成立。只有操作系统维护好了这个映射关系，才能让程序员只需写一些易于人理解的字符串符号来代表一个内存空间地址，且编译器只需确定一个固定地址作为程序的起始地址就可以不用考虑将来这个程序要在哪里运行的问题，从而实现了 内存地址虚拟化 。在这里，虚拟地址到物理地址的转换关系由操作系统与 MMU(Memory Management Unit) 进行交互从而处理映射关系。[/scode]应用程序在运行时不用考虑当前物理内存是否够用。如果应用程序需要一定空间的内存，但由于在某些情况下，物理内存的空闲空间可能不多了，这时操作系统通过把物理内存中最近没使用的空间（不是空闲的，只是最近用得少）换出（就是“挪地”）到硬盘上暂时缓存起来，这样空闲空间就大了，就可以满足应用程序的运行时内存需求了，从而实现了 内存大小虚拟化 。[scode type="green" size=""]这里其实就涉及到了内存页的概念，通过将内存按页面大小的映射，从而实现了方便的装载与卸载，等到需要使用时再重新建立映射关系[/scode]CPU虚拟化不同的应用程序可以在内存中并发运行，相同的应用程序也可有多个拷贝在内存中并发运行。而每个程序都“认为”自己完全独占了 CPU 在运行，这是 “CPU虚拟化” ，也是一种 “时间虚拟化” 。操作系统给了运行的应用程序一个幻象，即操作系统把时间分成小段，每个应用程序占用其中一小段时间片运行，用完这一时间片后，操作系统会切换到另外一个应用程序，让它运行。由于时间片很短，操作系统的切换开销也很小，应用程序或使用应用程序的用户基本上是看不出的，反而感觉到多个程序各自在独立“并行”执行，从而实现了 CPU虚拟化 。[scode type="yellow" size=""]并行 (Parallel) 是指两个或者多个事件在同一时刻发生；并发 (Concurrent) 是指两个或多个事件在同一时间间隔内发生。对于基于单 CPU 的计算机而言，各个“同时”运行的程序其实是串行分时复用一个 CPU ，任一个时刻点上只有一个程序在 CPU 上运行。 这些虚拟性的特征给应用程序的开发和执行提供了非常方便的执行环境，但也给操作系统的设计与实现提出了很多挑战。[/scode]并发性操作系统为了能够让 CPU 充分地忙起来，并充分利用各种资源，就需要有多种不同的应用程序在执行。这些应用程序是分时执行的，并由操作系统来完成各个应用在运行时的任务切换。并发性虽然能有效改善系统资源的利用率，但也带来了对共享资源的争夺问题，即同步互斥问题。还会带来执行时间的不确定性问题，即并发程序在执行中是走走停停，断续推进的，使得应用程序的完成时间是不确定的。并发性对操作系统的设计也带来了很多挑战，一不小心就会出现程序执行结果不确定，程序死锁等很难调试和重现的问题。异步性在这里，异步是指由于操作系统的调度和中断等，会不时地暂停或打断当前正在运行的程序，使得程序的整个运行过程走走停停。在应用程序运行的表现上，特别它的执行完成时间是不可预测的。但需要注意，只要应用程序的输入是一致的，那么它的输出结果应该是符合预期的。共享性共享是指多个应用并发运行时，宏观上体现出它们可同时访问同一个资源，即这个资源可被共享。但其实在微观上，操作系统在硬件等的支持下要确保应用程序互斥访问这个共享的资源。比如，对于两个应用同时访问同一个内存单元的情况，从宏观的应用层面上看，二者都能正确地读出同一个内存单元的内容；而在微观上，操作系统会调度应用程序的先后执行顺序，确保在任何一个时刻，只有一个应用去访问存储单元。持久性操作系统提供了文件系统来从可持久保存的存储介质（磁盘， SSD 等，以后以硬盘来代表）中取数据和代码到内存中，并可以把内存中的数据写回到硬盘上。硬盘在这里是外设，具有持久性，以文件系统的形式呈现给应用程序。[scode type="share" size=""]文件系统也可看成是操作系统对存储外设（如硬盘、SSD 等）的虚拟化。 这种持久性的特征进一步带来了共享属性，即在文件系统中的文件可以被多个运行的程序所访问，从而给应用程序之间实现数据共享提供了方便。即使掉电，存储外设上的数据还不会丢失，可以在下一次机器加电后提供给运行的程序使用。持久性对操作系统的执行效率提出了挑战，如何让数据在高速的内存和慢速的硬盘间高效流动是需要操作系统考虑的问题。[/scode]
[scode type="share"]上篇文章写的有些简略，而且有些地方在实际开发中还是有一定问题存在的，所以就有了这个进阶篇，这篇文章也是因为最近在搞前端开发，所以顺便总结一下[/scode]大纲首先我先说明一下这次使用的技术栈：less css预处理器，知名的还有sass、scss、stylus等react 主要的开发依赖库typescript 提供静态类型等诸多好处webpack 打包工具webpack-dev-server 开发用服务器react-router 路由管理库axios 网络请求库babel es新特性等代码的polyfill以这些作为主要的开发工具和生产依赖，除此之外还有一些关于webpack打包的插件，babel的插件之类的都会在后续进行一一说明步骤首先，我们从新建文件夹开始mkdir my-app \ && cd my-app \ && npm init -y然后我们就开始装依赖了npm install react react-dom react-router-dom axios --save-exact以上是几个生产环境所需要的依赖，所以放在一起进行安装，这里的--save-exact是为了把下载下来的依赖库版本固定，从而避免不同人开发时因为版本更新导致的诸多问题现在我们开始安装开发方面的依赖库，这里要使用一个参数--save-dev，说明这些依赖只在开发时使用npm install @babel/core @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react @babel/runtime @babel/preset-typescript --save-dev \ && npm install webpack webpack-cli webpack-dev-middleware webpack-dev-server webpack-merge --save-dev \ && npm install typescript @types/react @types/react-dom @types/webpack @types/node ts-node --save-dev \ && npm install less less-loader css-loader babel-loader style-loader --save-dev \ && npm install clean-webpack-plugin css-minimizer-webpack-plugin fork-ts-checker-webpack-plugin html-webpack-plugin mini-css-extract-plugin --save-dev这里可以看到，有非常多的开发方面的依赖，在这里我一行一行的进行解释首先第一行，我们可以看到都是关于babel方面的一些库，下面是它们的作用@babel/core babel的核心库@babel/plugin-transform-runtime 和 @babel/runtime 提供对async/await功能的支持@babel/preset-env 提供对ES规范新功能的支持@babel/preset-react 提供对react相关代码规范的支持@babel/preset-typescript 提供对typescript代码的支持babel主要的作用就是为了提供对一些浏览器不支持的语法进行降级转换的作用，使得其可以在浏览器运行然后是第二行，关于webpack的一些功能模块webpack 和 webpack-cli 一个是核心库，一个是命令行支持webpack-dev-middleware 和 webpack-dev-server 对开发环境提供热替换开发服务器功能webpack-merge 用于对配置文件的merge，方便进行配置文件的区分管理（ts原生也可以合并，但是这个比较智能）第三行，关于typescript方面的一些模块声明文件还有typescript自身，这里就不做详细解释了第四行，提供了less支持和一些webpack读取相关文件的相应loader第五行，关于webpack的一些插件clean-webpack-plugin 在打包生成文件时会对之前的文件进行相应的清理操作，主要是为了修复webpack-dev-middleware在启用writeToDisk功能时，webpack原生配置启用自动清理却无法清理的bugcss-minimizer-webpack-plugin 对打包生成的css进行最小化压缩处理fork-ts-checker-webpack-plugin 提供在打包编译前先对源代码进行ts类型检查的功能html-webpack-plugin 提供对html模板的处理功能，比如改改标题，挂载打包生成的js和css文件之类的mini-css-extract-plugin 将 CSS 提取到单独的文件中，为每个包含 CSS 的 JS 文件创建一个 CSS 文件，并且支持 CSS 和 SourceMaps 的按需加载。[scode type="blue"]这里的SourceMaps实际上与压缩时采用的一种技术有关，因为压缩后生成的文件的可读性非常差，而SourceMaps其实就相当于一本密码本可以把压缩前的源文件给复原出来，从而方便开发人员调试，具体压缩时采用了VLQ编码，感兴趣的同学可以去看看阮一峰的这篇文章[/scode]到这里，我们就把所安装的所有依赖给说明完毕了，接下来就是这些依赖的配置篇了配置babel的相关配置我们首先在项目根目录下创建一个.babelrc文件然后填入以下内容{ "presets": [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ], "plugins": [ [ "@babel/plugin-transform-runtime", { "regenerator": true } ] ] }很容易理解，上面三项是关于上述的一些预设的转换规则的填入，而这里的plugin中的regenerator配置项，是有关js的generator函数方面的，也即形如下面的函数function* name([param[, param[, ... param]]]) { statements }这方面的内容可以参考MDN这里的配置主要是为了防止形如上述的函数污染全局变量域webpack的配置因为我们引入了webpack的types和ts-node，所以我们可以使用typescript进行webpack相关配置文件的编写，同时因为我们引入了webpack-merge，我们可以针对生产环境和开发环境采用不同的配置文件。所以我们可以创建webpack.common.ts webpack.dev.ts和webpack.prod.ts三个文件以下是common相关配置import HtmlWebpackPlugin from "html-webpack-plugin"; import { CleanWebpackPlugin } from "clean-webpack-plugin"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; import path from "path"; import { Configuration } from "webpack"; const commonConfig: Configuration = { entry: { app: ["./src/index.tsx"], }, output: { path: path.resolve(__dirname, "dist"), }, resolve: { alias: { "@": path.resolve(__dirname, "src"), "@component": path.resolve(__dirname, "src/components"), "@page": path.resolve(__dirname, "src/pages"), "@lib": path.resolve(__dirname, "src/libs"), }, extensions: [".ts", ".tsx", ".js", "jsx", ".json"], }, module: { rules: [ { test: /\.(ts|js)x?$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript", ], }, }, }, { test: /\.(png|jpg|gif)$/, type: "asset/resource", generator: { filename: "images/[hash][ext][query]", }, }, { test: /\.svg$/, type: "asset/inline", }, ], }, plugins: [ new HtmlWebpackPlugin({ title: "my_app", filename: "index.html", template: "./public/index.html", }), new CleanWebpackPlugin(), new ForkTsCheckerWebpackPlugin({ async: false, //set this to false so that Webpack waits for the type checking process to finish before it emits any code. }), ], }; export default commonConfig;可以看到，我们这里引入了三个plugin，作用在上面已经解释过了然后我们看到entry就是相关的入口文件，在react里面，我们一般就会有以下的入口文件定义import * as React from "react"; import * as ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import App from "@/App"; ReactDOM.render( <App /> , document.getElementById("app") );然后关于相关的html模板则会有如下的定义 <%= htmlWebpackPlugin.options.title %> 这里有一个<%= htmlWebpackPlugin.options.title %>，这个是为了能够让html-webpack-plugin中配置的标题能够注入进模板中而这里关于所谓的@别名其实就与webpack中配置的resolve中的alias有关了，具体配置方法可以参照以上配置文件，但是需要注意的是，仅仅只在webpack配置文件配置这个别名是不够的，还必须在tsconfig.json中有相关的配置，这里给出tsconfig的配置文件{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@page/*": ["src/pages/*"], "@component/*": ["src/components/*"], "@lib/*": ["src/libs/*"] }, "lib": [ "dom", "DOM.Iterable", "esnext" ] /* Specify library files to be included in the compilation. */, "allowJs": true, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, "sourceMap": true /* Generates corresponding '.map' file. */, "outDir": "./dist/" /* Redirect output structure to the directory. */, "removeComments": false /* Do not emit comments to output. */, "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "preserveConstEnums": true, "resolveJsonModule": true, "moduleResolution": "node" /* Specify module resolution strategy: 'node', 'classic' or 'none'. */, "forceConsistentCasingInFileNames": true /* Ensures that the casing of referenced file names is consistent during the type checking process. */, "skipLibCheck": false, "isolatedModules": true, "strict": true, "noEmit": true, "typeRoots": [ "node_modules/@types" ] /* List of folders to include type definitions from. */ }, "include": ["src/**/*"] }可以看到，我们需要在paths中配置相关的别名，这里主要解释几个配置项，一个是 include ，配置我们需要读取的源文件在哪里，还有就是 lib ，告诉编译器我们需要编译出的js代码的对象是提供给浏览器使用的，还有就是编译成最新的es规范代码，其余的一些配置项详情可以在以下页面进行查看然后回到我们的webpack配置，可以看到module里面有关于不同文件的不同处理方式，这些东西大多参照自webpack官方文档，没有什么非常需要解释的，总的来说，就是对匹配到的不同类型的文件进行不同的打包处理，而关于 resolve 下的 extensions ，其实就是定义了同名文件的解析顺序，如果有多个文件的名字相同而类型不同，就会按照定义的顺序进行解析，如果解析成功就会跳过之后的文件后缀名。然后再来讲讲开发环境下webpack的特殊配置import { merge } from "webpack-merge"; import { Configuration as WebpackConfiguration } from "webpack"; import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server"; import commonConfig from "./webpack.common"; interface Configuration extends WebpackConfiguration { devServer?: WebpackDevServerConfiguration; } const devConfig: Configuration = merge(commonConfig, { mode: "development", output: { filename: "js/[name].[hash].bundle.js", }, module: { rules: [ { test: /\.css$/i, use: ["style-loader", "css-loader"], }, { test: /\.less$/i, use: ["style-loader", "css-loader", "less-loader"], }, ], }, devtool: "inline-source-map", optimization: { runtimeChunk: "single", }, devServer: { historyApiFallback: true, devMiddleware: { writeToDisk: true, }, hot: true, }, }); export default devConfig;这里由于webpack自身和webpack-dev-server的配置文件类型中有所不同，所以为了包含webpack-dev-server的配置文件，在这里自己定义了一个接口来进行整合处理。然后这里关于dev-server的配置，historyApiFallback是为了适配react-router的特殊路由系统，hot则是启用HMR（热模块重载）功能，然后就是重新定义了输出的js文件名和关于样式文件的处理方式与生产环境有所不同import { merge } from "webpack-merge"; import commonConfig from "./webpack.common"; import CssMinimizerPlugin from "css-minimizer-webpack-plugin"; import MiniCssExtractPlugin from "mini-css-extract-plugin"; import { Configuration } from "webpack"; const prodConfig: Configuration = merge(commonConfig, { devtool: "source-map", mode: "production", output: { filename: "js/[name].[contenthash].bundle.js", }, module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, "css-loader"], }, { test: /\.less$/i, use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"], }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].css", }), ], optimization: { minimizer: [new CssMinimizerPlugin()], splitChunks: { chunks: "all", }, }, }); export default prodConfig;总体来讲，这两种配置的差异主要就是输出的js文件命名方式不同，还有样式文件的输出方式不同，除此之外就只有sourcemap的生成方式不同配置完了这些，我们就可以来配置几个快速指令了，打开package.json，在scripts写入以下配置{ "start": "webpack-dev-server --open --config webpack.dev.ts", "build": "webpack --config webpack.prod.ts" }然后我们就可以使用npm start来启动这个项目了，当然在此之前，我们还得写一个App.tsx，我想这个应该就不用赘述了。效率提高为了统一的代码风格，我们可以引入prettier进行代码格式化管理npm install --save-dev prettier然后在项目根目录创建.prettierrc.json和.prettierignore.prettierrc.json写入以下内容{ "trailingComma": "es5", "tabWidth": 4, "semi": true, "singleQuote": false }.prettierignore填入node_modules # Ignore artifacts: dist关于prettier的具体配置可以查看官网最后这段时间也是正好在写一个前端的项目，自己也翻了无数的资料文档，最终才总结出来了这么一些配置，前端的工具真的是太多太多了，所以也是让人看的眼花缭乱，光是一个webpack的配置就已经非常的复杂了，我看了好几天的webpack文档，通过一点点的去尝试修改，才最终获得了一个满意的开发环境。可以说，从零开始配置前端开发环境是真的非常困难的一件事情，在如今这样一个前端工具漫天飞的时代，也许脚手架才是更好的选择，但是偶尔停下脚步，去探究一下黑盒之下的东西，也是为了更好的理解脚手架的工作，以及懂得脚手架带来的便利是一个多么伟大的进步。
起因：这几天，终于闲下心来，少娱乐，多玩点技术。首先，关于linux环境，之前hackweek买的一台学生机没派上用场，想着闲着也是闲着，不如好好利用一下学学linux，之前构建web服务器环境都是直接宝塔后台可视化界面，一键部署的懒人操作。说实话，这样对我的linux知识了解很不好，于是我打算从零开始，编译安装配置每一项我需要构建的东西。先把阿里云的服务器系统改成了最新的CentOS 8.2,然后开工。安装nginx安装nginx是直接到官网下载源代码进行编译安装，所以要先准备前置条件gcc安装安装 nginx 需要先将官网下载的源码进行编译，编译依赖 gcc 环境，如果没有 gcc 环境，则需要安装：yum install gcc-c++PCRE pcre-devel 安装PCRE(Perl Compatible Regular Expressions) 是一个Perl库，包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式，所以需要在 linux 上安装 pcre 库，pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。命令：yum install -y pcre pcre-develzlib 安装zlib库提供了很多种压缩和解压缩的方式， nginx 使用 zlib 对 http 包的内容进行 gzip ，所以需要在 Centos 上安装 zlib 库。yum install -y zlib zlib-develOpenSSL 安装OpenSSL 是一个强大的安全套接字层密码库，囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议，并提供丰富的应用程序供测试或其它目的使用。nginx 不仅支持 http 协议，还支持 https（即在ssl协议上传输http），所以需要在 Centos 安装 OpenSSL 库。yum install -y openssl openssl-devel做好了前置安装，然后就是直接到nginx官网下载了，这一步一般都是用wget下载(推荐），所以如果没有wget，还得下一个wget。yum install wget然后就是到官网复制下载链接了：nginx官网解压下载完成后，使用tar -zxvf nginx-1.19.6.tar.gz cd nginx-1.19.6/进入到解压根目录后，直接使用./configure等待配置完成后，开始编译安装make && make install安装完成后查找安装目录whereis nginx然后cd /usr/local/nginxnginx的可执行文件在根目录下的sbin中，进入sbin使用./nginx即可启动nginx服务[scode type="blue"]这里补充一些常用命令：./nginx -s stop：此方式相当于先查出nginx进程id再使用kill命令强制杀掉进程。./nginx -s quit：此方式停止步骤是待nginx进程处理任务完毕进行停止。./nginx -s reload：重载nginx的设置，即nginx目录下的conf中的nginx.conf中的设置更改后需要执行此命令重载[/scode]开机自启动即在rc.local增加启动代码就可以了。vi /etc/rc.local增加一行 /usr/local/nginx/sbin/nginx设置执行权限：chmod 755 rc.local然后来到/usr/local/nginx/conf，用vim打开nginx.conf，把server_name改成自己的服务器ip或者已经准备好的域名，保存后使用重载命令后便可以直接访问nginx搭建好的web网站了。安装php一般可以直接通过yum -y install php直接安装php，但是我想要直接上最新版本的，所以得官网下载然后进行编译安装。这里得下两个前置包，不然在执行./configure的时候会报错yum install libxml2-devel yum install sqlite-devel然后到官网找到最新版下载地址使用wget进行下载，但是说实话，php官网下载速度一言难尽，所以我自己找了个下载源：下载源wget http://mirrors.sohu.com/php/php-8.0.0.tar.gz tar -zxvf php-8.0.0.tar.gz cd php-8.0.0/然后这里因为要搭配nginx使用，所以在进行编译配置的时候要加一点点参数./configure --prefix=/usr/local/php --enable-fpm--enable-fpm是为了安装php-fpm组件，然后--prefix代表了安装目录如果出现以下说明，证明配置成功了然后执行make && make install等待安装完成后找到php-fpm在/usr/local/php/sbin中，这个时候是启动不了php-fpm的，需要去/usr/local/php/etc中，把php-fpm.conf.default重命名为php-fpm.conf；除此之外还有该目录下的php-fpm.d中的www.conf.default要重命名为www.conf。不然启动php-fpm会显示找不到配置文件。mv php-fpm.conf.default php-fpm.conf cd php-fpm.d mv www.conf.default www.conf此时再回到/usr/local/php/sbin输入./php-fpm便可以成功启动了，然后php-fpm是没有重载命令的，只能使用killall php-fpm来停止服务.这时候回到nginx的conf目录中，将nginx.conf用vim打开，即vim nginx.conf然后，找到里面被注释掉的这个地方改成图片中的样子即可保存。location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; #fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }这时候可以去nginx目录下的html中新建一个index.php，使用vim输入以下内容<?php echo phpinfo();?>然后保存，访问自己之前在server_name输入的内容后加上/index.php便可以看到以下内容:到这里，web服务器的php支持配置也告一段落了。[scode type="yellow"]当创建其他php文件的时候一定要注意文件的读写权限，不然有可能出现访问的时候显示access denied的情况[/scode]安装MySQL弄完了上面这些基础条件，那么就开始弄MySQL数据库了，首先第一件是得搞懂自己的系统版本,这里就直接使用hostnamectl这里可以看到是linux 8，所以来到mysql官网下载界面[scode type="share"]自CentOS7开始MySQL数据库已从默认的程序列表中移除，所以在安装前我们需要先去官网下载 Yum 资源包[/scode][scode type="share"]CentOS是Red Hat的改进版本，所以下载选择版本时选择Red Hat开头的[/scode]然后复制下载链接回到终端，使用wget https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm rpm -ivh mysql80-community-release-el8-1.noarch.rpm yum update yum install mysql-server等待下载完成后，进入下一步权限设置chown mysql:mysql -R /var/lib/mysql[scode type="blue"]chwon(英文全拼：change owner)用于设置文件所有者和文件关联组的命令; -R : 处理指定目录以及其子目录下的所有文件; mysql:mysql表示设置该目录下的所有文件的拥有者为mysql，群体的使用者为mysql[/scode]初始化MySQLmysqld --initialize启动MySQL服务systemctl start mysqld检查MySQL服务状态systemctl status mysqld现在可以使用mysqladmin来检测mysql是否安装成功mysqladmin --version该命令将输出以下结果，该结果基于系统信息：如果上述命令没有输出任何结果就说明没有安装成功咯MySQL基础配置现在可以直接输入mysql -u root -p，密码为空便可以进入mysql界面了执行后会输出 mysql>提示符然后现在最重要的事情就是设置密码，这里有三种方法，第一种是用exit退出mysql，使用mysqladmin -uroot password "new_password";或者直接在mysql里面使用set password for username@localhost = password(newpwd);或者先use mysql;//连接到mysql数据库 update mysql.user set authentication_string=password('新密码') where user='用户名' and Host ='localhost';//修改密码 flush privileges;//更新权限 quit;//退出数据库到这里数据库安装也告一段落了。安装Docker经过一系列的安装配置，可以利用docker对上面的配置进行打包整理，来实现重复利用。[scode type="blue"]上面指docker新建容器，然后在容器中把上面的操作全部重新进行一遍后打包成镜像（诶嘿~~）[/scode]首先得下载yum工具包，它提供了yum-config-manager用于管理yum安装配置，我们就要利用这个工具来设置docker的下载仓库sudo yum install -y yum-utils //安装yum-utils sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo //绑定docker仓库绑定好下载仓库后，进入下一步安装docker引擎和容器sudo yum install docker-ce docker-ce-cli containerd.io等待全部安装完成后，便可以启动docker然后为了测试安装是否成功，我们可以按照官方文档上的说明一样，使用sudo docker run hello-world看是否显示如下画面如上就说明docker安装成功了。更新docker当要更新docker的时候，必须要先把旧版卸载掉，那么此时可以使用如下命令来进行卸载[scode type="yellow"]这里的卸载并不会把自己下的镜像之类的给删除，所以可以放心卸载[/scode]sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine卸载完成后可以回到上面的安装docker引擎和容器进行更新卸载docker使用以下命令卸载docker引擎，容器和命令行工具sudo yum remove docker-ce docker-ce-cli containerd.io然后再使用以下命令删除镜像，自定义设置等文件sudo rm -rf /var/lib/docker[scode type="blue"]这一块算附加内容吧[/scode]
用于处理溢出的属性名为overflow它有以下几种处理方式：visible(默认值）为了防止数据损失hidden 也即将溢出内容隐藏掉，只能用于溢出内容不重要时scroll：这个属性可以产生一个滚动条，保证内容不溢出[scode type="blue"]同时的，overflow可以拆分成overflow-x和overflow-y代表这两个方向上对于溢出的处理方法,当然也可以通过对overflow设置两个值来分别控制在x轴和y轴上对于溢出的处理[/scode][scode type="red"]任何设计网页的时候都要注意对于溢出可能性的处理[/scode]