前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[Maven 插件自定义打包配置]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...核通过全局描述符表来定义和管理每个进程的代码段、数据段以及其他系统段的权限和地址范围,以确保不同进程间的隔离性和安全性。当cmd_execute_program函数加载并执行用户程序时,会根据当前进程的全局描述符表项设置相应的代码段和数据段,而文中提到的安全漏洞正是由于恶意程序crack.c利用了这个机制,使得两个进程间的数据段得以共享,从而进行非法篡改。 中断处理 , 中断处理是操作系统内核实现的一种重要机制,用于响应来自硬件或软件的各种事件。当CPU检测到如输入输出完成、计时器溢出、错误条件等中断事件时,会暂时停止当前正在执行的程序,并转而去执行预先设定好的中断服务例程(ISR)。在文章中,通过中断处理机制,操作系统能够在应用程序试图执行特权指令或侵犯内核空间时,及时切断其执行,并重新获得对CPU的控制权,从而保障系统的安全稳定运行。 特权指令 , 特权指令是在计算机体系结构中只能由操作系统内核或者处于特定特权模式下的程序执行的一类特殊指令。这类指令通常涉及到诸如访问和修改关键系统资源(如内存管理、中断控制器、任务调度等)的操作。在本文的上下文中,如果用户态的应用程序尝试执行特权指令,系统将触发中断,防止非授权访问内核资源,确保操作系统底层的安全性不受侵害。
2023-03-14 19:08:07
254
转载
转载文章
...e_Name);//定义断点long pos = 0,last = fSize-1,sum = 0;//判断前端需不需要分片下载if (null != request.getHeader("Range")){response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);String numRange = request.getHeader("Range").replaceAll("bytes=","");String[] strRange = numRange.split("-");if (strRange.length == 2){pos = Long.parseLong(strRange[0].trim());last = Long.parseLong(strRange[1].trim());//若结束字节超出文件大小 取文件大小if (last>fSize-1){last = fSize-1;} }else {//若只给一个长度 开始位置一直到结束pos = Long.parseLong(numRange.replaceAll("-","").trim());} }long rangeLenght = last-pos+1;String contentRange = new StringBuffer("bytes").append(pos).append("-").append(last).append("/").append(fSize).toString();response.setHeader("Content-Range",contentRange);// response.setHeader("Content-Lenght",String.valueOf(rangeLenght));os = new BufferedOutputStream(response.getOutputStream());is = new BufferedInputStream(new FileInputStream(file));is.skip(pos);//跳过已读的文件(重点,跳过之前已经读过的文件)byte[] buffer = new byte[1024];int lenght = 0;//相等证明读完while (sum < rangeLenght){lenght = is.read(buffer,0, (rangeLenght-sum)<=buffer.length? (int) (rangeLenght - sum) :buffer.length);sum = sum+lenght;os.write(buffer,0,lenght);}log.info("下载完成");}finally {if (is!= null){is.close();}if (os!=null){os.close();} }} } 启动成功 Vue <html xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"/><title>狂神说Java-ES仿京东实战</title><link rel="stylesheet" th:href="@{/css/style.css}"/></head><body class="pg"><div class="page" id="app"><div id="mallPage" class=" mallist tmall- page-not-market "><!-- 头部搜索 --><div id="header" class=" header-list-app"><div class="headerLayout"><div class="headerCon "><!-- Logo--><h1 id="mallLogo"><img th:src="@{/images/jdlogo.png}" alt=""></h1><div class="header-extra"><!--搜索--><div id="mallSearch" class="mall-search"><form name="searchTop" class="mallSearch-form clearfix"><fieldset><legend>天猫搜索</legend><div class="mallSearch-input clearfix"><div class="s-combobox" id="s-combobox-685"><div class="s-combobox-input-wrap"><input v-model="keyword" type="text" autocomplete="off" value="java" id="mq"class="s-combobox-input" aria-haspopup="true"></div></div><button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button></div></fieldset></form><ul class="relKeyTop"><li><a>狂神说Java</a></li><li><a>狂神说前端</a></li><li><a>狂神说Linux</a></li><li><a>狂神说大数据</a></li><li><a>狂神聊理财</a></li></ul></div></div></div></div></div><el-button @click="download" id="download">下载</el-button><!-- <el-button @click="concurrenceDownload" >并发下载测试</el-button>--><el-button @click="stop">停止</el-button><el-button @click="start">开始</el-button>{ {fileFinalOffset} }{ {contentList} }<el-progress type="circle" :percentage="percentage"></el-progress></div><!--前端使用Vue,实现前后端分离--><script th:src="@{/js/axios.min.js}"></script><script th:src="@{/js/vue.min.js}"></script><!-- 引入样式 --><link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"><!-- 引入组件库 --><script src="https://unpkg.com/element-ui/lib/index.js"></script><script>new Vue({ el: 'app',data: {keyword: '', //搜索关键字results: [] ,//搜索结果percentage: 0, // 下载进度filesCurrentPage:0,//文件开始偏移量fileFinalOffset:0, //文件最后偏移量stopRecursiveTags:true, //停止递归标签,默认是true 继续进行递归contentList: [], // 文件流数组breakpointResumeTags:false, //断点续传标签,默认是false 不进行断点续传temp:[],fileMap:new Map(),timer:null, //定时器名称},methods: {//根据关键字搜索商品信息searchKey(){var keyword=this.keyword;axios.get('/search/JD/search/'+keyword+"/1/10").then(res=>{this.results=res.data;//绑定数据console.log(this.results)console.table(this.results)})},//停止下载stop(){//改变递归标签为falsethis.stopRecursiveTags=false;},//开始下载start(){//重置递归标签为true 最后进行合并this.stopRecursiveTags=true;//重置断点续传标签this.breakpointResumeTags=true;//重新调用下载方法this.download();},// 分段下载需要后端配合download() {// 下载地址const url = "/down?fileName="+this.keyword.trim()+"&drive=E";console.log(url)const chunkSize = 1024 1024 50; // 单个分段大小,这里测试用100Mlet filesTotalSize = chunkSize; // 安装包总大小,默认100Mlet filesPages = 1; // 总共分几段下载//计算百分比之前先清空上次的if(this.percentage==100){this.percentage=0;}let sentAxios = (num) => {let rande = chunkSize;//判断是否开启了断点续传(断点续传没法并行-需要上次请求的结果作为参数)if (this.breakpointResumeTags){rande = ${Number(this.fileFinalOffset)+1}-${num chunkSize + 1};}else {if (num) {rande = ${(num - 1) chunkSize + 2}-${num chunkSize + 1};} else {// 第一次0-1方便获取总数,计算下载进度,每段下载字节范围区间rande = "0-1";} }let headers = {range: rande,};axios({method: "get",url: url.trim(),async: true,data: {},headers: headers,responseType: "blob"}).then((response) => {if (response.status == 200 || response.status == 206) {//检查了下才发现,后端对文件流做了一层封装,所以将content指向response.data即可const content = response.data;//截取文件总长度和最后偏移量let result= response.headers["content-range"].split("/");// 获取文件总大小,方便计算下载百分比filesTotalSize =result[1];//获取最后一片文件位置,用于断点续传this.fileFinalOffset=result[0].split("-")[1]// 计算总共页数,向上取整filesPages = Math.ceil(filesTotalSize / chunkSize);// 文件流数组this.contentList.push(content);// 递归获取文件数据(判断是否要继续递归)if (this.filesCurrentPage < filesPages&&this.stopRecursiveTags==true) {this.filesCurrentPage++;//计算下载百分比 当前下载的片数/总片数this.percentage=Number((this.contentList.length/filesPages)100).toFixed(2);sentAxios(this.filesCurrentPage);//结束递归return;}//递归标签为true 才进行下载if (this.stopRecursiveTags){// 文件名称const fileName =decodeURIComponent(response.headers["fname"]);//构造一个blob对象来处理数据const blob = new Blob(this.contentList);//对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性//IE10以上支持blob但是依然不支持downloadif ("download" in document.createElement("a")) {//支持a标签download的浏览器const link = document.createElement("a"); //创建a标签link.download = fileName; //a标签添加属性link.style.display = "none";link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click(); //执行下载URL.revokeObjectURL(link.href); //释放urldocument.body.removeChild(link); //释放标签} else {//其他浏览器navigator.msSaveBlob(blob, fileName);} }} else {//调用暂停方法,记录当前下载位置console.log("下载失败")} }).catch(function (error) {console.log(error);});};// 第一次获取数据方便获取总数sentAxios(this.filesCurrentPage);this.$message({message: '文件开始下载!',type: 'success'});} }})</script></body></html> 本篇文章为转载内容。原文链接:https://blog.csdn.net/kangshihang1998/article/details/129407214。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-19 08:12:45
546
转载
Kafka
...负责管理集群的状态和配置。没有Zookeeper,Kafka就无法正常运作。比如说啊,新添了个Broker(也就是那个消息中转站),Zookeeper就会赶紧告诉其他Broker:“嘿,快看看这位新伙伴,更新一下你们的状态吧!”还有呢,要是某个分区的老大换了(Leader切换了),Zookeeper也会在一旁默默记好这笔账,生怕漏掉啥重要信息似的。 java // 启动Zookeeper nohup zookeeper-server-start.sh config/zookeeper.properties & 虽然Zookeeper很重要,但它也有一定的局限性。比如,它可能会成为单点故障,影响整个系统的稳定性。因此,近年来Kafka也在尝试去掉对Zookeeper的依赖,开发了自己的内部协调机制。 3.3 日志(Log):Kafka的四肢 日志是Kafka存储消息的地方,每个分区对应一个日志文件。嘿,这个日志设计可太聪明了!它用的是顺序写入的方法,就像一条直线往前跑,根本不用左顾右盼,写起来那叫一个快,效率直接拉满! java // 查看日志路径 cat config/server.properties | grep log.dirs 日志的大小可以通过参数log.segment.bytes来控制。默认值是1GB,你可以根据实际情况调整。要是日志文件太大了,查个东西就像在大海捞针一样慢吞吞的;但要是弄得太小吧,又老得换新的日志文件,麻烦得很,还费劲。 --- 4. 实战演练 从零搭建一个Kafka环境 说了这么多理论,咱们来实际操作一下吧!假设我们要搭建一个简单的Kafka环境,用来收集用户的登录日志。 4.1 安装Kafka和Zookeeper 首先,我们需要安装Kafka和Zookeeper。可以从官网下载最新的二进制包,解压后按照文档配置即可。 bash 下载Kafka wget https://downloads.apache.org/kafka/3.4.0/kafka_2.13-3.4.0.tgz 解压 tar -xzf kafka_2.13-3.4.0.tgz 4.2 创建主题和消费者 接下来,我们创建一个名为login_logs的主题,并启动一个消费者来监听消息。 bash 创建主题 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic login_logs 启动消费者 bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic login_logs --from-beginning 4.3 生产消息 最后,我们可以编写一个简单的Java程序来生产消息。 java import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import java.util.Properties; public class KafkaProducerExample { public static void main(String[] args) { Properties props = new Properties(); props.put("bootstrap.servers", "localhost:9092"); props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); KafkaProducer producer = new KafkaProducer<>(props); for (int i = 0; i < 10; i++) { producer.send(new ProducerRecord<>("login_logs", "key" + i, "value" + i)); } producer.close(); } } 这段代码会向login_logs主题发送10条消息,每条消息都有一个唯一的键和值。 --- 5. 总结 Kafka的魅力在于细节 好了,到这里咱们的Kafka之旅就告一段落了。通过这篇文章,我希望大家能更好地理解Kafka的命名规范和组织结构。Kafka为啥这么牛?因为它在设计的时候真是把每个小细节都琢磨得特别透。就像给主题起名字吧,分个区啦,还有消费者组怎么配合干活儿,这些地方都能看出人家确实是下了一番功夫的,真不是随便凑合出来的! 当然,Kafka的学习之路还有很多内容需要探索,比如监控、调优、安全等等。其实我觉得啊,只要你把命名的规矩弄明白了,东西该怎么放也心里有数了,那你就算是走上正轨啦,成功嘛,它就已经在向你招手啦!加油吧,朋友们! --- 希望这篇文章对你有所帮助,如果有任何疑问,欢迎随时交流哦!
2025-04-05 15:38:52
95
彩虹之上
转载文章
...字,而且这个值不可以配置,不能修改,但是有理由相信512字节的区域是为了节约内存额外开销。按照这个值,1MB的内存只需要2KB的额外空间就能描述引用关系。这又带来另一个问题,就是512字节里面的内存可能被引用多次,所以这是一个粗略的关系描述,那么在使用的时候需要遍历这512字节。 再举一个例子,假设有两个对象B、C都在这512字节的区域内。为了方便处理,记录对象引用关系的时候,都使用对象的起始位置,然后用这个地址和512对齐,因此B和C对象的卡表指针都指向这一个卡表的位置。那么对于引用处理也有可有两种处理方法:·处理的时候会以堆分区为处理单位,遍历整个堆分区,在遍历的时候,每次都会以对象大小为步长,结合卡表,如果该卡表中对应的位置被设置,则说明对象和其他分区的对象发生了引用。具体内容在后文中介绍Refine的时候还会详细介绍。·处理的时候借助于额外的数据结构,找到真正对象的位置,而不需要从头开始遍历。在后文的并发标记处理时就使用了这种方法,用于找到第一个对象的起始位置。在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。 在G1除了512字节粒度的卡表之外,还有bitMap,例如使用bitMap可以描述一个分区对另外一个分区的引用情况。在JVM中bitMap使用非常多,例如还可以描述内存的分配情况。G1在混合收集算法中用到了并发标记。在并发标记的时候使用了bitMap来描述对象的分配情况。例如1MB的分区可以用16KB(16KB×ObjectAlignmentInBytes×8=1MB)来描述,即16KB额外的空间。其中ObjectAlignmentInBytes是8字节,指的是对象对齐,第二个8是指一个字节有8位。即每一个位可以描述64位。例如一个对象长度对齐之后为24字节,理论上它占用3个位来描述这个24字节已被使用了,实际上并不需要,在标记的时候只需要标记这3个位中的第一个位,再结合堆分区对象的大小信息就能准确找出。其最主要的目的是为了效率,标记一个位和标记3个位相比能节约不少时间,如果对象很大,则更划算。这些都是源码的实现细节,大家在阅读源码时需要细细斟酌。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_16500963/article/details/132133125。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-16 20:37:50
246
转载
转载文章
...it.msc~计算机配置~管理模板~网络~Qos数据计划程序~限制保留宽带~属 性~已启用~将宽带限制改为0%~选应用~确定 网页地址栏里有很多记录 只删其中某个,不是全部删:在注册表中修改:单击“开始”菜单-->运行,输入regedit,依次找到: HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\TypedURLs 在右空格中删除你想删的对应 网页的键值即可。 全删除: 1、打开IE选工具/Internet选项/高级/勾选“清除地址栏下拉列表中显示的历史记录”按应用。 2、打开IE选工具/Internet选项/常规/选“清除历史记录”按应用。 3、打开IE选工具/Internet选项/内容/自动完成/点击“清除表单”或“清除密码”,按确定。 误删资料恢复 步骤: 1、单击“开始——运行,然后输入regedit (打开注册表) 2、依次展开:EKEY——LOCAL——MACHIME/SOFTWARE/microsoft/WINDOWS/CURRENTVERSION/ EXPLORER/DESKTO P/NAMESPACE 在左边空白外点击“新建” ,选择:“主键”, 把它命名为 “645FFO40——081——101B——9F08——00AA002F954E” 再把右边的“默认”的主键的键值设 为“回收站”,然后退出注册表。就OK啦。 3、要重启计算机。只要机器没有运行过磁盘整理。系统完好.任何时候的文件都可以找回来。 win7清除任务栏无意义图标:www.shanpow.com_删除Download和DataStore文件夹中的所有文件。 1、输入“regedit”打开注册表编辑器,然后打开如下键值: HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify 在右边你可以看到两个键值IconStreams和PastIconsStream,将它们的值删除。 2、然后调出任务管理器将进程“explorer.exe”终止,再在任务管理器中点击“文件——新建任务”, 输入“explorer”——确定 Win7安全中心服务启用不了时: 开始----运行-----输入“services.msc "确定-----找到(windows)security center 启动类型设置为自动并启动它 或者 右键单击计算机---管理----服务和应用程序----服务---找到(windows)security centerwww.shanpow.com_删除Download和DataStore文件夹中的所有文件。 ----双击-----启动类型设置为“自动”。 1.在服务管理中,关闭Windows Update服务 2.打开C:\Windows\SoftwareDistribution文件夹 3.删除DataStore和Download文件夹下的所有文件 4.启动Windows Update服务 5.进入Windows Update查看一下,Windows更新记录已经清除了。 如何用B电脑远程登录A电脑 注意:AB电脑都连接上了互联网 A电脑: 1添加一个用户名,设置登录密码。2我的电脑→属性→远程→允许用户远程连接到此计算机前 打√确定3网上邻居→属性→本地连接状态→支持→记下IP 地址XXX.XXX.XXX.XXX。 B电脑登录过程4 开始→所有程序→附件→通讯→远程桌面连→在弹出的窗口里输入A电脑的IP 地址 →连接。连接成功后会变成一个黑屏幕的画面,在屏幕的最上方有一个指示条,指示着机器是在远程登 录状态。当A电脑响应了B电脑的远程登录请求后,会给你返回一个画面,要求你输入用户名,密码。 5输入用户名和密码→确定。验证的用户名和密码是对的,他就会把其A桌面画面全传送到B电脑的屏 幕上来,稳定后就成功了! 有一事你不能作:关机。因为B电脑左下角的开始,是指挥自己用的,没 法指挥A电脑。 想使用B电脑控制A电脑关机,得在A电脑上设置:附件→windows 资源管理器→ WINDOWS 的文件夹→SYSTEM32文件夹→taskmgr.exe文件,右击把他发送到桌面上建一“桌面快捷方式”。 你在要关掉A电脑时,只要双击这个快捷方式,就会弹出来一个“WINDWOS任务管理器”窗口,上面有 “关机”命令,点“关机”就行了,当A电脑电源关闭以后,连接自然就断开了。 但这样的远程连接, 是有条件的:A电脑须有独立的 IP ,就是说,A电脑不能是局域网的内部保留 IP,所谓保留IP是指 如 10.XXX.XXX.XXX 或 192.168.XXX.XXX 等地址。如A电脑用的是ADSL,一般来说都是独立的IP,但 如果A用户是几户人家共用一个 ADSL宽带连接,通过一个ADSL共同上网的,那或许就不行了。须在路 由器上作一个“端口映射”设置。注意:A电脑防火墙的影响,有可能连不通。防火墙的缺省设置,一 般是禁止 INTERNET 上的电脑访问它的资源的。因而须开启防火墙的这个设置:允许 INTERNET上的机 器访问本机(A电脑)资源。[shutdown –s –t 0]此命令强制关机,一般不要用, WIN7远程连接前几步设置与WinXP一样。 开始→搜索框中输入MSTSC回车→在弹出的对话框中输入需要连接的计算机的IP→连接→账户密码 →确定不久显示器上出现了另一计算机的桌面,远程桌面连接成功。 教你怎样解除电脑开机密码。此方法仅供交流,严禁作为非法手段使用 方法1在开机时按下F8进入带命令提示符的安全模式输入NET USER+用户名+123456/ADD 可把某用户的密码强行设置为123456 方法2如用户忘记登录密码可 按下方法解决 此法不适用于忘记安装时所设定〔administrator〕的密码 1.在计算机启动时按F8及选Safe Mode With Command Prompt 2.选Administrator后便会跳出Command Prompt的窗口 3.用Net的命令增加一个用户,例:增加一个用户名为alanhkg888,命令语法如下: net user alanhkg888/add 4.将新增用户提升至Administrator的权力,例:提升刚才增 加用户alanhkg888的权力,命令语法如下 net localgroup administrators alanhkg888/add 5.完成上列步骤后重新启动计算机,在 启动画面上便增加了一个用户alanhkg888了,选alanhkg888进入www.shanpow.com_删除Download和DataStore文件夹中的所有文件。 6.登入后在控制台→使用者账户→选忘记密码的用户,然后选移除密码 7.在登入画面中选原来的用户便可不需密码情况下等入(因已移除了) 8.删除刚才新增的用户:在控制台→使用者账户→选alanhkg888,然后选移除账户便可 方法3 1、重新启动Windows XP,在启动画面出现后的瞬间按F8,选择带命令行的安全模 式运行。 2、运行过程停止时,系统列出了超级用户administrator和本地用户owner的选择菜单, 点击administrator,进入命令行模式。 3、键入命令:net user owner 123456/add,强制性将owner用户的口令更改为123456。 若想在此添加某一用户:用户名为abcdef,口令为123456的话,请输入net user abcdef 123456/add,添加后可用net localgroup administrators abcdef/add命令将用户提升为 系统管理组administrators用户,具有超级权限。 4.DOS下删windows\system32\config里面的SAM档就可以了 5.开机后按键盘的Delete键进入BIOS界面。找到User Password选项,其默认为关闭状 态。启动并输入用户密码(1~8位英文或数字)。计算机提示请再输入一遍以确认密码无误, 保存退出后重新启动机器,这时就会在开机时出现密码菜单 方法4我们知道在安装Windows XP过程中,首先是以administrator默认登录,然后会要 求创建一个新账户,以便进入Windows XP时使用此新建账户登录,而且在Windows XP的 登录接口中也只会出现创建的这个用户账号,不会出现administrator,但实际上该 administrator账号还是存在的,且密码为空。 【二】:Windows 7实战经验 Windows 7实战经验:完美解决Windows 7更新失败(Windows Update 错误 80070003) 很多用户反映,为什么Windows 7的自动更新会出显未知错误,导致很多更新都不能正确安装?针对这个问题,在我对自己的Windows 7进行更新的时候,有时也会发生类似的问题,经过研究,已经完美解决,下面给大家解决方案! 如果在检查更新时收到Windows Update错误80070003,则需要删除Windows用于标识计算机更新的临时文件。若要删除临时文件,请停止Windows Update服务,删除临时更新文件,重新启动Windows Update服务,然后再次尝试检查Windows更新。 以下步骤为解决Windows 7更新错误方法,本博客亲测有效。 必须以管理员身份进行登录,才能执行这些步骤。 1.单击打开“管理工具(通过单击“开始”按钮,再依次单击“控制面板”,然后单击“管理工具”。 2.双击“服务”。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 3.单击“名称”列标题以逆序排列名称。找到“Windows Update”服务,右键单击该服务,然后单击“停止”。 1.打开“计算机”。 2.双击安装Windows的本地硬盘(通常是驱动器C)。 3.双击Windows文件夹,然后双击SoftwareDistribution文件夹。 4.双击打开DataStore文件夹,然后删除该文件夹中的所有文件。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 5.单击“后退”按钮。在SoftwareDistribution文件夹中,双击打开Download文件夹,删除该文件夹中的所有文件,然后关闭窗口。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 必须以管理员身份进行登录,才能执行这些步骤。 1.单击打开“管理工具(方法同上)”。 2.双击“服务”。如果系统提示您输入管理员密码或进行确认,请键入该密码或提供确认。 3.单击“名称”列标题以逆序排列名称。找到“Windows Update”服务,右键单击该服务,然后单击“启动”。 4.关闭“服务”窗口和“管理工具”窗口。 完成上面操作,你需要重新更新看看可以成功更新了吗,一般因为我们删除了自动更新的一些文件,如果你仔细观察的话,那些文件大小并不是很小,所以我们再更新的时候等待的时间可能会长一些! 【三】:Win10系统提示“无法完成更新正在撤销更改” 更新win10系统补丁之后,系统会提示“window10无法更新,正在撤销”,需要重启好几次,这该怎么办呢?下面小编就向大家介绍一下windows10系统无法完成更新正在撤销更改的解决方法,欢迎大家参考和学习。 系统更新失败,反复重启还是不行,那是不是下载下来的补丁没用了呢??所以我们先要删除Windows更新的缓存文件!在做以下操作之前,首先我们要确认系统内的windows update & BITS服务设置是否开启。 检查方法: 1、按“Win+R”组合键打开运行,输入“services.msc”,点击确定(如果弹出用户账户控制窗口,我们点击“继续”)。 2、双击打开“Background Intelligent Transfer Services”服务。 3、在选项卡点击“常规”,要保证“启动类型”是“自动”或者“手动”。然后点击“服务状态”“启用”按钮。 4. 重复步骤3分别对“Windows Installer”,“Cryptographic Services”, “software licensing service” 以及“Windows Update”这四项服务进行检查。 解决办法: 1、按“Windows+X”打开“命令提示符(管理员)”。 2、输入“net stop wuauserv”回车(我们先把更新服务停止)。 3、输入”%windir%\SoftwareDistribution“回车(删除Download和DataStore文件夹中的所有文件)。 4、最后输入“net start wuauserv”回车(重新开启系统更新服务)。 完成以上的步骤之后,我们就可以在“Windows Update”中再次尝试检查更新即可。 以上就是windows10系统无法完成更新正在撤销更改的解决方法介绍了。遇到同样问题的用户,可以尝试一下这个方法,如果不行,可以留言,小编会继续寻找其他的解决办法。 【四】:Windows更新失败提示错误码80070003怎么办 Windows7,Windows8.1,Windows10在更新过程中,所更新的程序无法安装,导致更新失败,提示错误码80070003。遇到这种情况,无论再试一次,或重启电脑,更新程序仍无法安装,出现错误码80070003提示。关于这个故障,下面小编就为大家介绍一下具体的解决方法吧,欢迎大家参考和学习。 具体解决方法步骤: 1、在电脑更新过程中,更新失败,程序无法安装,出现错误码80070003的提示。如图1 2、打开控制面板,点击“系统和安全”,打开对话框。如图2 3、在打开的对话框中,点击“管理工具”-双击“服务”,在打开的对话框的下方找到“Windows Update"。(如图3),选择Windows Update,点击界面左上角的”停止“按键,或是单击右键选择”停止“。(如图4),以管理员身份进入,如果提示需要输入秘码,则输入秘码。 4、在C盘,打开”Windows"文件夹,-双击打开“SoftwareDistribution"文件夹,找到下面的2个文件夹。打开”DataStore"文件夹,删除里面所有的文件。反回上一步。如图5.1,再打开"Download"文件夹,删除里面所有的文件。(如图5.2) 5、返回第三步的操作,选择Windows Update,右键单击,选择“启动”。 6、做完上面操作后,安装更新文件就会顺利了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_42620202/article/details/119158423。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-16 16:18:33
136
转载
转载文章
...m是一个jQuery插件,专门用于实现表单异步提交和文件上传功能。在文中,作者使用ajaxForm插件来封装用户的头像上传表单,通过AJAX方式将图片发送到服务器端,同时支持文件大小校验等前置处理以及成功后的回调函数执行。 Jcrop , Jcrop是一个基于JavaScript的图像裁剪插件,它提供了一种直观、交互式的图像选择与裁剪界面。在本文所描述的场景中,用户在上传头像后,利用Jcrop选择需要保留的部分,该插件能够实时显示裁剪区域及预览效果,最终将选定区域的数据发送给服务器完成头像的精确裁剪与更新。 FancyBox , FancyBox是一款流行且易用的jQuery轻量级插件,用于实现网页上的图片弹出展示、iframe内容加载等功能。在文章中,作者运用FancyBox来创建一个弹出层,用户点击修改头像时,会通过Fancybox弹出一个包含上传图片表单的界面,从而实现在不跳转页面的情况下完成头像上传的操作流程。
2023-07-18 10:58:17
268
转载
转载文章
... 6 2.2 服务器配置 7 2.2.1 Tomcat安装及配置 8 2.2.2 数据库配置 8 3系统分析 11 3.1 可行性分析 11 3.1.1 技术可行性 11 3.1.2 操作可行性 11 3.1.3 经济可行性 11 3.1.4 法律可行性 11 3.2 腕表交易系统功能需求分析 11 3.3 数据库需求分析 12 4系统设计 13 4.1 系统功能模块设计 13 4.2系统流程设计 13 4.2.1 系统开发流程 13 4.2.2 用户登录流程 14 4.2.3 系统操作流程 15 4.2.4 添加信息流程 15 4.2.5 修改信息流程 16 4.2.6 删除信息流程 16 4.3系统用例分析 17 4.3.1 管理员用例图 17 4.3.2 用户用例图 18 4.4 数据库设计 19 4.4.1 tb_Ware(商品信息表) 19 4.4.2 tb_manager(管理员信息表) 19 4.4.3 tb_sub(订单生成表) 19 4.4.4 tb_Link(超级链接表) 20 4.4.5 tb_Affiche(公告信息表) 20 4.3 用SSM连接数据库 20 5系统实现 22 5.1 前台部分 22 5.1.1 前台总体框架 22 5.1.2 商城首页 22 5.1.3 产品详情页 23 5.1.4 评价 23 5.2 后台部分 24 5.2.1 后台主页 24 5.2.2 后台评价管理 25 5.2.3 商品管理 25 5.2.4 商品修改 26 5.2.5 分类管理 26 5.2.6 订单管理 27 5.2.7 腕表购物车管理 27 6系统测试 28 6.1系统测试的意义 28 6.2性能测试 29 6.3测试分析 29 总 结 30 致 谢 31 参考文献 31 3系统分析 3.1 可行性分析 腕表交易系统主要目标是实现网上展示腕表交易系统信息,购买腕表产品。在确定了目标后,我们从以下四方面对能否实现本系统目标进行可行性分析。 3.1.1 技术可行性 腕表交易系统主要采用Java技术,基于B/S结构,MYSQL数据库,主要包括前端应用程序的开发以及后台数据库的建立和维护两个方面。对于应用程序的开发要求具备功能要完备、使用应简单等特点,而对于数据库的建立和维护则要求建立一个数据完整性强、数据安全性好、数据稳定性高的库。腕表交易系统的开发技术具有很高可行性,且开发人员掌握了一定的开发技术,所以系统的开发具有可行性。 3.1.2 操作可行性 腕表交易系统的登录界面简单易于操作,采用常见的界面窗口来登录界面,通过电脑进行访问操作,会员只要平时使用过电脑都能进行访问操作。此系统的开发采用PHP语言开发,基于B/S结构,这些开发环境使系统更加完善。本系统具有易操作、易管理、交互性好的特点,在操作上是非常简单的。因此本系统可以进行开发。 3.1.3 经济可行性 腕表交易系统是基于B/S模式,采用MYSQL数据库储存数据,所要求的硬件和软件环境,市场上都很容易购买,程序开发主要是管理系统的开发和维护。所以程序在开发人力、财力上要求不高,而且此系统不是很复杂,开发周期短,在经济方面具有较高的可行性。 3.1.4 法律可行性 此腕表交易系统是自己设计的管理系统,具有很大的实际意义。开发环境软件和使用的数据库都是开源代码,因此对这个系统进行开发与普通的系统软件设计存在很大不同,没有侵权等问题,在法律上完全具有可行性。 综上所述,腕表交易系统在技术、经济、操作和法律上都具有很高的可行性,开发此程序是很必要的。 3.2 腕表交易系统功能需求分析 此基于SSM的腕表交易系统分前台功能和后台功能: 1)前台部分由用户使用,主要包括用户注册,腕表购物车管理,订单管理,个人资料管理,留言板管理 2)后台部分由管理员使用,主要包括管理员身份验证,商品管理,处理订单,用户信息管理,连接信息管理 3.3 数据库需求分析 数据库的设计通常是以一个已经存在的数据库管理系统为基础的,常用的数据库管理系统有MYSQL,SQL,Oracle等。我采用了Mysql数据库管理系统,建立的数据库名为db_business。 整个系统功能需要以下数据项: 用户:用户id、用户名称、登录密码、用户真实姓名、性别、邮箱地址、联系地址、联系电话、密码问题、答案、注册时间。 留言:主题id、作者姓名、Email、主题名称、留言内容、发布时间。 商品:商品id、名称、价格、图片路径、类型、简要介绍、存储地址、上传人姓名、发布时间、是否推荐。 订单:订单号、用户名、真实姓名、订购日期、Email、地址、邮编、付款方式、联系方式、运送方式、订单核对、其他。 管理员:管理员id、管理员名称、管理员密码。 公告:公告内容、公告时间。 4系统设计 4.1 系统功能模块设计 功能结构图如下: 图9 功能模块设计图 从图中可以看出,网上腕表交易系统可以分为前台和后台两个部分,前台部分由用户使用,主要包括用户注册,生成订单,腕表购物车管理,查看腕表购物车,查看留言,订购产品,订单查询和发布留言7个模块;本文转载自http://www.biyezuopin.vip/onews.asp?id=11975后台部分由管理员使用,主要包括管理员身份验证,商品管理,处理订单,用户信息管理,连接信息管理5个模块。 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"/><title>腕表商城</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><meta name="viewport" content="width=device-width, initial-scale=1"><!-- Favicon --><link rel="shortcut icon" type="image/x-icon" href="img/favicon.png"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/font-awesome.min.css" /><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/bootstrap.css" /><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/style.css"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/magnific-popup.css"><link rel="stylesheet" type="text/css" href="<%=basePath%>home/css/owl.carousel.css"><script type="text/javascript">function getprofenlei(){ var html = ""; $.ajax({url: "leixing.action?list&page=0&rows=30",type: "POST",async: false, contentType: "application/x-www-form-urlencoded;charset=UTF-8",success: function (data) { $.each(data.rows, function (i, val) { html += ' <li ><a href="home/search.jsp?fenlei='+val.id+'" >'+val.a1+' </a></li>';})} }); $("fenlei").html(html);}function gettop1(){var html = "";$.ajax({url: "leixing.action?list&page=0&rows=10",type: "POST",async: false,success: function (data) {var total='';//<div class="tab-pane active" id="nArrivals">// <div class="nArrivals owl-carousel" id="top1">$.each(data.rows, function (i, valmm) { html+='<div class="nArrivals owl-carousel" id="'+valmm.id+'">';$.ajax({url: "shangpin.action?list&page=0&rows=10",type: "POST",async: false,data: { fenlei:valmm.id },success: function (data) { $.each(data.rows, function (i, val) { html+='<div class="product-grid">'+'<div class="item">'+' <div class="product-thumb">'+' <div class="image product-imageblock"> <a href="home/details.jsp?ids='+val.id+'"> <img data-name="product_image" style="width:223px;height:285px;" src="<%=basePath%>'+val.tupian1+'" alt="iPod Classic" title="iPod Classic" class="img-responsive"> <img style="width:223px;height:285px;" src="<%=basePath%>'+val.tupian1+'" alt="iPod Classic" title="iPod Classic" class="img-responsive"> </a> </div>'+' <div class="caption product-detail text-left">'+' <h6 data-name="product_name" class="product-name mt_20"><a href="home/details.jsp?ids='+val.id+'" title="Casual Shirt With Ruffle Hem">'+val.biaoti+'</a></h6>'+' <div class="rating"> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-1x"></i></span> <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-1x"></i><i class="fa fa-star fa-stack-x"></i></span> </div>'+'<span class="price"><span class="amount"><span class="currencySymbol">$</span>'+val.jiage+'</span>'+'</span>'+'<div class="button-group text-center">'+' <div class="wishlist"><a href="home/details.jsp?ids='+val.id+'"><span>wishlist</span></a></div>'+'<div class="quickview"><a href="home/details.jsp?ids='+val.id+'"><span>Quick View</span></a></div>'+'<div class="compare"><a href="home/details.jsp?ids='+val.id+'"><span>Compare</span></a></div>'+'<div class="add-to-cart"><a href="home/details.jsp?ids='+val.id+'"><span>Add to cart</span></a></div>'+'</div>'+'</div>'+'</div>'+'</div>'+' </div>'; })html+='</div>'; } })}) $("nArrivals").html(html); } }); 本篇文章为转载内容。原文链接:https://blog.csdn.net/newlw/article/details/127608579。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-21 18:24:50
66
转载
转载文章
...) 输出通道1 配置PWM模式下的定时器(高电平有效) 端口为P5 初始占空比为100%clock = time.clock() 设置一个时钟用于追踪FPS 加载模型try:net = tf.load("trained.tflite", load_to_fb=uos.stat('trained.tflite')[6] > (gc.mem_free() - (641024)))except Exception as e:print(e)raise Exception('Failed to load "trained.tflite", did you copy the .tflite and labels.txt file onto the mass-storage device? (' + str(e) + ')') 加载标签try:labels = [line.rstrip('\n') for line in open("labels.txt")]except Exception as e:raise Exception('Failed to load "labels.txt", did you copy the .tflite and labels.txt file onto the mass-storage device? (' + str(e) + ')') 不断的进行运行while(True):clock.tick() 更新时钟img = sensor.snapshot().binary([(0,64)]) 抓取一张图像以灰度图显示lcd.display(img) 拍照并显示图像for obj in net.classify(img, min_scale=1.0, scale_mul=0.8, x_overlap=0.5, y_overlap=0.5): 初始化最大值和标签max_num = -1max_index = -1print("\nPredictions at [x=%d,y=%d,w=%d,h=%d]" % obj.rect())img.draw_rectangle(obj.rect()) 预测值和标签写成一个列表predictions_list = list(zip(labels, obj.output())) 输出各个标签的预测值,找到最大值进行输出for i in range(len(predictions_list)):print('%s 的概率为: %f' % (predictions_list[i][0], predictions_list[i][1]))if predictions_list[i][1] > max_num:max_num = predictions_list[i][1]max_index = int(predictions_list[i][0])run(max_index)print('该数字预测为:%d' % max_index)print('FPS为:', clock.fps())print('PWM波占空比为: %d%%' % (max_index10)) 2. 采用器件 使用的器件为OpenMV4 H7 Plus和L298N以及常用的直流电机。关键是找到器件的引脚图,再进行简单的连线即可。 参考文章:【L298N驱动模块学习笔记】–openmv驱动 参考文章:【openmv】原理图 引脚图 2. 注意事项 上述代码中我用到了lcd屏幕,主要是为了方便离机操作。使用过程中,OpenMV的lcd初始化时会重置端口,所有我们在输出PWM波的时候一定不要发生引脚冲突。我们可以在OpenMV官网查看lcd用到的端口: 可以看到上述用到的是P0、P2、P3、P6、P7和P8。所有我们输出PWM波时要避开这些端口。下面是OpenMV的PWM资源: 总结 本人第一次自己做东西也是第一次使用python,所以代码和项目写的都很粗糙,只是简单的识别数字控制直流电机。我也是四处借鉴修改后写下的大小,这篇文章主要是为了给那些像我一样的小白们提供一点帮助,减少大家查找资料的时间。模型的缺陷以及改进方法上述中已经说明,如果我有写错或者大家有更好的方法欢迎大家告诉我,大家一起进步! 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_57100435/article/details/130740351。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-01-10 08:44:41
282
转载
Golang
...术特性,还在于它重新定义了开发者的工作方式,使其更加专注于业务逻辑而非底层实现细节。 展望未来,随着5G、物联网和人工智能等新技术的兴起,Golang有望在更多领域大放异彩。无论是边缘计算、大数据处理还是实时数据分析,Golang都展现出了巨大的潜力。正如Google Go团队负责人Robert Griesemer所说:“Golang的目标始终是让开发者能够更快、更好地完成工作。”这种理念无疑将继续引领技术发展的潮流。
2025-04-23 15:46:59
39
桃李春风一杯酒
转载文章
...isor – 安装和配置 提示:一台服务器首选ESXI 九、KVM安装 调整虚拟机 虚拟化Intel使用的是Intel VT-X ; 虚拟化AMD使用的是AMD-V 创建虚拟机步骤 1.准备虚拟机硬盘 2.需要系统iso镜像3.需要安装一个vnc的客户端来连接 查看系统环境 [root@linux-node1 ~] cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [root@linux-node1 ~] uname -r 3.10.0-327.36.2.el7.x86_64 检查是否有vmx或者svm [root@linux-node1 ~] grep -E '(vmx|svm)' /proc/cpuinfo 安装kvm用户态模块 [root@linux-node1 ~] yum list|grep kvm libvirt-daemon-kvm.x86_64 1.2.17-13.el7_2.5 updates pcp-pmda-kvm.x86_64 3.10.6-2.el7 base qemu-kvm.x86_64 10:1.5.3-105.el7_2.7 updates qemu-kvm-common.x86_64 10:1.5.3-105.el7_2.7 updates qemu-kvm-tools.x86_64 10:1.5.3-105.el7_2.7 updates [root@linux-node1 ~] yum install qemu-kvm qemu-kvm-tools libvirt -y libvirt 用来管理kvm kvm属于内核态,不需要安装。但是需要一些类似于依赖的 kvm属于内核态,不需要安装。但是需要安装一些类似于依赖的东西 启动 [root@linux-node1 ~] systemctl start libvirtd.service [root@linux-node1 ~] systemctl enable libvirtd.service 启动之后我们可以使用ifconfig进行查看,libvirtd已经为我们安装了一个桥接网卡 libvirtd为我们启动了一个dnsmasqp,这个主要是用来dhcp连接的,这个工具会给我们的虚拟机分配IP地址 [root@linux-node1 ~] ps -ef|grep dns nobody 5233 1 0 14:27 ? 00:00:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper root 5234 5233 0 14:27 ? 00:00:00 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelperoot 5310 2783 0 14:31 pts/0 00:00:00 grep --color=auto dns 查看磁盘空间大小 最好是20G以上 [root@linux-node1 tmp] df -h 上传镜像 提示:如果使用rz上传镜像可能会出现错误,所以我们使用dd命令,复制系统的镜像。只需要挂载上光盘即可 [root@linux-node1 opt] dd if=/dev/cdrom of=/opt/CentOS-7.2.iso [root@linux-node1 opt] ll total 33792 -rw-r--r-- 1 root root 34603008 Jun 12 18:18 CentOS-7.2-x86_64-DVD-1511.iso 下载VNC 下载地址:http://www.tightvnc.com/download/2.8.5/tightvnc-2.8.5-gpl-setup-64bit.msi 安装完VNC如下图 创建磁盘 提示: qemu-img软件包是我们安装qemu-kvm-tools 依赖给安装上的 [root@linux-node1 opt] qemu-img create -f raw /opt/CentOS-7.2-x86_64.raw 10GFormatting '/opt/Centos-7-x86_64.raw', fmt=raw size=10737418240 [root@linux-node1 opt] [root@linux-node1 opt] ll /opt/Centos-7-x86_64.raw -rw-r--r-- 1 root root 10737418240 Oct 26 14:53 /opt/Centos-7-x86_64.raw-f 制定虚拟机格式,raw是裸磁盘/opt/Centos 存放路径 10G 代表镜像大小 安装启动虚拟机的包 [root@linux-node1 tmp] yum install -y virt-install 安装虚拟机 [root@linux-node1 tmp] virt-install --help 我们可以指定虚拟机的CPU、磁盘、内存等 [root@linux-node1 opt] virt-install --name CentOS-7.2-x86_64 --virt-type kvm --ram 1024 --cdrom=/opt/CentOS-7.2.iso --disk path=/opt/CentOS-7.2-x86_64.raw --network network=default --graphics vnc,listen=0.0.0.0 --noautoconsole --name = 给虚拟机起个名字 --ram = 内存大小 --cdrom = 镜像位置,就是我们上传iso镜像的位置,我放在/tmp下了 --disk path = 指定磁盘--network network= 网络配置 default 就会用我们刚刚ifconfig里面桥接的网卡--graphics vnc,listen= 监听vnc, 分区说明 提示:我们不分交换分区,因为公有云上的云主机都是没有交换分区的 十、Libvirt介绍 libvirt是一个开源免费管理工具,可以管理KVM、VMware等 他需要起一个后台的进程,它提供了API。像openstack就是通过libvirt API来管理虚拟机 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcp4lgAZ-1596980494935)(libvirt.jpg)] 二、KVM虚拟机和VMware区别 虚拟机监控程序(KVM)是虚拟化平台的根基。从传统供应商到各种开源替代品,可供选择的虚拟机监控程序有很多。 VMware 是一款实现虚拟化的热门产品,可以提供 ESXi 虚拟机监控程序和 vSphere 虚拟化平台。 基于内核的虚拟机(KVM)则是 Linux® 系统上的一种开源解决方案。 VMware vSphere 与 VMware ESXi VMware 可以提供 ESXi 虚拟机监控程序和 vSphere 虚拟化平台。VMware ESXi 是一个能够直接安装到物理服务器上的裸机虚拟机监控程序,可以帮你整合硬件。你可以用 VMware 的虚拟化技术来创建和部署虚拟机(VM),从而现代化改造自己的基础架构,来交付和管理各种新旧应用。 选用 VMware vSphere 后,你需要使用 VMware 的控制堆栈来管理虚拟机,而且有多个许可证授权级别可供使用。 KVM 开源虚拟化技术 KVM 是一种开源虚拟化技术,能将 Linux 内核转变成可以实现虚拟化的虚拟机监控程序,而且可以替代专有的虚拟化技术(比如 VMware 提供的专有虚拟化技术)。 迁移到基于 KVM 的虚拟化平台,你就可以检查、修改和完善虚拟机监控程序背后的源代码。能够访问源代码,就如同掌握了开启无限可能的钥匙,能够让你虚拟化传统工作负载和应用,并为云原生和基于容器的工作负载奠定基础。由于 KVM 内置于 Linux 内核中,所以使用和部署起来非常方便。 KVM 虚拟机和 VMware vSphere 的主要区别 VMware 可以提供一个完善稳定的虚拟机监控程序,以及出色的性能和多样化的功能。但是,专有虚拟化会阻碍你获得开展云、容器和自动化投资所需的资源。解除供应商锁定,你就可以任享自由、灵活与丰富的资源,从而为未来的云原生和容器化环境打下基础。 生产就绪型的 KVM 具有支持物理和虚拟基础架构的功能,可以让你以更低的运营成本为企业工作负载提供支持。相比使用 VMware vSphere 等其他解决方案,选用基于 KVM 的虚拟化选项能够带来很多优势。 开源Linux KVM的优势: 更低的总拥有成本,从而省下运营预算,用来探索现代化创新技术。 不再受供应商捆绑。无需为不用的产品付费,也不会受到软件选择限制。 跨平台互操作性:KVM 可以在 Linux 和 Windows 平台上运行,所以你可以充分利用现有的基础架构投资。 出色简便性:可以通过单个虚拟化平台,在数百个其他硬件或软件上创建、启动、停止、暂停、迁移和模板化数百个虚拟机。 卓越性能:应用在 KVM 上的运行速度比其他虚拟机监控程序都快。 开源优势:不但能访问源代码,还能灵活地与各种产品集成。 享受 Linux 操作系统的现有功能: 安全防护功能 内存管理 进程调度器 设备驱动程序 网络堆栈 红帽 KVM 企业级虚拟化的优势 选择红帽® 虚拟化,就等于选择了 KVM。红帽虚拟化是一款适用于虚拟化服务器和技术工作站的完整基础架构解决方案。红帽虚拟化基于强大的红帽企业 Linux® 平台和 KVM 构建而成,能让你轻松、敏捷、安全地使用资源密集型虚拟化工作负载。红帽虚拟化可凭借更加优越的性能、具有竞争力的价格和值得信赖的红帽环境,帮助企业优化 IT 基础架构。 红帽的虚拟化产品快速、经济、高效,能够帮助你从容应对当前的挑战,并为未来的技术发展奠定基础。VMware 等供应商提供的纵向扩展虚拟化解决方案不但成本高昂,而且无法帮助企业完成所需的转型,因而难以支持在混合云中运行云原生应用。要转而部署混合云环境,第一步要做的就是摆脱专有虚拟化。 红帽虚拟化包含 sVirt 和安全增强型 Linux(SELinux),是红帽企业 Linux 专为检测和预防当前 IT 环境中的复杂安全隐患而开发的技术。 业完成所需的转型,因而难以支持在混合云中运行云原生应用。要转而部署混合云环境,第一步要做的就是摆脱专有虚拟化。 红帽虚拟化包含 sVirt 和安全增强型 Linux(SELinux),是红帽企业 Linux 专为检测和预防当前 IT 环境中的复杂安全隐患而开发的技术。 借助红帽虚拟化,你可以尽享开源虚拟机监控程序的所有优势,还能获得企业级技术支持、更新和补丁,使你的环境保持最新状态,持续安心运行。开源和 RESTful API,以及 Microsoft Windows 的认证,可帮你实现跨平台的互操作性。提供的 API 和软件开发工具包(SDK)则有助于将我们的解决方案扩展至你现有和首选管理工具,并提供相关支持。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_34799070/article/details/107900861。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-06 08:58:59
121
转载
转载文章
...让你演示调试,结束后打包发送到一个邮箱里。 网数只有机试和面试,13号上午机试,15号面试。机试一个小时七道题目,在自己电脑上写然后拷到老师的优盘上。考察了包括链表、二叉树、图等,偏向于工程,据说今年的题目是计算所一个工程博士出的。机试70人,进入面试60人。面试每人15分钟,包括自我介绍,专业知识,是否读博,项目等。 计算所环境 八、一些建议和感想 一些建议: 提前准备,给自己定位,有针对性的准备,多在网上找经验贴;多和本校保研的学长学姐交流,多和同学交流,多搜集信息; 4月份前把简历、推荐信、个人陈述等写好,再不断修改完善; 最好能提前联系一个老师,以免拿到优营而没有找到好老师; 准备好专业知识,线代、概率论、数据结构、计网、计组、操作系统等; 如果编程能力不是特别强,最好大三开始就刷题,LeetCode的中档题难度基本就够用了; 一些体会与感想: 机会是留给有准备的人的,越努力越幸运! 做最坏的打算,做最好的准备。 保研是一场马拉松,坚持到底就是胜利。 遵道而行,但到半途需努力;会心不远,欲登绝顶莫辞劳。 也送给自己一句话:流年笑掷,未来可期! 以上仅代表个人观点与感想,如果对你有帮助记得点赞哦~如有问题,可以关注我的公主号【驭风者小窝】,我会尽我最大的努力帮助你! 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_28983299/article/details/118319985。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-02 23:03:36
120
转载
转载文章
...”的人。 软件设计和定义过程中存在很多取舍,例如: 完善功能和尽早发布的取舍。 伸缩性和性能的取舍。 著名的 CAP 原则,就是一个很好的取舍指导策略。为了更好的取舍,保持架构风格的一致性,在一开始架构师就应该根据系统的实际需求来定义一些取舍的原则,如: 数据一致性拥有最高优先级。 提前发布核心功能优于完整发布等。 非功能性需求决定架构 因为软件是为了满足客户的功能性需求的,所以很多设计人员可能会认为架构是由要实现的功能性需求决定的。但实际上真正决定软件架构的其实是非功能性需求。 架构师要更加关注非功能性需求,常见的非功能性包括:性能,伸缩性,扩展性和可维护性等,甚至还包括团队技术水平和发布时间要求。能实现功能的设计总是有很多,考虑了非功能性需求后才能筛选出最合适的设计。 以上架构模式来自《面向模式的软件架构》的第一卷,这套书多年来一直是架构师的必读经典。面向架构的模式就是为不同的非功能性需求提供了很好的参考和指导。图中的 Micro-Kernel 模式,更加关注可扩展性和可用性(错误隔离)。 “简单”并不“容易” 很多架构师都会常常提到保持简单,但是有时候我们会混淆简单和容易。简单和容易在英语里也是两个词“simple”和“easy”。 “Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it’s worth it in the end because once you get there, you can move mountains. To be truly simple, you have to go really deep.” –SteveJobs 真正的一些简单的方法其实来自于对问题和技术更深入的理解。这些方案往往不是容易获得的、表面上的方法。简单可以说蕴含着一种深入的技巧在其中。 下面我来举一个例子。 首先我们来回顾一下软件生命周期中各个阶段的成本消耗占比。以下是来一个知名统计机构的分析报告。我们可以看到占比最大的是维护部分,对于这一部分的简化将最具有全局意义。 我曾经开发过一个设备管理系统,移动运营商通过这个系统来管理移动设备,实现包括设备的自动注册、固件和软件的同步等管理功能。这些功能是通过一些管理系统与移动设备间的预定义的交互协议来完成的。 电信专家们会根据业务场景及需求来调整和新增这些交互协议。起初我们采用了一种容易实现的方式,即团队中的软件工程会根据电信专家的说明,将协议实现为对应代码。 之后我们很快发现这样的方式,让我们的工作变得没那么简单。 “I believe that the hardest part of software projects, the most common source of project failure, is communication with the customers and users of that software.” –Martin Fowler 正如软件开发大师 MartinFowler 提到的,“沟通”往往是导致软件项目失败的主要原因。前面这个项目最大的问题是在系统上线后的运行维护阶段,电信专家和开发工程师之间会不断就新的协议修改和增加进行持续的沟通,而他们的领域知识和词汇都有很大的差别,这会大大影响沟通的效率。因此这期间系统的运行维护(协议的修改)变得十分艰难,不仅协议更新上线时间慢,而且由于软件工程对于电信协议理解程度有限,很多问题都要在实际上线使用后才能被电信专家发现,导致了很多的交换和反复。 针对上面提到的问题,后来我们和电信专家一起设计了一种协议设计语言(并提供可视化的工具),这种设计语言使用的电信专家所熟悉的词汇。然后通过一个类似于编译器的程序将电信专家定义好的协议模型转换为内存中的 Java 结构。这样整个项目的运行和维护就变得简单高效了,省去了低效的交流和不准确人工转换。 我们可以看到一开始按电信专家的说明直接实现协议是更为容易的办法,但就整个软件生命周期来看却并不是一个简单高效的方法。 永远不要停止编码 架构师也是程序员,代码是软件的最终实现形态,停止编程会逐渐让你忘记作为程序员的感受,更重要的是忘记其中的“痛”,从而容易产生一些不切实际的设计。 大家可能听说过在 Amazon,高级副总裁级别的 Distinguish Engineer(如:James Gosling,Java 之父),他们每年的编码量也非常大,常在 10 万行以上。 风险优先 架构设计很重要的一点是识别可能存在的风险,尤其是非功能性需求实现的风险。因为这些风险往往没有功能性需求这么容易在初期被发现,但修正的代价通常要比修正功能性需求大非常多,甚至可能导致项目的失败,前面我们也提到了非功能性需求决定了架构,如数据一致性要求、响应延迟要求等。 我们应该通过原型或在早期的迭代中确认风险能够通过合理的架构得以解决。 绝对不要把风险放到最后,就算是一个项目要失败也要让它快速失败,这也是一种敏捷。 从“问题”开始,而不是“技术” 技术人员对于新技术的都有着一种与身俱来的激情,总是乐于去学习新技术,同时也更有激情去使用新技术。但是这也同样容易导致一个通病,就是“当我们有一个锤子的时候看什么都是钉子”,使用一些不适合的技术去解决手边的问题,常常会导致简单问题复杂化。 我曾经的一个团队维护过这样一个简单的服务,起初就是一个用 MySQL 作数据存储的简单服务,由团队的一个成员来开发和维护。后来,这位成员对当时新出的 DynamoDB 产生了兴趣,并学习了相关知识。 然后就发生下面这样的事: 用DynamoDB替换了MySQL。 很快发现DynamoDB并不能很好的支持事务特性,在当时只有一个性能极差的客户端类库来支持事物,由于采用客户端方式,引入了大量的额外交互,导致性能差别达7倍之多。这时候,这个同学就采用了当时在NoSQL领域广泛流行的最终一致技术,通过一个Pub-Sub消息队列来实现最终一致(即当某对象的值发生改变后会产生一个事件,然后关注这一改变的逻辑,就会订阅这个通知,并改变于其相关数据,从而实现不同数据的最终一致)。 接着由于DynamoDB无法提供SQL那样方便的查询机制,为了实现数据分析就又引入了EMR/MapReduceJob。 到此,大家可以看到实现一样的功能,但是复杂性大大增加,维护工作也由一个人变成了一个团队。 过度忙碌使你落后 对于 IT 人而言忙碌已成为了习惯,加班常挂在嘴边。“996”工作制似乎也变成了公司高效的标志。而事实上过度的忙碌使你落后。经常遇见一些朋友,在一个公司没日没夜的干了几年,没有留一点学习时间给自己。几年之后倒是对公司越来越“忠诚”了,但忙碌的工作同时也导致了没有时间更新知识,使得自己已经落后了,连跳槽的能力和勇气都失去了。 过度忙碌会导致没有时间学习和更新自己的知识,尤其在这个高速发展的时代。我在工作经历中发现过度繁忙通常会带来以下问题: 缺乏学习导致工作能力没有提升,而面对的问题却变得日益复杂。 技术和业务上没有更大的领先优势,只能被动紧紧追赶。试想一下,要是你都领先同行业五年了,还会在乎通过加班来早一个月发布吗? 反过来上面这些问题会导致你更加繁忙,进而更没有时间提高自己的技术技能,很快就形成了一个恶性循环。 练过健身的朋友都知道,光靠锻炼是不行的,营养补充和锻炼同样重要。个人技术成长其实也一样,实践和学习是一样重要的,当你在一个领域工作了一段时间以后,工作对你而言就主要是实践了,随着你对该领域的熟悉,能学习的到技术会越来越少。所以每个技术人员都要保证充足的学习时间,否则很容易成为井底之蛙,从而陷入前面提到的恶性循环。 最后,以伟大诗人屈原的诗句和大家共勉:“路漫漫其修远兮,吾将上下而求索“。希望我们大家都可以不忘初心,保持匠心! 作者简介: 蔡超,Mobvista 技术 VP 兼首席架构师,SpotMax 云服务创始人。拥有超过 15 年的软件开发经验,其中 9 年任世界级 IT 公司软件架构师/首席软件架构师。2017 年加入 Mobvista,任公司技术副总裁及首席架构师,领导公司的数字移动营销平台的开发,该平台完全建立于云计算技术之上,每天处理来自全球不同 region 的超过 600 亿次的请求。 在加入 Mobvista 之前,曾任亚马逊全球直运平台首席架构师,亚马逊(中国)首席架构师,曾领导了亚马逊的全球直运平台的开发,并领导中国团队通过 AI 及云计算技术为中国客户打造更好的本地体验;曾任 HP(中国)移动设备管理系统首席软件架构师,该系统曾是全球最大的无线设备管理系统(OMA DM)(客户包括中国移动,中国联通,中国电信等);曾任北京天融信网络安全技术公司,首席软件架构师,领导开发的网络安全管理系统(TopAnalyzer)至今仍被政府重要部门及军队广为采用,该系统也曾成功应用于 2008 北京奥运,2010 上海世博等重要事件的网络安全防护。 本篇文章为转载内容。原文链接:https://blog.csdn.net/Honnyee/article/details/111896981。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-19 14:55:26
78
转载
转载文章
...擎,它将应用及其依赖打包成独立可移植的容器,实现了应用程序及其环境的一次构建、到处运行的效果,极大地简化了开发、测试和部署流程。在文中,作者提到了随着Docker的广泛应用,底层网络工作方式发生变化,并开始研究网络虚拟化相关技术。 协同算法 , 协同算法是一种通过分析用户行为、兴趣偏好等信息,实现个性化推荐或优化某种目标的技术。文中提及作者在搜狗手机助手项目中,采用用户协同、标签相似、点击反馈等方法,提升了搜索转化率,这里的“协同算法”就是一种利用用户间关联性进行优化的算法。
2023-02-06 11:38:24
232
转载
转载文章
...xOrthoRH函数定义投影矩阵。但是,要小心使用D3DXMatrixLookAtRH函数,并相应地颠倒背面剔除的顺序及放置立方体贴图。 虽然左手坐标系和右手坐标系是最为常用的系统,但在三维软件中还使用许多其它坐标系。例如,对三维建模应用程序而言,使用y轴指向或背向观察者的坐标系统并非罕见。在这种情况下,任意轴(x,y或z)的正半轴指向观察者的被定义为右手系。任意轴(x,y或z)的正半轴背向观察者的被定义为左手系。如果正在移植一个基于左手系进行建模的应用程序,z轴向上,那么除了前面的步骤外,还必须旋转所有的顶点数据(译注:如果原来的坐标系为正x轴向里,正y轴向左,正z轴向上,那么传给Direct3D的顶点的x值对应原来的y值,y值对应原来的z值,z值对应原来的x值,亦即旋转顶点数据)。 对三维坐标系统中定义的三维物体执行的最基本操作是变换、旋转和缩放。可以合并这些基本变换以创建一个新的变换矩阵。细节请参阅三维变换。 即使合并相同的变换操作,不同的合并顺序得到的结果是不可交换的——矩阵相乘的顺序很重要。 三维图元 三维图元是组成单个三维实体的顶点集合。三维坐标系统中最简单的图元是点的集合,称为点表。 通常三维图元是多边形。一个多边形是由至少三个顶点描绘的三维形体。最简单的多边形是三角形。Microsoft® Direct3D®使用三角形组成大多数多边形,因为三角形的三个顶点一定是共面的。应用程序可以用三角形组合成大而复杂的多边形及网格(mesh)。 下图显示了一个立方体。立方体的每个面由两个三角形组成。整个三角形的集合构成了一个立方体图元。可以将纹理和材质应用于图元的表面使它们看起来像是实心的。 可以使用三角形创建具有光滑曲面的图元。下图显示了如何用三角形模拟一个球体。应用了材质后,渲染得到的球体看起来是弯曲的。如果使用高洛德着色,结果更是如此。更多信息请参阅高洛德着色。 表面和顶点法向量 网格中的每个面有一个垂直的法向量。该向量的方向由定义顶点的顺序及坐标系统是左手系还是右手系决定。表面法向量从表面上指向正向面那一侧,如果把表面水平放置,正向面朝上,背向面朝下,那么表面法向量为垂直于表面从下方指向上方。在Microsoft® Direct3D®中,只有面的正向是可视的。一个正向面是顶点按照顺时针顺序定义的面。 任何不是正向面的面都是背向面。由于Direct3D不总是渲染背向面,因此背向面要被剔除。如果想要渲染背向面的话,可以改变剔除模式。更多信息请参阅剔除状态。 Direct3D在计算高洛德着色、光照和纹理效果时使用顶点法向。 Direct3D使用顶点法向计算光源和表面间的夹角,对多边形进行高洛德着色。Direct3D计算每个顶点的颜色和亮度值,并对图元表面所覆盖的所有像素点进行插值。Direct3D使用夹角计算光强度,夹角越大,表面得到的光照就越少。 如果正在创建的物体是平直的,可将顶点法向设为与表面垂直,如下图所示。该图定义了一个由两个三角形组成的平直表面。 但是,更可能的情况是物体由三角形带(triangle strips)组成且三角形不共面。要对整个三角形带的三角形平滑着色的一个简单方法是首先计算与顶点相关联的每个多边形表面的表面法向量。可以这样计算顶点法向,使顶点法向与顶点所属的每个表面的法向的夹角相等。但是,对复杂图元来说这种方法可能不够有效。 这种方法如下图所示。图中有两个表面,S1与S2,它们的邻边在上方。S1与S2的法向量用蓝色显示。顶点的法向量用红色显示。顶点法向量与S1表面法向的夹角和顶点法向量与S2表面法向的夹角相同。当对这两个表面进行光照计算和高洛德着色时,得到结果是中间的边被平滑着色,看起来像是弧形的(而不是有棱角的)。 如果顶点法向偏向与它相关联的某个面,那么会导致那个面上的点光强度的增加或减少。下图显示了一个例子。这些面的邻边依然朝上。顶点法向倾向S1,与顶点法向与表面法向有相同的夹角相比,这使顶点法向与光源间的夹角变小。 可以用高洛德着色在三维场景中显示一些有清晰边缘的物体。要达到这个目的,只要在需要产生清晰边缘的表面交线处,把表面法向复制给交线处顶点的法向,如下图所示。 如果使用DrawPrimitive方法渲染场景,要将有锋利边缘的物体定义为三角形表,而非三角形带。当将物体定义为三角形带时,Direct3D会将它作为由多个三角形组成的单个多边形处理。高洛德着色被同时应用于多边形每个表面的内部和表面之间。结果产生表面之间平滑着色的物体。因为三角形表由一系列不相连的三角形面组成,所以Direct3D对多边形每个面的内部使用高洛德着色。但是,没有在表面之间应用高洛德着色。如果三角形表的两个或更多的三角形是相邻的,那么在它们之间看起来会有一条锋利边缘。 另一种可选的方法是在渲染具有锋利边缘的物体时改变到平面着色模式。这在计算上是最有效的方法,但它可能导致场景中的物体不如用高洛德着色渲染的物体真实。 三角形光栅化法则 顶点指定的点经常不能精确地对应到屏幕上的像素。此时,Microsoft® Direct3D®使用三角形光栅化法则决定对于给定三角形使用哪个像素。 三角形光栅化法则 点、线光栅化法则 点精灵光栅化法则 三角形光栅化法则 Direct3D在填充几何图形时使用左上填充约定(top-left filling convention)。这与Microsoft Windows®的图形设备接口(GUI)和OpenGL中的矩形使用的约定相同。Direct3D中,像素的中心是决定点。如果中心在三角形内,那么该像素就是三角形的一部分。像素中心用整数坐标表示。 这里描述的Direct3D使用的三角形光栅化法则不一定适用于所有可用的硬件。测试可以发现这些法则的实现间的细微变化。 下图显示了一个左上角为(0,0),右下角为(5,5)的矩形。正如大家想象的那样,此矩形填充25个像素。矩形的宽度由right减left定义。高度由bottom减top定义。 在左上填充约定中,上表示水平span在垂直方向上的位置,左表示span中的像素在水平方向上的位置。一条边除非是水平的,否则不可能是顶边——一般来说,大多数三角形只有左边或右边。 左上填充约定确定当一个三角形穿过像素的中心时Direct3D采取的动作。下图显示了两个三角形,一个在(0,0),(5,0)和(5,5),另一个在(0,5),(0,0)和(5,5)。在这种情况下第一个三角形得到15个像素(显示为黑色),而第二个得到10个像素(显示为灰色),因为公用边是第一个三角形的左边。 如果应用程序定义一个左上角为(0.5,0.5),右下角为(2.5,4.5)的矩形,那么这个矩形的中心在(1.5,2.5)。当Direct3D光栅化器tessellate这个矩形时,每个像素的中心都毫无异义地分别位于四个三角形中,此时就不需要左上填充约定。下图显示了这种情况。矩形内的像素根据在Direct3D中被哪个三角形包含做了相应的标注。 如果将上例中的矩形移动,使之左上角为(1.0,1.0),右下角为(3.0,5.0),中心为(2.0,3.0),那么Direct3D使用左上角填充约定。这个矩形中大多数的像素跨越两个或更多的三角形的边界,如下图所示。 这两个矩形会影响到相同的像素。 点、线光栅化法则 点和点精灵一样,都被渲染为与屏幕边缘对齐的四边形,因此它们使用与多边形同样的渲染法则。 非抗锯齿线段的渲染法则与GDI使用的法则完全相同。 更多有关抗锯齿线段的渲染,请参阅ID3DXLine。 点精灵光栅化法则 对点精灵和patch图元的渲染,就好像先把图元tessellate成三角形,然后将得到的三角形进行光栅化。更多信息,请参阅点精灵。 矩形 贯穿Microsoft® Direct3D®和Microsoft Windows®编程,都是用术语包围矩形来讨论屏幕上的物体。由于包围矩形的边总是与屏幕的边平行,因此矩形可以用两个点描述,左上角和右下角。当在屏幕上进行位块传输(Blit = Bit block transfer)或命中检测时,大多数应用程序使用RECT结构保存包围矩形的信息。 C++中,RECT结构有如下定义。 typedef struct tagRECT { LONG left; // 这是左上角的x坐标。 LONG top; // 这是左上角的y坐标。 LONG right; // 这是右下角的x坐标。 LONG bottom; // 这是右下角的y坐标。 } RECT, PRECT, NEAR NPRECT, FAR LPRECT; 在上例中,left和top成员是包围矩形左上角的x-和y-坐标。类似地,right和bottom成员组成右下角的坐标。下图直观地显示了这些值。 为了效率、一致性及易用性, Direct3D所有的presentation函数都使用矩形。 三角形插值对象(interpolants) 在渲染时,流水线会贯穿每个三角形的表面进行顶点数据插值。有五种可能的数据类型可以进行插值。顶点数据可以是各种类型的数据,包括(但不限于):漫反射色、镜面反射色、漫反射阿尔法(三角形透明度)、镜面反射阿尔法、雾因子(固定功能流水线从镜面反射的阿尔法分量中取得,可编程顶点流水线则从雾寄存器中取得)。顶点数据通过顶点声明定义。 对一些顶点数据的插值取决于当前的着色模式,如下表所示。 着色模式 描述 平面 在平面着色模式下只对雾因子进行插值。对所有其它的插值对象,整个面都使用三角形第一个顶点的颜色。 高洛德 在所有三个顶点间进行线性插值。 根据不同的颜色模型,对漫反射色和镜面反射色的处理是不同的。在RGB颜色模型中,系统在插值时使用红、绿和蓝颜色分量。 颜色的阿尔法成员作为单独的插值对象对待,因为设备驱动程序可以以两种不同的方法实现透明:使用纹理混合或使用点画法(stippling)。 可以用D3DCAPS9结构的ShadeCaps成员确定设备驱动程序支持何种插值。 向量、顶点和四元数 贯穿Microsoft® Direct3D®,顶点用于描述位置和方向。图元中的每个顶点由指定其位置的向量、颜色、纹理坐标和指定其方向的法向量描述。 四元数给三元素向量的[ x, y, z]值增加了第四个元素。用于三维旋转的方法,除了典型的矩阵以外,四元数是另一种选择。四元数表示三维空间中的一根轴及围绕该轴的一个旋转。例如,一个四元数可能表示轴(1,1,2)和1度的旋转。四元数包含了有价值的信息,但它们真正的威力源自可对它们执行的两种操作:合成和插值。 对四元数进行插值与合成它们类似。两个四元数的合成如下表示: 将两个四元数的合成应用于几何体意味着“把几何体绕axis2轴旋转rotation2角度,然后绕axis1轴旋转rotation1角度”。在这种情况下,Q表示绕单根轴的旋转,该旋转是先后将q2和q1应用于几何体的结果。 使用四元数,应用程序可以计算出一条从一根轴和一个方向到另一根轴和另一个方向的平滑、合理的路径。因此,在q1和q2间插值提供了一个从一个方向变化到另一个方向的简单方法。 当同时使用合成与插值时,四元数提供了一个看似复杂而实际简单的操作几何体的方法。例如,设想我们希望把一个几何体旋转到某个给定方向。我们已经知道希望将它绕axis2轴旋转r2度,然后绕axis1轴旋转r1度,但是我们不知道最终的四元数。通过使用合成,我们可以在几何体上合成两个旋转并得到最终单个的四元数。然后,我们可以在原始四元数和合成的四元数间进行插值,得到两者之间的平滑转换。 Direct3D扩展(D3DX)工具库包含了帮助用户使用四元数的函数。例如,D3DXQuaternionRotationAxis函数给一个定义旋转轴的向量增加一个旋转值,并在由D3DXQUTERNION结构定义的四元数中返回结果。另外,D3DXQuaternionMultiply函数合成四元数,D3DXQuaternionSlerp函数在两个四元数间进行球面线性插值(spherical linear interpolation)。 Direct3D应用程序可以使用下列函数简化对四元数的使用。 D3DXQuaternionBaryCentric D3DXQuaternionConjugate D3DXQuaternionDot D3DXQuaternionExp D3DXQuaternionIdentity D3DXQuaternionInverse D3DXQuaternionIsIdentity D3DXQuaternionLength D3DXQuaternionLengthSq D3DXQuaternionLn D3DXQuaternionMultiply D3DXQuaternionNormalize D3DXQuaternionRotationAxis D3DXQuaternionRotationMatrix D3DXQuaternionRotationYawPitchRoll D3DXQuaternionSlerp D3DXQuaternionSquad D3DXQuaternionToAxisAngle Direct3D应用程序可以使用下列函数简化对三成员向量的使用。 D3DXVec3Add D3DXVec3BaryCentric D3DXVec3CatmullRom D3DXVec3Cross D3DXVec3Dot D3DXVec3Hermite D3DXVec3Length D3DXVec3LengthSq D3DXVec3Lerp D3DXVec3Maximize D3DXVec3Minimize D3DXVec3Normalize D3DXVec3Project D3DXVec3Scale D3DXVec3Subtract D3DXVec3Transform D3DXVec3TransformCoord D3DXVec3TransformNormal D3DXVec3Unproject D3DX工具库提供的数学函数中包含了许多辅助函数,可以简化对二成员和四成员向量的使用 http://www.gesoftfactory.com/developer/3DCS.htm 本篇文章为转载内容。原文链接:https://blog.csdn.net/okvee/article/details/3438011。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-24 12:49:42
271
转载
转载文章
...后调用设备驱动程序中定义的mmap函数。 2.mmap系统调用 mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。 当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注意,直接对该段内存写时不会写入超过当前文件大小的内容. 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。 基于文件的映射,在mmap和munmap执行过程的任何时刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情况下没有得到更新,首次对映射区的第一个页索引时会更新该字段的值。用PROT_WRITE 和 MAP_SHARED标志建立起来的文件映射,其st_ctime 和 st_mtime在对映射区写入之后,但在msync()通过MS_SYNC 和 MS_ASYNC两个标志调用之前会被更新。 用法: include <sys/mman.h> void mmap(void start, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void start, size_t length); 返回说明: 成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void )-1],munmap返回-1。errno被设为以下的某个值 EACCES:访问出错 EAGAIN:文件已被锁定,或者太多的内存已被锁定 EBADF:fd不是有效的文件描述词 EINVAL:一个或者多个参数无效 ENFILE:已达到系统对打开文件的限制 ENODEV:指定文件所在的文件系统不支持内存映射 ENOMEM:内存不足,或者进程已超出最大内存映射数量 EPERM:权能不足,操作不允许 ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志 SIGSEGV:试着向只读区写入 SIGBUS:试着访问不属于进程的内存区 参数: start:映射区的开始地址。 length:映射区的长度。 prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起 PROT_EXEC //页内容可以被执行 PROT_READ //页内容可以被读取 PROT_WRITE //页可以被写入 PROT_NONE //页不可访问 flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体 MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。 MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。 MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。 MAP_DENYWRITE //这个标志被忽略。 MAP_EXECUTABLE //同上 MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。 MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。 MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。 MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。 MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。 MAP_FILE //兼容标志,被忽略。 MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。 MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。 MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。 fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。 offset:被映射对象内容的起点。 3.munmap系统调用 include <sys/mman.h> int munmap( void addr, size_t len ) 该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。 4.msync系统调用 include <sys/mman.h> int msync ( void addr , size_t len, int flags) 一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。 二 系统调用mmap()用于共享内存的两种方式 (1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下: [cpp] view plaincopy fd=open(name, flag, mode); if(fd<0) ... ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通过mmap()实现共享内存的通信方式有许多特点和要注意的地方 (2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。 对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可. 三 mmap进行内存映射的原理 mmap系统调用的最终目的是将,设备或文件映射到用户进程的虚拟地址空间,实现用户进程对文件的直接读写,这个任务可以分为以下三步: 1.在用户虚拟地址空间中寻找空闲的满足要求的一段连续的虚拟地址空间,为映射做准备(由内核mmap系统调用完成) 每个进程拥有3G字节的用户虚存空间。但是,这并不意味着用户进程在这3G的范围内可以任意使用,因为虚存空间最终得映射到某个物理存储空间(内存或磁盘空间),才真正可以使用。 那么,内核怎样管理每个进程3G的虚存空间呢?概括地说,用户进程经过编译、链接后形成的映象文件有一个代码段和数据段(包括data段和bss段),其中代码段在下,数据段在上。数据段中包括了所有静态分配的数据空间,即全局变量和所有申明为static的局部变量,这些空间是进程所必需的基本要求,这些空间是在建立一个进程的运行映像时就分配好的。除此之外,堆栈使用的空间也属于基本要求,所以也是在建立进程时就分配好的,如图3.1所示: 图3.1 进程虚拟空间的划分 在内核中,这样每个区域用一个结构struct vm_area_struct 来表示.它描述的是一段连续的、具有相同访问属性的虚存空间,该虚存空间的大小为物理内存页面的整数倍。可以使用 cat /proc/<pid>/maps来查看一个进程的内存使用情况,pid是进程号.其中显示的每一行对应进程的一个vm_area_struct结构. 下面是struct vm_area_struct结构体的定义: [cpp] view plaincopy struct vm_area_struct { struct mm_struct vm_mm; / The address space we belong to. / unsigned long vm_start; / Our start address within vm_mm. / unsigned long vm_end; / The first byte after our end address within vm_mm. / / linked list of VM areas per task, sorted by address / struct vm_area_struct vm_next, vm_prev; pgprot_t vm_page_prot; / Access permissions of this VMA. / unsigned long vm_flags; / Flags, see mm.h. / struct rb_node vm_rb; / For areas with an address space and backing store, linkage into the address_space->i_mmap prio tree, or linkage to the list of like vmas hanging off its node, or linkage of vma in the address_space->i_mmap_nonlinear list. / union { struct { struct list_head list; void parent; / aligns with prio_tree_node parent / struct vm_area_struct head; } vm_set; struct raw_prio_tree_node prio_tree_node; } shared; / A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma list, after a COW of one of the file pages. A MAP_SHARED vma can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack or brk vma (with NULL file) can only be in an anon_vma list. / struct list_head anon_vma_chain; / Serialized by mmap_sem & page_table_lock / struct anon_vma anon_vma; / Serialized by page_table_lock / / Function pointers to deal with this struct. / const struct vm_operations_struct vm_ops; / Information about our backing store: / unsigned long vm_pgoff; / Offset (within vm_file) in PAGE_SIZE units, not PAGE_CACHE_SIZE / struct file vm_file; / File we map to (can be NULL). / void vm_private_data; / was vm_pte (shared mem) / unsigned long vm_truncate_count;/ truncate_count or restart_addr / ifndef CONFIG_MMU struct vm_region vm_region; / NOMMU mapping region / endif ifdef CONFIG_NUMA struct mempolicy vm_policy; / NUMA policy for the VMA / endif }; 通常,进程所使用到的虚存空间不连续,且各部分虚存空间的访问属性也可能不同。所以一个进程的虚存空间需要多个vm_area_struct结构来描述。在vm_area_struct结构的数目较少的时候,各个vm_area_struct按照升序排序,以单链表的形式组织数据(通过vm_next指针指向下一个vm_area_struct结构)。但是当vm_area_struct结构的数据较多的时候,仍然采用链表组织的化,势必会影响到它的搜索速度。针对这个问题,vm_area_struct还添加了vm_avl_hight(树高)、vm_avl_left(左子节点)、vm_avl_right(右子节点)三个成员来实现AVL树,以提高vm_area_struct的搜索速度。 假如该vm_area_struct描述的是一个文件映射的虚存空间,成员vm_file便指向被映射的文件的file结构,vm_pgoff是该虚存空间起始地址在vm_file文件里面的文件偏移,单位为物理页面。 图3.2 进程虚拟地址示意图 因此,mmap系统调用所完成的工作就是准备这样一段虚存空间,并建立vm_area_struct结构体,将其传给具体的设备驱动程序 2 建立虚拟地址空间和文件或设备的物理地址之间的映射(设备驱动完成) 建立文件映射的第二步就是建立虚拟地址和具体的物理地址之间的映射,这是通过修改进程页表来实现的.mmap方法是file_opeartions结构的成员: int (mmap)(struct file ,struct vm_area_struct ); linux有2个方法建立页表: (1) 使用remap_pfn_range一次建立所有页表. int remap_pfn_range(struct vm_area_struct vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot); 返回值: 成功返回 0, 失败返回一个负的错误值 参数说明: vma 用户进程创建一个vma区域 virt_addr 重新映射应当开始的用户虚拟地址. 这个函数建立页表为这个虚拟地址范围从 virt_addr 到 virt_addr_size. pfn 页帧号, 对应虚拟地址应当被映射的物理地址. 这个页帧号简单地是物理地址右移 PAGE_SHIFT 位. 对大部分使用, VMA 结构的 vm_paoff 成员正好包含你需要的值. 这个函数影响物理地址从 (pfn<<PAGE_SHIFT) 到 (pfn<<PAGE_SHIFT)+size. size 正在被重新映射的区的大小, 以字节. prot 给新 VMA 要求的"protection". 驱动可(并且应当)使用在vma->vm_page_prot 中找到的值. (2) 使用nopage VMA方法每次建立一个页表项. struct page (nopage)(struct vm_area_struct vma, unsigned long address, int type); 返回值: 成功则返回一个有效映射页,失败返回NULL. 参数说明: address 代表从用户空间传过来的用户空间虚拟地址. 返回一个有效映射页. (3) 使用方面的限制: remap_pfn_range不能映射常规内存,只存取保留页和在物理内存顶之上的物理地址。因为保留页和在物理内存顶之上的物理地址内存管理系统的各个子模块管理不到。640 KB 和 1MB 是保留页可能映射,设备I/O内存也可以映射。如果想把kmalloc()申请的内存映射到用户空间,则可以通过mem_map_reserve()把相应的内存设置为保留后就可以。 (4) remap_pfn_range与nopage的区别 remap_pfn_range一次性建立页表,而nopage通过缺页中断找到内核虚拟地址,然后通过内核虚拟地址找到对应的物理页 remap_pfn_range函数只对保留页和物理内存之外的物理地址映射,而对常规RAM,remap_pfn_range函数不能映射,而nopage函数可以映射常规的RAM。 3 当实际访问新映射的页面时的操作(由缺页中断完成) (1) page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。 (2) 文件与 address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。 (3) 进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。 (4) 对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区 (swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。 注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新. (5) 所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。 四 总结 1.对于mmap的内存映射,是将物理内存映射到进程的虚拟地址空间中去,那么进程对文件的访问就相当于直接对内存的访问,从而加快了读写操作的效率。在这里,remap_pfn_range函数是一次性的建立页表,而nopage函数是根据page fault产生的进程虚拟地址去找到内核相对应的逻辑地址,再通过这个逻辑地址去找到page。完成映射过程。remap_pfn_range不能对常规内存映射,只能对保留的内存与物理内存之外的进行映射。 2.在这里,要分清几个地址,一个是物理地址,这个很简单,就是物理内存的实际地址。第二个是内核虚拟地址,即内核可以直接访问的地址,如kmalloc,vmalloc等内核函数返回的地址,kmalloc返回的地址也称为内核逻辑地址。内核虚拟地址与实际的物理地址只有一个偏移量。第三个是进程虚拟地址,这个地址处于用户空间。而对于mmap函数映射的是物理地址到进程虚拟地址,而不是把物理地址映射到内核虚拟地址。而ioremap函数是将物理地址映射为内核虚拟地址。 3.用户空间的进程调用mmap函数,首先进行必要的处理,生成vma结构体,然后调用remap_pfn_range函数建立页表。而用户空间的mmap函数返回的是映射到进程地址空间的首地址。所以mmap函数与remap_pfn_range函数是不同的,前者只是生成mmap,而建立页表通过remap_pfn_range函数来完成。 本篇文章为转载内容。原文链接:https://blog.csdn.net/wh8_2011/article/details/52373213。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-20 22:49:12
464
转载
转载文章
...CMA-262 没有定义 Date.parse()应该支持哪种日期格式,因此方法的行为因实现而异,因地区而异。默认通常接收的日期格式如下: ‘月/日/年’,如 6/13/2011; ‘英文月名 日, 年’,如 May 25, 2004; ‘英文星期几 英文月名 日 年 时:分:秒 时区’,如 Tue May 25 2004 00:00:00 GMT-070 alert(Date.parse('6/13/2011')); // 1307894400000 如果 Date.parse()没有传入或者不是标准的日期格式,那么就会返回 NaN。 alert(Date.parse()); // NaN 如果想输出指定的日期,那么把 Date.parse()传入 Date 构造方法里。 var box = new Date(Date.parse('6/13/2011')); // Mon Jun 13 2011 00:00:00 GMT+0800var box = new Date('6/13/2011'); // 直接传入,Date.parse()后台被调用 Date 对象及其在不同浏览器中的实现有许多奇怪的行为。其中有一种倾向是将超出的范围的值替换成当前的值,以便生成输出。例如,在解析“January 32, 2007”时,有的浏览器会将其解释为“February 1, 2007”。而 Opera 则倾向与插入当前月份的当前日期。 Date.UTC()方法同样也返回表示日期的毫秒数,但它与 Date.parse()在构建值时使用不同的信息。(年份,基于 0 的月份[0 表示 1 月,1 表示 2 月],月中的哪一天[1-31],小时数[0-23] ,分钟,秒以及毫秒)。只有前两个参数是必须的。如果没有提供月数,则天数为 1;如果省略其他参数,则统统为 0。 alert(Date.UTC(2011,11)); // 1322697600000 如果 Date.UTC()参数传递错误,那么就会出现负值或者 NaN 等非法信息。 alert(Date.UTC()); // 负值或者 NaN 如果要输出指定日期,那么直接把 Date.UTC()传入 Date 构造方法里即可。 var box = new Date(Date.UTC(2011,11, 5, 15, 13, 16)); 通用的方法 与其他类型一样,Date 类型也重写了 toLocaleString()、toString()和 valueOf()方法;但这些方法返回值与其他类型中的方法不同。 var box = new Date(Date.UTC(2011,11, 5, 15, 13, 16));alert('toString:' + box.toString());alert('toLocaleString:' + box.toLocaleString()); // 按本地格式输出 这两个方法在不同浏览器显示的效果又不一样,但不用担心,这两个方法只是在调试比较有用,在显示时间和日期上,没什么价值。valueOf()方法显示毫秒数。 日期格式化方法 Date 类型还有一些专门用于将日期格式化为字符串的方法。 var box = new Date();alert(box.toDateString()); // 以特定的格式显示星期几、月、日和年alert(box.toTimeString()); // 以特定的格式显示时、分、秒和时区alert(box.toLocaleDateString()); // 以特定地区格式显示星期几、月、日和年alert(box.toLocaleTimeString()); // 以特定地区格式显示时、分、秒和时区alert(box.toUTCString()); // 以特定的格式显示完整的 UTC 日期 组件方法 组件方法,是为我们单独获取你想要的各种时间/日期而提供的方法。需要注意的时候 ,这些方法中,有带 UTC 的,有不带 UTC 的。UTC 日期指的是在没有时区偏差的情况下的日期值。 alert(box.getTime()); // 获取日期的毫秒数,和 valueOf()返回一致alert(box.setTime(100)); // 以毫秒数设置日期,会改变整个日期alert(box.getFullYear()); // 获取四位年份alert(box.setFullYear(2012)); // 设置四位年份,返回的是毫秒数alert(box.getMonth()); // 获取月份,没指定月份,从 0 开始算起alert(box.setMonth(11)); // 设置月份alert(box.getDate()); // 获取日期alert(box.setDate(8)); // 设置日期,返回毫秒数alert(box.getDay()); // 返回星期几,0 表示星期日,6 表示星期六alert(box.setDay(2)); // 设置星期几alert(box.getHours()); // 返回时alert(box.setHours(12)); // 设置时alert(box.getMinutes()); // 返回分钟alert(box.setMinutes(22)); // 设置分钟alert(box.getSeconds()); // 返回秒数alert(box.setSeconds(44)); // 设置秒数alert(box.getMilliseconds()); // 返回毫秒数alert(box.setMilliseconds()); // 设置毫秒数alert(box.getTimezoneOffset()); // 返回本地时间和 UTC 时间相差的分钟数 以上方法除了 getTimezoneOffset(),其他都具有 UTC 功能,例如 setDate()及 getDate()获取星期几,那么就会有 setUTCDate()及getUTCDate(),表示世界协调时间。 2、正则表达式 假设用户需要在 HTML 表单中填写姓名、地址、出生日期等。那么在将表单提交到服务器进一步处理前,JavaScript 程序会检查表单以确认用户确实输入了信息并且这些信息是符合要求的。 什么是正则表达式 正则表达式(regular expression)是一个描述字符模式的对象。ECMAScript 的 RegExp 类表示正则表达式,而 String 和 RegExp 都定义了使用正则表达式进行强大的模式匹配和文本检索与替换的函数。 正则表达式主要用来验证客户端的输入数据。用户填写完表单单击按钮之后,表单就会被发送到服务器,在服务器端通常会用 PHP、ASP.NET 等服务器脚本对其进行进一步处理 。因为客户端验证,可以节约大量的服务器端的系统资源,并且提供更好的用户体验。 创建正则表达式 创建正则表达式和创建字符串类似,创建正则表达式提供了两种方法,一种是采用 new 运算符,另一个是采用字面量方式。 两种创建方式 var box = new RegExp('box'); // 第一个参数字符串var box = new RegExp('box', 'ig'); // 第二个参数可选模式修饰符 模式修饰符的可选参数 参数 含义 i 忽略大小写 g 全局匹配 m 多行匹配 var box = /box/; // 直接用两个反斜杠var box = /box/ig; // 在第二个斜杠后面加上模式修饰符 测试正则表达式 RegExp 对象包含两个方法:test()和 exec(),功能基本相似,用于测试字符串匹配。test()方法在字符串中查找是否存在指定的正则表达式并返回布尔值,如果存在则返回 true,不存在则返回 false。exec()方法也用于在字符串中查找指定正则表达式,如果 exec()方法执行成功,则返回包含该查找字符串的相关信息数组。如果执行失败,则返回 null。 RegExp 对象的方法 方法 功能 test 在字符串中测试模式匹配,返回 true 或 false exec 在字符串中执行匹配搜索,返回结果数组 // 使用 new 运算符的 test 方法示例var pattern = new RegExp('box', 'i'); // 创建正则模式,不区分大小写var str = 'This is a Box!'; // 创建要比对的字符串alert(pattern.test(str)); // 通过 test()方法验证是否匹配// 使用字面量方式的 test 方法示例var pattern = /box/i; // 创建正则模式,不区分大小写var str = 'This is a Box!';alert(pattern.test(str));// 使用一条语句实现正则匹配alert(/box/i.test('This is a Box!')); // 模式和字符串替换掉了两个变量// 使用 exec 返回匹配数组var pattern = /box/i;var str = 'This is a Box!';alert(pattern.exec(str)); // 匹配了返回数组,否则返回 null 使用字符串的正则表达式方法 除了 test()和 exec()方法,String 对象也提供了 4 个使用正则表达式的方法。 String 对象中的正则表达式方法 方法 含义 match(pattern) 返回 pattern 中的子串或 null replace(pattern, replacement) 用 replacement 替换 pattern search(pattern) 返回字符串中 pattern 开始位置 split(pattern) 返回字符串按指定 pattern 拆分的数组 // 使用 match 方法获取获取匹配数组var pattern = /box/ig; // 全局搜索var str = 'This is a Box!,That is a Box too';alert(str.match(pattern)); // 匹配到两个 Box,Boxalert(str.match(pattern).length); // 获取数组的长度// 使用 search 来查找匹配数据var pattern = /box/ig;var str = 'This is a Box!,That is a Box too';alert(str.search(pattern)); // 查找到返回位置,否则返回-1 因为 search 方法查找到即返回,也就是说无需 g 全局。 // 使用 replace 替换匹配到的数据var pattern = /box/ig;var str = 'This is a Box!,That is a Box too';alert(str.replace(pattern, 'Tom')); // 将 Box 替换成了 Tom// 使用 split 拆分成字符串数组var pattern = / /ig;var str = 'This is a Box!,That is a Box too';alert(str.split(pattern)); // 将空格拆开分组成数组 RegExp 对象的静态属性 属性 短名 含义 input $_ 当前被匹配的字符串 lastMatch $& 最后一个匹配字符串 lastParen $+ 最后一对圆括号内的匹配子串 leftContext $ 最后一次匹配前的子串 multiline $ 用于指定是否所有的表达式都用于多行的布尔值 rightContext $’ 在上次匹配之后的子串 // 使用静态属性var pattern = /(g)oogle/;var str = 'This is google!';pattern.test(str); // 执行一下alert(RegExp.input); // This is google!alert(RegExp.leftContext); // This isalert(RegExp.rightContext); // !alert(RegExp.lastMatch); // googlealert(RegExp.lastParen); // galert(RegExp.multiline); // false Opera 不支持 input、lastMatch、lastParen 和 multiline 属性。IE 不支持 multiline 属性。所有的属性可以使用短名来操作。RegExp.input 可以改写成 RegExp['$_'],依次类推。但 RegExp.input 比较特殊,它还可以写成 RegExp.$_。 RegExp 对象的实例属性 属性 含义 global Boolean 值,表示 g 是否已设置 ignoreCase Boolean 值,表示 i 是否已设置 lastIndex 整数,代表下次匹配将从哪里字符位置开始 multiline Boolean 值,表示 m 是否已设置 Source 正则表达式的源字符串形式 // 使用实例属性var pattern = /google/ig;alert(pattern.global); // true,是否全局了alert(pattern.ignoreCase); // true,是否忽略大小写alert(pattern.multiline); // false,是否支持换行alert(pattern.lastIndex); // 0,下次的匹配位置alert(pattern.source); // google,正则表达式的源字符串var pattern = /google/g;var str = 'google google google';pattern.test(str); // google,匹配第一次alert(pattern.lastIndex); // 6,第二次匹配的位 以上基本没什么用。并且 lastIndex 在获取下次匹配位置上 IE 和其他浏览器有偏差 ,主要表现在非全局匹配上。lastIndex 还支持手动设置,直接赋值操作。 获取控制 正则表达式元字符是包含特殊含义的字符。它们有一些特殊功能,可以控制匹配模式的方式。反斜杠后的元字符将失去其特殊含义。 字符类:单个字符和数字 元字符/元符号 匹配情况 . 匹配除换行符外的任意字符 [a-z0-9] 匹配括号中的字符集中的任意字符 [^a-z0-9] 匹配任意不在括号中的字符集中的字符 \d 匹配数字 \D 匹配非数字,同[^0-9]相同 \w 匹配字母和数字及_ \W 匹配非字母和数字及_ 字符类:空白字符 元字符/元符号 匹配情况 \0 匹配 null 字符 \b 匹配空格字符 \f 匹配进纸字符 \n 匹配换行符 \r 匹配回车字符 \t 匹配制表符 \s 匹配空白字符、空格、制表符和换行符 \S 匹配非空白字符 字符类:锚字符 元字符/元符号 匹配情况 ^ 行首匹配 $ 行尾匹配 \A 只有匹配字符串开始处 \b 匹配单词边界,词在[]内时无效 \B 匹配非单词边界 \G 匹配当前搜索的开始位置 \Z 匹配字符串结束处或行尾 \z 只匹配字符串结束处 字符类:重复字符 元字符/元符号 匹配情况 x? 匹配 0 个或 1 个 x x 匹配 0 个或任意多个 x x+ 匹配至少一个 x (xyz)+ 匹配至少一个(xyz) x{m,n} 匹配最少 m 个、最多 n 个 x 字符类:替代字符 元字符/元符号 匹配情况 this where 字符类:记录字符 元字符/元符号 匹配情况 (string) 用于反向引用的分组 \1 或$1 匹配第一个分组中的内容 \2 或$2 匹配第二个分组中的内容 \3 或$3 匹配第三个分组中的内容 // 使用点元字符var pattern = /g..gle/; // .匹配一个任意字符var str = 'google';alert(pattern.test(str));// 重复匹配var pattern = /g.gle/; // .匹配 0 个一个或多个var str = 'google'; //,?,+,{n,m}alert(pattern.test(str));// 使用字符类匹配var pattern = /g[a-zA-Z_]gle/; // [a-z]表示任意个 a-z 中的字符var str = 'google';alert(pattern.test(str));var pattern = /g[^0-9]gle/; // [^0-9]表示任意个非 0-9 的字符var str = 'google';alert(pattern.test(str));var pattern = /[a-z][A-Z]+/; // [A-Z]+表示 A-Z 一次或多次var str = 'gOOGLE';alert(pattern.test(str));// 使用元符号匹配var pattern = /g\wgle/; // \w匹配任意多个所有字母数字_var str = 'google';alert(pattern.test(str));var pattern = /google\d/; // \d匹配任意多个数字var str = 'google444';alert(pattern.test(str));var pattern = /\D{7,}/; // \D{7,}匹配至少 7 个非数字var str = 'google8';alert(pattern.test(str));// 使用锚元字符匹配var pattern = /^google$/; // ^从开头匹配,$从结尾开始匹配var str = 'google';alert(pattern.test(str));var pattern = /goo\sgle/; // \s 可以匹配到空格var str = 'goo gle';alert(pattern.test(str));var pattern = /google\b/; // \b 可以匹配是否到了边界var str = 'google';alert(pattern.test(str));// 使用或模式匹配var pattern = /google|baidu|bing/; // 匹配三种其中一种字符串var str = 'google';alert(pattern.test(str));// 使用分组模式匹配var pattern = /(google){4,8}/; // 匹配分组里的字符串 4-8 次var str = 'googlegoogle';alert(pattern.test(str));var pattern = /8(.)8/; // 获取 8..8 之间的任意字符var str = 'This is 8google8';str.match(pattern);alert(RegExp.$1); // 得到第一个分组里的字符串内容var pattern = /8(.)8/;var str = 'This is 8google8';var result = str.replace(pattern,'<strong>$1</strong>'); // 得到替换的字符串输出document.write(result);var pattern = /(.)\s(.)/;var str = 'google baidu';var result = str.replace(pattern, '$2 $1'); // 将两个分组的值替换输出document.write(result); 贪婪 惰性 + +? ? ?? ? {n} {n}? {n,} {n,}? {n,m} {n,m}? // 关于贪婪和惰性var pattern = /[a-z]+?/; // ?号关闭了贪婪匹配,只替换了第一个var str = 'abcdefjhijklmnopqrstuvwxyz';var result = str.replace(pattern, 'xxx');alert(result);var pattern = /8(.+?)8/g; // 禁止了贪婪,开启的全局var str = 'This is 8google8, That is 8google8, There is 8google8';var result = str.replace(pattern,'<strong>$1</strong>');document.write(result);var pattern = /8([^8])8/g; // 另一种禁止贪婪var str = 'This is 8google8, That is 8google8, There is 8google8';var result = str.replace(pattern,'<strong>$1</strong>');document.write(result);// 使用 exec 返回数组var pattern = /^[a-z]+\s[0-9]{4}$/i;var str = 'google 2012';alert(pattern.exec(str)); // 返回整个字符串var pattern = /^[a-z]+/i; // 只匹配字母var str = 'google 2012';alert(pattern.exec(str)); // 返回 googlevar pattern = /^([a-z]+)\s([0-9]{4})$/i; // 使用分组var str = 'google 2012';alert(pattern.exec(str)[0]); // google 2012alert(pattern.exec(str)[1]); // googlealert(pattern.exec(str)[2]); // 2012// 捕获性分组和非捕获性分组var pattern = /(\d+)([a-z])/; // 捕获性分组var str = '123abc';alert(pattern.exec(str));var pattern = /(\d+)(?:[a-z])/; // 非捕获性分组var str = '123abc';alert(pattern.exec(str));// 使用分组嵌套var pattern = /(A?(B?(C?)))/; // 从外往内获取var str = 'ABC';alert(pattern.exec(str));// 使用前瞻捕获var pattern = /(goo(?=gle))/; // goo 后面必须跟着 gle 才能捕获var str = 'google';alert(pattern.exec(str));// 使用特殊字符匹配var pattern = /\.\[\/b\]/; // 特殊字符,用\符号转义即可var str = '.[/b]';alert(pattern.test(str));// 使用换行模式var pattern = /^\d+/mg; // 启用了换行模式var str = '1.baidu\n2.google\n3.bing';var result = str.replace(pattern, '');alert(result); 常用的正则 检查邮政编码 var pattern = /[1-9][0-9]{5}/; // 共 6 位数字,第一位不能为 0var str = '224000';alert(pattern.test(str)); 检查文件压缩包 var pattern = /[\w]+\.zip|rar|gz/; // \w 表示所有数字和字母加下划线var str = '123.zip'; // \.表示匹配.,后面是一个选择alert(pattern.test(str)); 删除多余空格 var pattern = /\s/g; // g 必须全局,才能全部匹配var str = '111 222 333';var result = str.replace(pattern,''); // 把空格匹配成无空格alert(result); 删除首尾空格 var pattern = /^\s+/; // 强制首var str = ' goo gle ';var result = str.replace(pattern, '');pattern = /\s+$/; // 强制尾result = result.replace(pattern, '');alert('|' + result + '|');var pattern = /^\s(.+?)\s$/; // 使用了非贪婪捕获var str = ' google ';alert('|' + pattern.exec(str)[1] + '|');var pattern = /^\s(.+?)\s$/;var str = ' google ';alert('|' + str.replace(pattern, '$1') + '|'); // 使用了分组获取 简单的电子邮件验证 var pattern = /^([a-zA-Z0-9_\.\-]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,4})$/;var str = 'yc60.com@gmail.com';alert(pattern.test(str));var pattern = /^([\w\.\-]+)@([\w\.\-]+)\.([\w]{2,4})$/;var str = 'yc60.com@gmail.com';alert(pattern.test(str)); 3、Function类型 在 ECMAScript 中,Function(函数)类型实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针。 函数的声明方式 普通的函数声明 function box(num1, num2) {return num1+ num2;} 使用变量初始化函数 var box= function(num1, num2) {return num1 + num2;}; 使用 Function 构造函数 var box= new Function('num1', 'num2' ,'return num1 + num2'); 第三种方式我们不推荐,因为这种语法会导致解析两次代码(第一次解析常规 ECMAScript 代码,第二次是解析传入构造函数中的字符串),从而影响性能。但我们可以通过这种语法来理解"函数是对象,函数名是指针"的概念。 作为值的函数 ECMAScript 中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。 function box(sumFunction, num) {return sumFunction(num); // someFunction}function sum(num) {return num + 10;}var result = box(sum, 10); // 传递函数到另一个函数里 函数内部属性 在函数内部,有两个特殊的对象:arguments 和 this。arguments 是一个类数组对象,包含着传入函数中的所有参数,主要用途是保存函数参数。但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。 function box(num) {if (num <= 1) {return 1;} else {return num box(num-1); // 一个简单的的递归} } 对于阶乘函数一般要用到递归算法,所以函数内部一定会调用自身;如果函数名不改变是没有问题的,但一旦改变函数名,内部的自身调用需要逐一修改。为了解决这个问题,我们可以使用 arguments.callee 来代替。 function box(num) {if (num <= 1) {return 1;} else {return num arguments.callee(num-1); // 使用 callee 来执行自身} } 函数内部另一个特殊对象是 this,其行为与 Java 和 C中的 this 大致相似。换句话说 ,this 引用的是函数据以执行操作的对象,或者说函数调用语句所处的那个作用域。当在全局作用域中调用函数时,this 对象引用的就是 window。 // 便于理解的改写例子window.color = '红色的'; // 全局的,或者 var color = '红色的';也行alert(this.color); // 打印全局的 colorvar box = {color : '蓝色的', // 局部的 colorsayColor : function () {alert(this.color); // 此时的 this 只能 box 里的 color} };box.sayColor(); // 打印局部的 coloralert(this.color); // 还是全局的// 引用教材的原版例子window.color = '红色的'; // 或者 var color = '红色的';也行var box = {color : '蓝色的'};function sayColor() {alert(this.color); // 这里第一次在外面,第二次在 box 里面}getColor();box.sayColor = sayColor; // 把函数复制到 box 对象里,成为了方法box.sayColor(); 函数属性和方法 ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性 :length 和 prototype。其中,length 属性表示函数希望接收的命名参数的个数。 function box(name, age) {alert(name + age);}alert(box.length); // 2 对于 prototype 属性,它是保存所有实例方法的真正所在,也就是原型。这个属性 ,我们将在面向对象一章详细介绍。而 prototype 下有两个方法:apply()和 call(),每个函数都包含这两个非继承而来的方法。这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。 function box(num1, num2) {return num1 + num2; // 原函数}function sayBox(num1, num2) {return box.apply(this, [num1, num2]); // this 表示作用域,这里是 window} // []表示 box 所需要的参数function sayBox2(num1, num2) {return box.apply(this, arguments); // arguments 对象表示 box 所需要的参数}alert(sayBox(10,10)); // 20alert(sayBox2(10,10)); // 20 call()方法于 apply()方法相同,他们的区别仅仅在于接收参数的方式不同。对于 call()方法而言,第一个参数是作用域,没有变化,变化只是其余的参数都是直接传递给函数的。 function box(num1, num2) {return num1 + num2;}function callBox(num1, num2) {return box.call(this, num1, num2); // 和 apply 区别在于后面的传参}alert(callBox(10,10)); 事实上,传递参数并不是 apply()和 call()方法真正的用武之地;它们经常使用的地方是能够扩展函数赖以运行的作用域。 var color = '红色的'; // 或者 window.color = '红色的';也行var box = {color : '蓝色的'};function sayColor() {alert(this.color);}sayColor(); // 作用域在 windowsayColor.call(this); // 作用域在 windowsayColor.call(window); // 作用域在 windowsayColor.call(box); // 作用域在 box,对象冒充 这个例子是之前作用域理解的例子修改而成,我们可以发现当我们使用 call(box)方法的时候,sayColor()方法的运行环境已经变成了 box 对象里了。 使用 call()或者 apply()来扩充作用域的最大好处,就是对象不需要与方法发生任何耦合关系(耦合,就是互相关联的意思,扩展和维护会发生连锁反应)。也就是说,box 对象和 sayColor()方法之间不会有多余的关联操作,比如 box.sayColor = sayColor;。 本篇文章为转载内容。原文链接:https://blog.csdn.net/gongxifacai_believe/article/details/108286196。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-24 13:01:25
529
转载
转载文章
...对象 2.全局范围内定义的对象 3.局部自动对象 4.静态局部对象 5.例 四、对象数组 1.含义 2.【例3.6】 五、对象指针 1.指向对象的指针 2.指向对象成员的指针 (1)含义 (2)指向对象公有数据成员的指针 (3)指向对象成员函数的指针 (4)【例3.7】有关对象指针的使用方法 3.this指针 六、共用数据的保护 1.常对象 2.常对象成员 (1)常数据成员 (2)常成员函数 3.指向对象的常指针 4.指向常对象的指针变量 5.对象的常引用 (1)含义 (2)格式 (3)【例3.8】对象的引用 6.const型数据小结 编辑 七、对象的动态建立与释放——动态建立对象 八、对象的赋值和复制 1.对象的赋值 (1)含义 (2)【例3.9】对象的赋值 (3)说明 2.对象的复制 (1)含义 (2)【例】用复制对象的方法创建Box类的对象(用默认复制构造函数) (3)说明 九、静态成员 1.静态数据成员 (1)定义格式 (2)特性 (3)说明 (4)【例3.10】引用静态数据成员 2.静态成员函数 (1)含义 (2)【例3.11】关于引用非静态成员和静态成员的具体方法 (3)【例】具有静态数据成员的point类 (4)静态成员函数举例 (5)具有静态数据、函数成员的Point类 (6)静态成员函数、静态数组及其初始化 十、友元 1.友元函数 (1)含义 (2)格式 (3)【例3.12】将普通函数声明为友元函数 (4)友元成员函数 2.友元类 十一、类模板 1.含义 2.定义类模板的格式 3.在类模板外定义成员函数的语法 4.使用类模板时,定义对象的格式 5.【例3.14】声明类模板,实现两个整数、浮点数和字符的比较,求出大数和小数 前言 通过第二章的学习,已经对类和对象有了初步了解。本章将对类和对象进行进一步讨论。 一、构造函数 如果定义一个变量,而程序未对其进行初始化的话,这个变量的值是不确定的,因为C和C++不会自觉地去为它赋值。与此相似,如果定义一个对象,而程序未对其数据成员进行初始化的话,这个对象的值也是不确定的。 1.对象的初始化 在定义一个类时,不能对其数据成员赋初值,因为类是一种类型,系统不会为它分配内存空间。在建立一个对象时,需要对其数据成员赋初值。如果一个数据成员未被赋初值,则它的值是不确定的。因为系统为对象分配内存时,保持了内存单元的原状,它就成为数据成员的初值。这个值是随机的。 C++提供了构造函数机制,用来为对象的数据成员进行初始化。在前面的学习中一直未讲这个概念,其实如果你未设计构造函数,系统在创建对象时,会自动提供一个默认的构造函数,而它只为对象分配内存空间其他什么也不做。 如果类中的所有数据成员是公有的,可以在定义对象时对其数据成员初始化。例如: class Time{public:int hour;int minute;int sec;};Time t1{15,36,26}; 在一个打括号内顺序列出各个公有数据成员的值,在两个值之间用逗号分隔。注意这只能用于数据成员都是共有的情况。 在前面的例子里,是用成员函数对对象的数据成员赋初值,如果一个类定义了多个对象,对每个对象都要调用成员函数对数据成员赋初值,那么程序就会变得繁琐,所以用成员函数为数据成员赋初值不是一个好办法。 2.构造函数的作用 构造函数用于为对象分配空间和进行初始化,它属于某一个类,可以由系统自动生成。也可以由程序员编写,程序员根据初始化的要求设计构造函数及函数参数。 构造函数是一种特殊的成员函数,在程序中不需要写调用语句,在系统建立对象时由系统自觉调用执行。 构造函数的特点: 构造函数的名字与它的类名必须相同 它没有类型,也不返回值 它可以带参数,也可以不带参数 include <iostream>using namespace std;class Time {public:Time() {hour = 0;minute = 0;sec = 0;}void set_time();void show_time();private:int hour;int minute;int sec;};int main() {Time t1;t1.set_time();t1.show_time();Time t2;t2.show_time();return 0;}void Time::set_time() {cin >> hour;cin >> minute;cin >> sec;}void Time::show_time() {cout << hour << ":" << minute << ":" << sec << endl;} 在类Time中定义了构造函数Time,它与所在的类同名。在建立对象时自动执行构造函数,该函数的作用是为对象中的每个数据成员赋初值0。注意只有执行构造函数时才能为数据成员赋初值。 程序运行时首先建立对象t1,并对t1中的数据成员赋初值0,然后执行t1.set_time函数,从键盘输入新值给对象t1的数据成员,再输出t1的数据成员的值。接着建立对象t2,同时对t2中的数据成员赋初值0,最后输出t2的数据成员的初值。程序运行情况如下: 也可以在类内声明构造函数然后在类外定义构造函数。将程序修改为Time();然后在类外定义构造函数: Time::Time() {hour = 0;minute = 0;sec = 0;} 关于构造函数的使用,说明如下: 什么时候调用构造函数?当函数执行到对象定义语句时建立对象,此时就要调用构造函数,对象就有了自己的作用域,对象的生命周期开始了。 构造函数没有返回值,因此不需要在定义中声明类型。 构造函数不需要显式地调用,构造函数是在建立对象时由系统自动执行的,且只执行以此。构造函数一般定义为public。 在构造函数中除了可以对数据成员赋初值,还可以使用其他语句。 如果用户没有定义构造函数,C++系统会自动生成一个构造函数,而这个函数体是空的,不执行初始化操作。 3.带形参数的构造函数 (1)含义 可以采用带形参数的构造函数,在调用不同对象的构造函数时,从外边将不同的数据传递给构造函数,实现不同对象的初始化。 构造函数的首部的一般格式为:构造函数名(类型 形参1,类型 形参2,……)。在定义对象时指定实参,定义对象的格式为:类名 对象名(实参1,实参2,……)。 (2)【例3.2】 有两个长方柱,其长、宽、高分别为:(1)12,25,30(2)15,30,21编写程序,在类中用带参数的构造函数,计算它们的体积。 分析:可以在类中定义一个计算长方体体积的成员函数计算对象的体积。 include<iostream>using namespace std;class Box{public:Box(int,int,int); //声明int volume();private:int height;int width;int length;};Box::Box(int h,int w,int len) //长方体构造函数{height=h;width=w;length=len;}int Box::volume() //计算长方体体积{return(heightwidthlength);}int main(){Box box1(12,25,30); //定义对象box1cout<<"box1体积="<<box1.volume()<<endl;Box box2(15,30,21); //定义对象box2cout<<"box2体积="<<box2.volume()<<endl;return 0;} 【注】 带形参的构造函数在定义对象时必须指定实参 用这种方法可以实现不同对象的初始化 4.用参数初始化表对数据成员初始化 C++提供了参数初始化表的方法对数据成员初始化。这种方法不必再构造函数内对数据成员初始化,在函数的首部就能实现数据成员初始化。 函数名(类型1 形参1,类型2 形参2): 成员名1(形参1),成员名2(形参2){ } 功能:执行构造函数时,将形参1的值赋予成员1,将形参2的值赋予成员2,形参的值由定义对象时的实参值决定。此时定义对象的格式依然是带实参的形式:类名 对象名(实参1,实参2); 例:定义带形参初始化表的构造函数 Box::Box(int h,int w,int len):height(h),width(w),length(len){}//定义对象:Box box1(12,25,30);//……Box box2(15,30,21); 5.构造函数的重载 (1)含义 构造函数也可以重载。一个类可以有多个同名构造函数,函数参数的个数、参数的类型各不相同。 (2)【例3.3】 在【例3.2】的基础上定义两个构造函数,其中一个无参数,另一个有参数 include <iostream>using namespace std;class Box {public:Box();Box(int h, int w, int len): height(h), width(w), length(len) {}int volume();private:int height;int width;int length;};Box::Box() {height = 10;width = 10;length = 10;}int Box::volume() {return (height width length);}int main() {Box box1;cout << "box1 体积" << box1.volume() << endl;Box box2(15, 30, 25);cout << "box2 体积" << box2.volume() << endl;return 0;} (3)说明 不带形参的构造函数为默认构造函数,每个类只有一个默认构造函数,如果是系统自动给的默认构造函数,其函数体是空的 虽然每个类可以包含多个构造函数,但是创建对象时,系统仅执行其中一个 6.使用默认参数值的构造函数 (1)含义 C++允许在构造函数里为形参指定默认值,如果创建对象时,未给出相应的实参时,系统将用形参的默认值为形参赋值。 (2)格式 函数名(类型 形参1=常数,类型 形参2=常数,……); (3)【例3.4】 将【例3.3】中的构造函数改用带默认值的参数,长、宽、高的默认值都是10 include <iostream>using namespace std;class Box {public:Box(int w = 10, int h = 10, int len = 10);int volume();private:int height;int width;int length;};Box::Box(int w, int h, int len) {height = h;width = w;length = len;}int Box::volume() {return (height width length);}int main() {Box box1;cout << "box1 体积" << box1.volume() << endl;Box box2(15);cout << "box2 体积" << box2.volume() << endl;Box box3(15, 30);cout << "box3 体积" << box3.volume() << endl;Box box4(15, 30, 20);cout << "box4 体积" << box4.volume() << endl;return 0;} (4)说明 如果在类外定义构造函数,应该在声明构造函数时指定默认参数值,再定以函数时不再指定默认参数值 在声明构造函数时,形参名可以省略。例如:Box(int 10,int 10,int 10); 如果构造函数的所有形参都指定了默认值,在定义对象时,可以指定实参也可不指定实参。由于不指定实参也可以调用构造函数,因此全部形参都指定了默认值的构造函数也属于默认构造函数。为了避免歧义,不允许同时定义不带形参的构造函数和全部形参都指定默认值的构造函数。 不能同时使用重载构造函数和带默认值的构造函数 二、析构函数 1.含义 析构函数也是个特殊的成员函数,它的作用与构造函数相反,当对象的生命周期结束时,系统自动调用析构函数,收回对象占用的内存空间。 2.执行析构函数的时机 在一个函数内定义的对象当这个函数结束时,自动执行析构函数释放对象 static局部对象要到main函数结束或执行exit命令时才自动执行析构函数释放对象 全局对象(在函数外定义的对象)当main函数结束或执行exit命令时自动执行析构函数释放对象 如果用new建立动态对象,用delete时自动执行析构函数释放对象 3.特征 以~符号开始后跟类名 析构函数没有数据类型、返回值、形参。由于没有形参所以析构函数不能重载。一个类只有一个析构函数 如果程序员没有定义析构函数,C++编译系统会自动生成一个析构函数 【注】析构函数除了释放对象(资源)外,还可以执行程序员在最后一次适用对象后希望执行的任何操作。例如输出有关的信息。 4.【例3.5】包含构造函数和析构函数的C++程序 include <iostream>include <string>using namespace std;class Student {public:Student(int n, string nam, char s) {num = n;name = nam;sex = s;cout << "Constructor called." << endl;}~Student() {cout << "Destructor called." << endl;}void display() {cout << "num:" << num << endl;cout << "name:" << name << endl;cout << "sex:" << sex << endl;}private:int num;string name;char sex;};int main() {Student stud1(10010, "wang_li", 'f');stud1.display();Student stud2(10011, "zhang_han", 'm');stud2.display();return 0;}//main函数前声明的类其作用域是全局的 三、调用构造函数和析构函数的顺序 1.同一类存储类别的对象 一般情况下,调用析构函数的次序与调用构造函数的次序恰好相反:最先调用构造函数的对象,最后调用析构函数;最后调用构造函数的对象,最先调用析构函数。可简记为:先构造的后析构,后构造的先析构。它相当于一个栈,后进先出。 2.全局范围内定义的对象 在全局范围内定义的对象(在所有函数之外定义的对象),在文件中的所有函数(包括主函数)执行前调用构造函数。当主函数结束或执行exit函数时,调用析构函数。 3.局部自动对象 如果定义局部自动对象(在函数内定义对象),在创建对象时调用构造函数。如多次调用对象所在的函数,则每次创建对象时都调用构造函数。在函数调用结束时调用析构函数。 4.静态局部对象 如果在函数中定义静态局部对象,则在第一次调用该函数建立对象时调用构造函数,但在主函数结束或调用exit函数时才调用析构函数。 5.例 void fun(){student st1; //定义局部自动对象static student st2; //定义静态局部对象...} 对象st1是每次调用函数fun时调用构造函数。在函数fun结束时调用析构函数。 对象st2是第一次调用函数fun时调用构造函数,在函数fun结束时并不调用析构函数,到主函数结束时才调用析构函数 四、对象数组 1.含义 类是一种特殊的数据类型,它当然是C++的合法类型,自然可以定义对象数组。在一个对象数组中各个元素都是同类对象。例如一个班级有50个同学,每个学生有学号、年龄、成绩等属性,可以为这个班级建立一个对象数组,数组包括了50个元素:student std[50];。 可以这样建立构造函数:student::student(int 1001,int 18,int 60);。 在建立数组时,同样要调用构造函数。上面的数组有50个元素,要调用50次构造函数。如果构造函数有多个参数,C++要求:在等号后的花括号中为每个对象分别写出构造函数并指定实参。格式为: student st[n]={ student(实参1,实参2,实参3); …… student(实参1,实参2,实参3); }; 假定对象有三个数据成员:学号、年龄、成绩。下面定义有三个学生的对象数组: student st[3]={ student(1001,18,87); student(1002,19,76); student(1003,18,80); };//构造函数带实参 在建立对象数组时,分别调用构造函数,对每个对象初始化。每个元素的实参用括号括起来,实参的位置与构造函数形参的位置一一对应,不会混淆。 2.【例3.6】 include <iostream>using namespace std;class Box {public:Box(int h = 10, int w = 12, int len = 15): height(h), width(w), length(len) {} //int volume();private:int height;int width;int length;};int Box::volume() {return (height width length);}int main() {Box a[3] = {Box(10, 12, 15), Box(15, 18, 20), Box(16, 20, 26)};cout << "a[0]的体积是" << a[0].volume() << endl;cout << "a[1]的体积是" << a[1].volume() << endl;cout << "a[2]的体积是" << a[2].volume() << endl;return 0;}//每个数组元素是一个对象 五、对象指针 指针的含义是内存单元的地址,可以指向一般的变量,也可以指向对象。 1.指向对象的指针 对象要占据一片连续的内存空间,CPU实际都是按地址访问内存,所以对象在内存的其实地址是CPU确定对象在内存中位置的依据。这个起始地址称为对象指针。 C++的对象也可以参加取地址运算:&对象名。运算的结果是该对象的起始地址,也称对象的指针,要用与对象类型相同的指针变量保存运算的结果。 C++中定义对象的指针变量与定义其他的指针变量相似,格式如下:类名 变量名表。类名表示对象所属的类,变量名按标识符规则取名,两个变量名之间用逗号分隔。定义好指针变量后,必须先给赋予合法的地址后才能使用。 例如定义如下一个类: class Time {public:Time() {hour = 0;minute = 0;sec = 0;}void set_time();void show_time();private:int hour;int minute;int sec;};void Time::set_time() {cin >> hour;cin >> minute;cin >> sec;}void Time::show_time() {cout << hour << ":" << minute << ":" << sec << endl;} 在此基础上,有如下语句: Time pt; //定义pt是指向Time类对象的指针Time t1; //定义Time类对象t1pt=&t1; //将对象t1的地址赋予pt 程序在此基础上就可以用指针变量访问对象的成员。 (pt).hour;pt->hour;(pt).show_time();pt->show_time(); 2.指向对象成员的指针 (1)含义 对象由成员组成。对象占据的内存区是各个数据成员占据的内存区的总和。对象成员也有地址,即指针。这指针分指向数据成员的指针和指向成员函数的指针。 (2)指向对象公有数据成员的指针 定义数据成员的指针变量:数据类型 指针变量名(这里的数据类型是数据成员的数据类型) 计算公有数据成员的地址:&对象名.成员名 Time t1;int p1; //定义一个指向整型数据的指针变量p1=&t1.hour; //假定hour是公有成员cout<<p1<<endl; (3)指向对象成员函数的指针 定义指向成员函数的指针变量:数据类型(类名::变量名)(形参表); 数据类型是成员函数的类型;类名是对象所属的类;变量名按标识符取名;形参表:指定成员函数的形参表(形参个数、类型) 取成员函数的地址:&类名::成员函数名 给指针变量赋初值:指针变量名=&类名::成员函数名; 用指针变量调用成员函数:(对象名.指针变量名)([实参表]); 对象名:指定调用成员函数的对象;:明确其后的是一个指针变量;实参表:与成员函数的形参表对应,如无形参,可以省略实参表 (4)【例3.7】有关对象指针的使用方法 include <iostream>using namespace std;class Time {public:Time(int, int, int);int hour;int minute;int sec;void get_time();};Time::Time(int h, int m, int s) {hour = h;minute = m;sec = s;}void Time::get_time() {cout << hour << ":" << minute << ":" << sec << endl;}int main() {Time t1(10, 13, 56);int p1 = &t1.hour; //定义指向数据成员的指针p1cout << p1 << endl;t1.get_time(); //调用成员函数Time p2 = &t1; //定义指向对象t1的指针p2p2->get_time(); //用对象指针调用成员函数void(Time::p3)(); //定义指向成员函数的指针p3 = &Time::get_time; //给成员函数的指针赋初值(t1.p3)(); //用指向成员函数的指针调用成员函数return 0;} 【注】代码的34,35行可合并为:void(Time::p3)=&Time::get_time; 3.this指针 一个类的成员函数只有一个内存拷贝。类中不论哪个对象调用某个成员函数,调用的都是内存中同一个成员函数代码。例如Time类一个成员函数: void Time::get_time(){cout<<hour<<":"<<minute<<":"<<sec<<endl;}t1.get_time();t2.get_time(); 当不同对象的成员函数访问数据成员时,怎么保证访问的就是指定对象的数据成员?其实每个成员函数中都包含一个特殊的指针,他的名字是this指针。它是指向本类对象的指针。当对象调用成员函数时,它的值就是该对象的起始地址。所以为了区分不同对象访问成员函数,语法要求的调用成员函数的格式是:对象名.成员函数名(实参表)。从语法上明确是对象名所指的对象调用成员函数。This指针是隐式使用的,在调用成员函数时C++把对象的地址作为实参传递给this指针。例如成员函数定义如下: int Box::volume(){return(heightwidthlength);} C++编译成: int Box::volume(this){return(this->heightthis->widththis->length);} 对于计算长方体体积的成员函数volume,当对象调用它时,就把对象地址给this指针,编译程序将的地址作为实参调用成员函数:a.volume(&a);。实际上函数是计算(this->height)(this->width)(this->length),这时就等价计算(a.height)(a.width)(a.length)。 可以用(this)表示调用成员函数的对象。(this)就是this所指的对象。如前面的计算长方体体积的函数中return语句可以写成:return((this).height(this).width(this).length);注意,this两侧的括号不能省略。 C++通过编译程序,在对象调用成员函数时,把对象的地址赋予this指针,用this指针指向对象,实现了用同一个成员函数访问不同对象的数据成员。 六、共用数据的保护 如果既希望数据在一定范围内共享,又不愿它被随意修改,从技术上可以把数据指定为只读型的。C++提供const手段,将数据、对象、成员函数指定为常量,从而实现了只读要求,达到保护数据的目的。 1.常对象 定义格式: const 类名 对象名(实参表);或 类名 const 对象名(实参表); 把对象定义为常对象,对象中的数据成员就是常变量,在定义时必须带实参作为数据成员的初值,在程序中不允许修改常对象的数据成员值。 如果一个常对象的成员函数未被定义为常成员函数(除构造函数和析构函数外),则对象不能调用这样的函数。 const Time t1(10,16,36);t1.get_time();//错误,不能调用 为了访问常对象中的数据成员,要定义常成员函数。 void get_time() const 如果在常对象中要修改某个数据成员,C++提供了指定可变的数据成员方法。 格式:mutable 类型 数据成员 在定义数据成员时加mutable后,将数据成员声明为可变的数据成员,就可以用声明为const的成员函数修改它的值。 2.常对象成员 可以在声明普通对象时将数据成员或成员函数声明为常数据成员或常成员函数。 (1)常数据成员 格式: const 类型 数据成员名 将类中的数据成员定义为具有只读的性质。注意只能通过带参数初始表的构造函数对常数据成员进行初始化。例如: const int hour;Time::Time(int h){hour=h;...//错误}Time::Time(int h):hour(h){}//正确 在类中声明了某个常数据成员后,该类中每个对象的这个数据成员的值都是只读的,而每个对象的这个数据成员的值可以不同,由定义对象时给出。 (2)常成员函数 定义格式:类型 函数名 (形参表)const const是函数类型的一部分,在声明函数原型和定义函数时都要用const关键字。 【注1】const是函数类型的一个组成部分,因此在函数的实现部分也要使用关键字const。常成员函数不能修改对象的数据成员,也不能调用该类中没有由关键字const修饰的成员函数,从而保证了在常成员函数中不会修改数据成员的值。如果一个对象被说明为常对象,则通过该对象只能调用它的常成员函数。 【注2】一般成员函数可以访问或修改本类中非const数据成员。而常成员函数只能读本类中的数据成员,而不能写他们。 数据成员 非const成员函数 const成员函数 非const的数据成员 可以引用,也可以改变值 可以引用,但不可以改变值 const数据成员 可以引用,但不可以改变值 可以引用,但不可以改变值 const对象的数据成员 不允许引用和改变值 可以引用,但不可以改变值 常成员函数的使用: 如果类中有部分数据成员的值要求为只读,可以将它们声明为const,这样成员函数只能读这些数据成员的值,但不能修改它们的值 如果所有数据成员的值为只读,可将对象声明为const,在类中必须声明const成员函数,常对象只能通过常成员函数读数据成员 常对象不能调用非const成员函数 【注】如果常对象的成员函数未加const,编译系统将其当作非const成员函数;常成员函数不能调用非const成员函数 3.指向对象的常指针 如果在定义指向对象的指针时,使用了关键字const,他就是一个常指针,必须在定义时对其初始化,并且在程序运行中不能再修改指针的值。 格式:const 指针变量名=对象地址 Time t1(10,12,15),t2;Time const p1=&t1;//在此后,不能修改p1Time const p1=&t2;//错误语句 指向对象的常指针,在程序运行中始终指向的是同一个对象。即指针变量的值始终不变,但它所指对象的数据成员值可以修改。当需要将一个指针变量固定地与一个对象相联系时,就可将指针变量指定为const。往往用常指针作为函数的形参,目的是不允许在函数中修改指针变量的值,让它始终指向原来的对象。 4.指向常对象的指针变量 5.对象的常引用 (1)含义 前面学过引用是传递参数的有效方法。用引用形参时,形参变量与实参变量是同一个变量,在函数内修改引用形参也就是修改实参变量。如果用引用形参又不想让函数修改实参,可以使用常引用机制。 (2)格式 const 类名 &形参变量名 (3)【例3.8】对象的引用 include <iostream>using namespace std;class Time {public:Time(int, int, int);int hour;int minute;int sec;};Time::Time(int h, int m, int s) {hour = h;minute = m;sec = s;}void fun(Time &t) {t.hour = 18;}int main() {Time t1(10, 13, 56);fun(t1);cout << t1.hour << endl;return 0;} //如果用引用形参又不想让函数修改实参,可以使用常引用机制include <iostream>using namespace std;class Time {public:Time(int, int, int);void fun(int &t) {hour = t;t = 18;}int hour;int minute;int sec;};Time::Time(int h, int m, int s) {hour = h;minute = m;sec = s;}int main(int argc, char argc[]) {int x = 15;Time t1(10, 13, 56);t1.fun(x);cout << t1.hour << endl;cout << x << endl;return 0;} 6.const型数据小结 七、对象的动态建立与释放——动态建立对象 C++提供了new和delete运算符,实现动态分配、回收内存。他们也可以用来动态建立对象和释放对象。 格式:new 类名; 功能:在堆里分配内存,建立指定类的一个对象。如果分配成功,将返回动态对象的起始地址(指针);如不成功,返回0.为了保存这个指针,必须事先建立以类名为类型的指针变量。 格式:类名 指针变量名 Box pt;pt=new Box;//如果分配成功,就可以用指针变量pt访问动态对象的数据成员cout<<pt->height;cout<<pt->volume(); 当不再需要使用动态变量时,必须用delete运算符释放内存。 格式:delete 指针变量(存放的是用new运算返回的指针) 八、对象的赋值和复制 1.对象的赋值 (1)含义 如果一个类定义了两个或多个对象,则这些同类对象之间可以相互赋值。这里所指的对象的值含义是对象中所有数据成员的值。对象1、对象2都是已建立好的同类对象。 格式:对象1=对象2; (2)【例3.9】对象的赋值 include <iostream>using namespace std;class Box {public:Box(int = 10, int = 10, int = 10);int volume();private:int height;int width;int length;};Box::Box(int h, int w, int len) {height = h;width = w;length = len;}int Box::volume() {return (height width length);}int main() {Box box1(15, 30, 25), box2;cout << "box1 体积=" << box1.volume() << endl;box2 = box1;cout << "box2 体积=" << box2.volume() << endl;return 0;} (3)说明 对象的赋值只对数据成员操作 数据成员中不能含有动态分配的数据成员 2.对象的复制 (1)含义 对象赋值的前提是对象1和对象2是已经建立的对象。C++还可以按照一个对象克隆出另一个对象(从无到有),这就是复制对象。复制对象是创建对象的另一种方法(以前学过的是定义对象)。创建对象必须调用构造函数,复制对象要调用复制构造函数。以Box类为例,复制构造函数的形式是: Box::Box(const Box &b){height=b.height;width=b.width;length=b.length;} 复制构造函数只有一个参数,这个参数是本类的对象,且采用引用对象形式。为了防止修改数据,加const限制。构造函数的内容就是将实参对象的数据成员值赋予新对象对应的数据成员,如果程序中未定义复制构造函数,编译系统将提供默认的复制构造函数,复制类中的数据成员。 复制对象有两种格式: 类名 对象2(对象1);按对象1复制对象2 类名 对象2=对象1,对象3=对象1,……按对象1复制对象2、对象3 (2)【例】用复制对象的方法创建Box类的对象(用默认复制构造函数) //include "stdafx.h"include <iostream>using namespace std;class Box {public:Box(int = 10, int = 10, int = 10);int volume();private:int height;int width;int length;};Box::Box(int h, int w, int len) {height = h;width = w;length = len;}int Box::volume() {return (height width length);}int main() {Box box1(15, 30, 25);cout << "box1 体积=" << box1.volume() << endl;//Box box2=box1,box3=box2;Box box2(box1), box3(box2);cout << "box2 体积=" << box2.volume() << endl;cout << "box3 体积=" << box3.volume() << endl;return 0;} (3)说明 在以下情况调用复制构造函数: 在程序里用复制对象格式创建对象 当函数的参数是对象。调用函数时,需要将实参对象复制给形参对象,在此系统将调用复制构造函数 void fun(Box b){...}int main(){Box box1(12,15,18);fun(box1);return 0;} 在函数返回值是类的对象时,需要将函数里的对象复制一个临时对象当作函数值返回 Box f(){Box box1(12,15,18);return box1;}int main(){Box box2;box2=f();} 九、静态成员 C++用const保护数据对象不被修改,在实际中还需要共享数据,C++怎样提供数据共享机制?C++静态成员、友元实现对象之间、类之间的数据共享。 1.静态数据成员 (1)定义格式 static 类型 数据成员名 class Box{public:Box(int=10,int=10,int=10);int volume();private:static int height;int width;int length;}; (2)特性 设Box有n个对象box1..boxn。这n个对象的height成员在内存中共享一个整型数据空间。如果某个对象修改了height成员的值,其他n-1个对象的height成员值也被改变,从而达到n个对象共享height成员值的目的。 (3)说明 由于一个类的所有对象共享静态数据成员,所以不能用构造函数为静态数据成员初始化,只能在类外专门对其初始化。如果程序未对静态数据成员赋初值,则编译系统自动用0为它赋初值 格式:数据类型 类名::静态数据成员名=初值; 即可已用对象名引用静态成员,也可以用类名引用静态成员 静态数据成员在对象外单独开辟内存空间,只要在类中定义了静态成员,即使不定义对象,系统也为静态成员分配内存空间,可以被引用 在程序开始时为静态成员分配内存空间,直到程序结束才释放内存空间 静态数据成员作用域是它的类的作用域(如果在一个函数内定义类,他的静态数据成员作用域就是这个函数)在此范围内可以用“类名::静态成员名”的形式访问静态数据成员 (4)【例3.10】引用静态数据成员 include <iostream>using namespace std;class Box {public:Box(int, int);int volume();static int height;int width;int length;};Box::Box(int w, int len) {width = w;length = len;}int Box::volume() {return (height width length);}int Box::height = 10;int main() {Box a(15, 20), b(25, 30);cout << a.height << endl;cout << b.height << endl;cout << Box::height << endl;cout << a.volume() << endl;cout << b.volume() << endl;return 0;} 2.静态成员函数 (1)含义 C++提供静态成员函数,用它访问静态数据成员,静态成员函数不属于某个对象而属于类。 类中的非静态成员函数可以访问类中所有数据成员;而静态成员函数可以直接访问类的静态成员,不能直接访问非静态成员。 静态成员函数定义格式: static 类型 成员函数(形参表){……} 调用公有静态成员函数格式: 类名::成员函数(实参表) 引用方式 静态数据成员 非静态数据成员 静态成员函数 成员名 对象名.成员名 非静态成员函数 成员名 成员名 【注】静态成员函数不带this指针,所以必须用对象名和成员运算符.访问非静态成员;而普通成员函数有this指针,可以在函数中直接引用成员名。 (2)【例3.11】关于引用非静态成员和静态成员的具体方法 class Student {private:int num;int age;float score;static float sum;static int count;public:Student(int, int, int);void total();static float average();};Student::Student(int m, int a, int s) {num = m;age = a;score = s;}void Student::total() {sum += score;count++;}float Student::average() {return (sum / count);}float Student::sum = 0;int Student::count = 0;int main() {Student stud[3] = {Student(1001, 18, 70), Student(1002, 19, 79), Student(1005, 20, 98)};int n;cout << "请输入学生的人数:";cin >> n;for (int i = 1; i < n; i++)stud[i].total();cout << n << "个学生的平均成绩是:"cout << Student::average() << endl;return 0;} (3)【例】具有静态数据成员的point类 include <iostream>using namespace std;class Point {private:int X, Y;static int countP;public:Point(int xx = 0, int yy = 0) {X = xx;Y = yy;countP++;}Point(Point &p); //复制构造函数int GetX() {return X;}int GetY() {return Y;}int GetC() {cout << "Object id=" << countP << endl;return 0;} };Point::Point(Point &p) {X = p.X;Y = p.Y;countP++;}int Point::countP = 0;int main() {Point A(4, 5);cout << "Point A," << A.GetC() << "," << A.GetY();A.GetC();Point B(A);cout << "Point B," << B.GetC() << "," << B.GetY();B.GetC();return 0;} (4)静态成员函数举例 include <iostream>using namespace std;class application {private:static int global;public:static void f();static void g();};int application::global = 0;void application::f() {global = 5;}void application::g() {cout << global << endl;}int main() {application::f();application::g();return 0;} class A{private:int x; //非静态成员public:static void f(A a);};void A::f(A a){cout<<x; //对x的引用是错误的cout<<a.x; //正确} (5)具有静态数据、函数成员的Point类 include <iostream>using namespace std;class Point { //point类声明private: //私有数据成员int X, Y;static int countP;public: //外部接口Point(int xx = 0, int yy = 0) {X = xx;Y = yy;countP++;}Point(Point &p); //复制构造函数int GetX() {return X;}int GetY() {return Y;}static int GetC() {cout << "Object id=" << countP << endl;return 0;} };Point::Point(Point &p) {X = p.X;Y = p.Y;countP++;}int Point::countP = 0;int main() //主函数实现{ Point A(4, 5); //声明对象Acout << "Point A," << A.GetC() << "," << A.GetY();A.GetC(); //输出对象号,对象名引用Point B(A); //声明对象Bcout << "Point B," << B.GetC() << "," << B.GetY();Point::GetC(); //输出对象号,类名引用return 0;} (6)静态成员函数、静态数组及其初始化 include <iostream>include <stdio.h>using namespace std;class A {static int a[20];int x;public:A(int xx = 0) {x = xx;}static void in();static void out();void show() {cout << "x=" << x << endl;} };int A::a[20] = {0, 0};void A::in() {cout << "input a[20]:" << endl;for (int i = 0; i < 20; ++i)cin >> a[i];}void A::out() {for (int i = 0; i < 20; ++i)cout << "a[" << i << "]=" << a[i] << endl;}int main() {A::in();A::out();A a;a.out();a.show();return 0;} 十、友元 除了在同类对象之间共享数据外,类和类之间也可以共享数据。类的私有成员只能被类的成员函数访问,但是有时需要在类的外部访问类的私有成员,C++通过友元的手段实现这一特殊要求。友元可以是不属于任何类的一般函数,也可以是另一个类的成员函数,还可以是整个的一个类(这个类中的所有成员函数都可以成为友元函数)。 友元是C++提供的一种破坏数据封装和数据隐藏的机制。为了保证数据的完整性及数据封装与隐藏的原则,建议尽量不使用或少使用友元。 1.友元函数 (1)含义 如果在A类外定义一个函数(它可以是另一个类的成员函数,也可以是一个普通函数),在A类中声明该函数是A的友元函数后,这个函数就能访问A类中的所有成员。 (2)格式 friend 类型 类1::成员函数x(类2 &对象); friend 类型 函数y(类2 &对象); //类1是另一个类的类名,类2是本类的类名 功能:第一种形式在类2中声明类1的成员函数x为友元函数。第二种形式在类2中声明一个普通函数y是友元函数。 友元函数内访问对象的格式: 对象名.成员名 因为友元不是成员函数,它不属于类,所以它访问对象时必须冠以对象名。定义友元函数时形参通过定义引用对象,这样在友元函数内就能访问实参对象了。 (3)【例3.12】将普通函数声明为友元函数 include <iostream>using namespace std;class Time {public:Time(int, int, int);friend void display(Time &);private:int hour;int minute;int sec;};Time::Time(int h, int m, int s) {hour = h;minute = m;sec = s;}void display(Time &t) {cout << t.hour << ":" << t.minute << ":" << t.sec << endl;}int main() {Time t1(10, 13, 56);display(t1);return 0;} 【例】使用友元函数计算两点距离 include <iostream>include <cmath>using namespace std;class Point {public:Point(int xx = 0, int yy = 0) {X = xx;Y = yy;}int GetX() {return X;}int GetY() {return Y;}friend double Distance(Point &a, Point &b);private:int X, Y;};double Distance(Point &a, Point &b) {double dx = a.X - b.X;double dy = b.Y - b.Y;return sqrt(dx dx + dy dy);}int main() {Point p1(3.0, 5.0), p2(4.0, 6.0);double d = Distance(p1, p2);cout << "The distance is " << d << endl;return 0;} include <iostream>include <math.h>using namespace std;class TPoint {private:double x, y;public:TPoint(double a, double b) {x = a;y = b;cout << "点:(" << x << "," << y << ")" << endl;}friend double distance(TPoint &a, TPoint &b) {return sqrt((a.x - b.x) (a.x - b.x) + (a.y - b.y) (a.y - b.y));} };int main(int argc, char argv[]) {TPoint myp1(2.1, 1.3), myp2(5.4, 6.5);cout << "两点之间的距离为:";cout << distance(myp1, myp2) << endl;return 0;} (4)友元成员函数 【例3.13】将成员函数声明为友元函数 例子中有两个类Time和Date。其中Time类里定义了成员函数void display(Date &),他除了显示时间外还要显示日期,这个日期通过引用形参访问。在Date类中将Time类的display成员函数定义为友元函数,允许display访问Date类的所有私有数据成员。 include <iostream>using namespace std;class Date;class Time {private:int hour;int minute;int sec;public:Time(int, int, int);void display(const Date &);};class Date {private:int month;int day;int year;public:Date(int, int, int);friend void Time::display(const Date &);};Time::Time(int h, int m, int s) hour = h;minute = m;sec = s;}void Time::display(const Date &da) {cout << da.month << "/" << da.day << "/" << da.year << endl;cout << hour << ":" << minute << ":" << sec << endl;}Date::Date(int m, int d, int y) {month = m;day = d;year = y;}int main() {Time t1(10, 13, 56);Date d1(12, 25, 2004);t1.display(d1);return 0;} 【注1】友元是单向的,此例中声明Time的成员函数display是Date类的友元,允许它访问Date类的所有成员,但不等于说Date类的成员函数也是Time类的友元。 【注2】一个函数(包括普通函数和成员函数)可以被多个类声明为“朋友”,这样就可以引用多个类中的私有数据 【注3】例如可以将例3.13程序中的display函数作为类外的普通函数,分别在Time和Date类中将display声明为友元。Display就可以分别引用Time和Date类的对象的私有数据成员。输出年月日和时分秒。 2.友元类 C++允许将一个类声明为另一个类的友元。假定A类是B类的友元类,A类中所有的成员函数都是B类的友元函数,在B类中声明A类为友元类的格式:friend A; 【注1】友元关系是单向的,不是双向的 【注2】友元关系不能传递 【注3】实际中一般不把整个类声明友元类,而只是将确有需要的成员函数声明为友元函数 include <iostream>include <math.h>using namespace std;class B;class A {private:int x;public:A() {x = 3;}friend class B;};class B {public:void disp1(A temp) {temp.x++;cout << "disp1:x" << temp.x << endl;}void disp2(A temp) {temp.x--;cout << "disp2:x" << temp.x << endl;} };int main(int argc, char argv[]) {A a;B b;b.disp1(a);b.disp2(a);return 0;} class Student; //前向声明,类名声明class Teacher{privated:int noOfStudents;Student pList[100];public:void assignGrades(Student &s); //赋成绩void adjustHours(Student &s); //调整学时数};class Student{privated:int hours;float gpa;public:friend class Teacher;};void Teacher::assignGrades(Student &s){...};void Teacher::adjustHours(Student &s){...}; //函数定义必须在Student定义之后 十一、类模板 1.含义 对于功能相同而只是数据类型不同的函数,不必须定义出所有函数,我们定义一个可对任何类型变量操作的函数模板。对于功能相同的类而数据类型不同,不必定义出所有类,只要定义一个可对任何类进行操作的类模板。 例如定义比较两个整数的类和比较两个浮点数的类,这两个类做的工作是相似的,所以可以用类模板,减少工作量。 class Compare_int{private:int x,y;public:Compare_int(int a,int b){x=a;y=b;}int max(){return (x>y)?x:y;}int min(){return (x<y)?x:y;} };class Compare_float{private:float x,y;public:Compare_float(float a,float b){x=a;y=b;}float max(){return (x>y)?x:y;}float min(){return (x<y)?x:y;} }; 2.定义类模板的格式 template <class 类型参数名> class 类模板名 {……} 类型参数名:按标识符取名。如有多个类型参数,每个类型参数都要以class为前导,两个类型参数之间用逗号分隔 类模板名:按标识符取名 类模板{...}内定义数据成员和成员函数的规则:用类型参数作为数据类型,用类模板名作为类 template<class numtype>class Compare{private:numtype x,y;public:Compare(numtype a,numtype b){x=a,y=b;}numtype max(){return (x>y)?x:y;}numtype min(){return (x<y)?x:y;} }; 3.在类模板外定义成员函数的语法 类型参数 类模板名<类型参数>::成员函数名(形参表){……} 例如在类模板外定义max和min成员函数 template<class numtype>class Compare{public:Compare(numtype a,numtype b){x=a,y=b;}numtype max();numtype min();private:numtype x,y;};numtype Compare<numtype>::max(){return(x>y)?x:y;}numtype Compare<numtype>::min(){return(x<y)?x:y;} 4.使用类模板时,定义对象的格式 类模板名 <实际类型名>对象名; 类模板名 <实际类型名>对象名(实参表); 例如:Compare <int>cmp2(4,7) 在编译时, 编译系统用int取代类模板中的类型参数numtype,就把类模板具体化了。这时Compare<int>将相当于Compare_int类。 5.【例3.14】声明类模板,实现两个整数、浮点数和字符的比较,求出大数和小数 include <iostream>using namespace std;template<class numtype>class Compare {private:numtype x, y;public:Compare(numtype a, numtype b) {x = a;y = b;}numtype max() {return (x > y) ? x : y;}numtype min() {return (x < y) ? x : y;} };int main() {Compare<int>cmp1(3, 7);cout << cmp1.max() << "是两个整数中的大数." << endl;cout << cmp1.min() << "是两个整数中的小数." << endl;Compare<float>cmp2(45.78, 93.6);cout << cmp2.max() << "是两个浮点数中的大数." << endl;cout << cmp2.min() << "是两个浮点数中的小数." << endl;Compare<char>cmp3('a', 'A');cout << cmp3.max() << "是两个字符中的大者." << endl;cout << cmp3.min() << "是两个字符中的小者." << endl;return 0;} 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_72318954/article/details/127064376。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-01-29 12:38:23
544
转载
转载文章
...互比较同一算法的不同配置。 创建比较算法的结果图。 下面的示例在皮马印第安人发病的糖尿病数据集中将Logistic回归和线性判别分析进行了比较。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Compare Algorithms from pandas import read_csv from sklearn.model_selection import KFold from sklearn.model_selection import cross_val_score from sklearn.linear_model import LogisticRegression from sklearn.discriminant_analysis import LinearDiscriminantAnalysis load dataset url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] prepare models models = [] models.append(('LR', LogisticRegression(solver='liblinear'))) models.append(('LDA', LinearDiscriminantAnalysis())) evaluate each model in turn results = [] names = [] scoring = 'accuracy' for name, model in models: kfold = KFold(n_splits=10, random_state=7) cv_results = cross_val_score(model, X, Y, cv=kfold, scoring=scoring) results.append(cv_results) names.append(name) msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std()) print(msg) 哪种算法效果更好?你能做得更好吗?在评论中让我知道。 第11课:通过算法调整提高准确性 一旦找到一种或两种在数据集上表现良好的算法,您可能希望提高这些模型的性能。 提高算法性能的一种方法是将其参数调整为特定的数据集。 scikit-learn库提供了两种方法来搜索机器学习算法的参数组合。在今天的课程中,您的目标是练习每个。 使用您指定的网格搜索来调整算法的参数。 使用随机搜索调整算法的参数。 下面使用的代码段是一个示例,该示例使用网格搜索在Pima Indians糖尿病发病数据集上的Ridge回归算法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Grid Search for Algorithm Tuning from pandas import read_csv import numpy from sklearn.linear_model import Ridge from sklearn.model_selection import GridSearchCV url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] alphas = numpy.array([1,0.1,0.01,0.001,0.0001,0]) param_grid = dict(alpha=alphas) model = Ridge() grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3) grid.fit(X, Y) print(grid.best_score_) print(grid.best_estimator_.alpha) 哪些参数取得最佳效果?你能做得更好吗?在评论中让我知道。 第12课:利用集合预测提高准确性 您可以提高模型性能的另一种方法是组合来自多个模型的预测。 一些模型提供了内置的此功能,例如用于装袋的随机森林和用于增强的随机梯度增强。可以使用另一种称为投票的合奏将来自多个不同模型的预测组合在一起。 在今天的课程中,您将练习使用合奏方法。 使用随机森林和多余树木算法练习装袋。 使用梯度增强机和AdaBoost算法练习增强合奏。 通过将来自多个模型的预测组合在一起来练习投票合奏。 下面的代码段演示了如何在Pima Indians糖尿病发病数据集上使用随机森林算法(袋装决策树集合)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Random Forest Classification from pandas import read_csv from sklearn.model_selection import KFold from sklearn.model_selection import cross_val_score from sklearn.ensemble import RandomForestClassifier url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] num_trees = 100 max_features = 3 kfold = KFold(n_splits=10, random_state=7) model = RandomForestClassifier(n_estimators=num_trees, max_features=max_features) results = cross_val_score(model, X, Y, cv=kfold) print(results.mean()) 你能设计出更好的合奏吗?在评论中让我知道。 第13课:完成并保存模型 找到有关机器学习问题的良好模型后,您需要完成该模型。 在今天的课程中,您将练习与完成模型有关的任务。 练习使用模型对新数据(在训练和测试过程中看不到的数据)进行预测。 练习将经过训练的模型保存到文件中,然后再次加载。 例如,下面的代码片段显示了如何创建Logistic回归模型,将其保存到文件中,之后再加载它以及对看不见的数据进行预测。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Save Model Using Pickle from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression import pickle url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] test_size = 0.33 seed = 7 X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=test_size, random_state=seed) Fit the model on 33% model = LogisticRegression(solver='liblinear') model.fit(X_train, Y_train) save the model to disk filename = 'finalized_model.sav' pickle.dump(model, open(filename, 'wb')) some time later... load the model from disk loaded_model = pickle.load(open(filename, 'rb')) result = loaded_model.score(X_test, Y_test) print(result) 第14课:Hello World端到端项目 您现在知道如何完成预测建模机器学习问题的每个任务。 在今天的课程中,您需要练习将各个部分组合在一起,并通过端到端的标准机器学习数据集进行操作。 端到端遍历虹膜数据集(机器学习的世界) 这包括以下步骤: 使用描述性统计数据和可视化了解您的数据。 预处理数据以最好地揭示问题的结构。 使用您自己的测试工具抽查多种算法。 使用算法参数调整来改善结果。 使用集成方法改善结果。 最终确定模型以备将来使用。 慢慢进行,并记录结果。 您使用什么型号?您得到了什么结果?在评论中让我知道。 结束! (看你走了多远) 你做到了。做得好! 花一点时间,回头看看你已经走了多远。 您最初对机器学习感兴趣,并强烈希望能够使用Python练习和应用机器学习。 您可能是第一次下载,安装并启动Python,并开始熟悉该语言的语法。 在许多课程中,您逐渐地,稳定地学习了预测建模机器学习项目的标准任务如何映射到Python平台上。 基于常见机器学习任务的配方,您使用Python端到端解决了第一个机器学习问题。 使用标准模板,您所收集的食谱和经验现在可以自行解决新的和不同的预测建模机器学习问题。 不要轻描淡写,您在短时间内就取得了长足的进步。 这只是您使用Python进行机器学习的起点。继续练习和发展自己的技能。 喜欢点下关注,你的关注是我写作的最大支持 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_37337849/article/details/104016531。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-11 10:04:06
92
转载
转载文章
...据库的默认字符集都会配置成UTF-8(三字节),而utf8mb4在5.5以后才被支持,也很少会有DBA主动将系统默认字符集改成utf8mb4。 那么问题就来了,当我们把一个需要4字节UTF-8编码才能表示的字符存入数据库的时候就会报错:ERROR 1366: Incorrect string value: '\xF0\x9D\x8C\x86' for column 。 如果认真阅读了上面的解释,那么这个报错也就不难看懂了:我们试图将一串Bytes插入到一列中,而这串Bytes的第一个字节是 \xF0 意味着这是一个四字节的UTF-8编码。但是当MySQL表和列字符集配置为UTF-8的时候是无法存储这样的字符的,所以报了错。 那么遇到这种情况我们如何解决呢? 有两种方式: 1)升级MySQL到5.6或更高版本,并且将表字符集切换至utf8mb4; 2)在把内容存入到数据库之前做一次过滤,将Emoji字符替换成一段特殊的文字编码,然后再存入数据库中。之后从数据库获取或者前端展示时再将这段特殊文字编码转换成Emoji显示。 第二种方法我们假设用 --1F601-- 来替代4字节的Emoji,那么具体实现python代码可以参见Stackoverflow上的回答。 12、参考文献 [1] 如何配置Python默认字符集 [2] 字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8 [3] Unicode中文编码表 [4] Emoji Unicode Table [5] Every Developer Should Know About The Encoding 附录:更多IM开发方面的文章 [1] IM开发综合文章: 《新手入门一篇就够:从零开发移动端IM》 《移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”》 《移动端IM开发者必读(二):史上最全移动弱网络优化方法总结》 《从客户端的角度来谈谈移动端IM的消息可靠性和送达机制》 《现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障》 《腾讯技术分享:社交网络图片的带宽压缩技术演进之路》 《小白必读:闲话HTTP短连接中的Session和Token》 《IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理》 《移动端IM开发需要面对的技术问题》 《开发IM是自己设计协议用字节流好还是字符流好?》 《请问有人知道语音留言聊天的主流实现方式吗?》 《一个低成本确保IM消息时序的方法探讨》 《完全自已开发的IM该如何设计“失败重试”机制?》 《通俗易懂:基于集群的移动端IM接入层负载均衡方案分享》 《微信对网络影响的技术试验及分析(论文全文)》 《即时通讯系统的原理、技术和应用(技术论文)》 《开源IM工程“蘑菇街TeamTalk”的现状:一场有始无终的开源秀》 《QQ音乐团队分享:Android中的图片压缩技术详解(上篇)》 《QQ音乐团队分享:Android中的图片压缩技术详解(下篇)》 《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》 《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)》 《腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(下篇)》 《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》 《基于社交网络的Yelp是如何实现海量用户图片的无损压缩的?》 《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)》 《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)》 《字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8》 《全面掌握移动端主流图片格式的特点、性能、调优等》 《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践》 《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》 《自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)》 《融云技术分享:解密融云IM产品的聊天消息ID生成策略》 《适合新手:从零开发一个IM服务端(基于Netty,有完整源码)》 《拿起键盘就是干:跟我一起徒手开发一套分布式IM系统》 >> 更多同类文章 …… [2] 有关IM架构设计的文章: 《浅谈IM系统的架构设计》 《简述移动端IM开发的那些坑:架构设计、通信协议和客户端》 《一套海量在线用户的移动端IM架构设计实践分享(含详细图文)》 《一套原创分布式即时通讯(IM)系统理论架构方案》 《从零到卓越:京东客服即时通讯系统的技术架构演进历程》 《蘑菇街即时通讯/IM服务器开发之架构选择》 《腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT》 《微信后台基于时间序的海量数据冷热分级架构设计实践》 《微信技术总监谈架构:微信之道——大道至简(演讲全文)》 《如何解读《微信技术总监谈架构:微信之道——大道至简》》 《快速裂变:见证微信强大后台架构从0到1的演进历程(一)》 《17年的实践:腾讯海量产品的技术方法论》 《移动端IM中大规模群消息的推送如何保证效率、实时性?》 《现代IM系统中聊天消息的同步和存储方案探讨》 《IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?》 《IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议》 《IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token》 《WhatsApp技术实践分享:32人工程团队创造的技术神话》 《微信朋友圈千亿访问量背后的技术挑战和实践总结》 《王者荣耀2亿用户量的背后:产品定位、技术架构、网络方案等》 《IM系统的MQ消息中间件选型:Kafka还是RabbitMQ?》 《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》 《以微博类应用场景为例,总结海量社交系统的架构设计步骤》 《快速理解高性能HTTP服务端的负载均衡技术原理》 《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践》 《知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路》 《IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列》 《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》 《微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)》 《新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践》 《一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践》 《阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史》 《阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路》 《社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等》 《社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进》 《社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节》 《社交软件红包技术解密(四):微信红包系统是如何应对高并发的》 《社交软件红包技术解密(五):微信红包系统是如何实现高可用性的》 《社交软件红包技术解密(六):微信红包系统的存储层架构演进实践》 《社交软件红包技术解密(七):支付宝红包的海量高并发技术实践》 《社交软件红包技术解密(八):全面解密微博红包技术方案》 《社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等》 《即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?》 《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》 《多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了》 《从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路》 《从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结》 《IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!》 《瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)》 《阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处》 >> 更多同类文章 …… (本文同步发布于:http://www.52im.net/thread-2868-1-1.html) 本篇文章为转载内容。原文链接:https://blog.csdn.net/hellojackjiang2011/article/details/103586305。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-04-29 12:29:21
522
转载
转载文章
...zlib选项来禁用 配置hosts 所有主机上都配置 [root@xl2 11] cat /etc/hosts127.0.0.1 localhost192.168.20.132 gtm192.168.20.133 xl1192.168.20.134 xl2 关闭防火墙、Selinux 所有主机都执行 关闭防火墙: [root@gtm ~] systemctl stop firewalld.service[root@gtm ~] systemctl disable firewalld.service selinux设置: [root@gtm ~]vim /etc/selinux/config 设置SELINUX=disabled,保存退出。 This file controls the state of SELinux on the system. SELINUX= can take one of these three values: enforcing - SELinux security policy is enforced. permissive - SELinux prints warnings instead of enforcing. disabled - No SELinux policy is loaded.SELINUX=disabled SELINUXTYPE= can take one of three two values: targeted - Targeted processes are protected, minimum - Modification of targeted policy. Only selected processes are protected. mls - Multi Level Security protection. 安装依赖包 所有主机上都执行 yum install -y flex bison readline-devel zlib-devel openjade docbook-style-dsssl gcc 创建用户 所有主机上都执行 [root@gtm ~] useradd postgres[root@gtm ~] passwd postgres[root@gtm ~] su - postgres[root@gtm ~] mkdir ~/.ssh[root@gtm ~] chmod 700 ~/.ssh 配置SSH免密登录 仅仅在gtm节点配置如下操作: [root@gtm ~] su - postgres[postgres@gtm ~] ssh-keygen -t rsa[postgres@gtm ~] cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys[postgres@gtm ~] chmod 600 ~/.ssh/authorized_keys 将刚生成的认证文件拷贝到xl1到xl2中,使得gtm节点可以免密码登录xl1~xl2的任意一个节点: [postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl1:~/.ssh/[postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl2:~/.ssh/ 对所有提示都不要输入,直接enter下一步。直到最后,因为第一次要求输入目标机器的用户密码,输入即可。 下载源码 下载地址:https://www.postgres-xl.org/download/ [root@slave ~] ll postgres-xl-10r1.1.tar.gz-rw-r--r-- 1 root root 28121666 May 30 05:21 postgres-xl-10r1.1.tar.gz 编译、安装Postgres-XL 所有节点都安装,编译需要一点时间,最好同时进行编译。 [root@slave ~] tar xvf postgres-xl-10r1.1.tar.gz[root@slave ~] ./configure --prefix=/home/postgres/pgxl/[root@slave ~] make[root@slave ~] make install[root@slave ~] cd contrib/ --安装必要的工具,在gtm节点上安装即可[root@slave ~] make[root@slave ~] make install 配置环境变量 所有节点都要配置 进入postgres用户,修改其环境变量,开始编辑 [root@gtm ~]su - postgres[postgres@gtm ~]vi .bashrc --不是.bash_profile 在打开的文件末尾,新增如下变量配置: export PGHOME=/home/postgres/pgxlexport LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATHexport PATH=$PGHOME/bin:$PATH 按住esc,然后输入:wq!保存退出。输入以下命令对更改重启生效。 [postgres@gtm ~] source .bashrc --不是.bash_profile 输入以下语句,如果输出变量结果,代表生效 [postgres@gtm ~] echo $PGHOME 应该输出/home/postgres/pgxl代表生效 配置集群 生成pgxc_ctl.conf配置文件 [postgres@gtm ~] pgxc_ctl prepare/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.ERROR: File "/home/postgres/pgxl/pgxc_ctl/pgxc_ctl.conf" not found or not a regular file. No such file or directoryInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxl/pgxc_ctl --configuration /home/postgres/pgxl/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxl/pgxc_ctl 配置pgxc_ctl.conf 新建/home/postgres/pgxc_ctl/pgxc_ctl.conf文件,编辑如下: 对着模板文件一个一个修改,否则会造成初始化过程出现各种神奇问题。 pgxcInstallDir=$PGHOMEpgxlDATA=$PGHOME/data pgxcOwner=postgres---- GTM Master -----------------------------------------gtmName=gtmgtmMasterServer=gtmgtmMasterPort=6666gtmMasterDir=$pgxlDATA/nodes/gtmgtmSlave=y Specify y if you configure GTM Slave. Otherwise, GTM slave will not be configured and all the following variables will be reset.gtmSlaveName=gtmSlavegtmSlaveServer=gtm value none means GTM slave is not available. Give none if you don't configure GTM Slave.gtmSlavePort=20001 Not used if you don't configure GTM slave.gtmSlaveDir=$pgxlDATA/nodes/gtmSlave Not used if you don't configure GTM slave.---- GTM-Proxy Master -------gtmProxyDir=$pgxlDATA/nodes/gtm_proxygtmProxy=y gtmProxyNames=(gtm_pxy1 gtm_pxy2) gtmProxyServers=(xl1 xl2) gtmProxyPorts=(6666 6666) gtmProxyDirs=($gtmProxyDir $gtmProxyDir) ---- Coordinators ---------coordMasterDir=$pgxlDATA/nodes/coordcoordNames=(coord1 coord2) coordPorts=(5432 5432) poolerPorts=(6667 6667) coordPgHbaEntries=(0.0.0.0/0)coordMasterServers=(xl1 xl2) coordMasterDirs=($coordMasterDir $coordMasterDir)coordMaxWALsernder=0 没设置备份节点,设置为0coordMaxWALSenders=($coordMaxWALsernder $coordMaxWALsernder) 数量保持和coordMasterServers一致coordSlave=n---- Datanodes ----------datanodeMasterDir=$pgxlDATA/nodes/dn_masterprimaryDatanode=xl1 主数据节点datanodeNames=(node1 node2)datanodePorts=(5433 5433) datanodePoolerPorts=(6668 6668) datanodePgHbaEntries=(0.0.0.0/0)datanodeMasterServers=(xl1 xl2)datanodeMasterDirs=($datanodeMasterDir $datanodeMasterDir)datanodeMaxWalSender=4datanodeMaxWALSenders=($datanodeMaxWalSender $datanodeMaxWalSender) 集群初始化,启动,停止 初始化 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all 输出结果: /bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existpg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord2" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ echo $PGHOME/home/postgres/pgxl[postgres@gtm ~]$ ll /home/postgres/pgxl/pgxc/nodes/gtm/gtm.^C[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.ERROR: target coordinator master coord1 is running now. Skip initilialization.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1ERROR: target coordinator master coord1 is already running now. Skip initialization.Starting coordinator master coord22019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv6 address "::", port 54322019-05-30 21:09:25.563 EDT [2148] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:09:25.601 EDT [2149] LOG: database system was shut down at 2019-05-30 21:09:22 EDT2019-05-30 21:09:25.605 EDT [2148] LOG: database system is ready to accept connections2019-05-30 21:09:25.612 EDT [2156] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.WARNING: datanode master datanode1 is running now. Skipping.Starting datanode master datanode2.2019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv6 address "::", port 154322019-05-30 21:09:33.355 EDT [2404] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:09:33.392 EDT [2404] LOG: redirecting log output to logging collector process2019-05-30 21:09:33.392 EDT [2404] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC stop coordinator master coord1Stopping coordinator master coord1.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.PGXC stop datanode master datanode1Stopping datanode master datanode1.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.PGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC monitor allNot running: gtm masterNot running: coordinator master coord1Not running: coordinator master coord2Not running: datanode master datanode1Not running: datanode master datanode2PGXC exit[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1Starting coordinator master coord22019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.000 EDT [25137] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.038 EDT [25138] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.042 EDT [25137] LOG: database system is ready to accept connections2019-05-30 21:13:04.049 EDT [25145] LOG: cluster monitor started2019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.021 EDT [2730] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.057 EDT [2731] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.061 EDT [2730] LOG: database system is ready to accept connections2019-05-30 21:13:04.062 EDT [2738] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.Starting datanode master datanode2.2019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.079 EDT [25392] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.114 EDT [25392] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.114 EDT [25392] HINT: Future log output will appear in directory "pg_log".2019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.081 EDT [2985] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.117 EDT [2985] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.117 EDT [2985] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done. 启动 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf start all 关闭 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all 查看集群状态 [postgres@gtm ~]$ pgxc_ctl monitor all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlRunning: gtm masterRunning: coordinator master coord1Running: coordinator master coord2Running: datanode master datanode1Running: datanode master datanode2 配置集群信息 分别在数据节点、协调器节点上分别执行以下命令: 注:本节点只执行修改操作即可(alert node),其他节点执行创建命令(create node)。因为本节点已经包含本节点的信息。 create node coord1 with (type=coordinator,host=xl1, port=5432);create node coord2 with (type=coordinator,host=xl2, port=5432);alter node coord1 with (type=coordinator,host=xl1, port=5432);alter node coord2 with (type=coordinator,host=xl2, port=5432);create node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);create node datanode2 with (type=datanode, host=xl2,port=15432);alter node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);alter node datanode2 with (type=datanode, host=xl2,port=15432);select pgxc_pool_reload(); 分别登陆数据节点、协调器节点验证 postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633datanode2 | D | 15432 | xl2 | f | f | -905831925datanode1 | D | 15432 | xl1 | t | f | 888802358(4 rows) 测试 插入数据 在数据节点1,执行相关操作。 通过协调器端口登录PG [postgres@xl1 ~]$ psql -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= create database lei;CREATE DATABASEpostgres= \c lei;You are now connected to database "lei" as user "postgres".lei= create table test1(id int,name text);CREATE TABLElei= insert into test1(id,name) select generate_series(1,8),'测试';INSERT 0 8lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试3 | 测试4 | 测试7 | 测试(8 rows) 注:默认创建的表为分布式表,也就是每个数据节点值存储表的部分数据。关于表类型具体说明,下面有说明。 通过15432端口登录数据节点,查看数据 有5条数据 [postgres@xl1 ~]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试(5 rows) 登录到节点2,查看数据 有3条数据 [postgres@xl2 ~]$ psql -p15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------3 | 测试4 | 测试7 | 测试(3 rows) 两个节点的数据加起来整个8条,没有问题。 至此Postgre-XL集群搭建完成。 创建数据库、表时可能会出现以下错误: ERROR: Failed to get pooled connections 是因为pg_hba.conf配置不对,所有节点加上host all all 192.168.20.0/0 trust并重启集群即可。 ERROR: No Datanode defined in cluster 首先确认是否创建了数据节点,也就是create node相关的命令。如果创建了则执行select pgxc_pool_reload();使其生效即可。 集群管理与应用 表类型说明 REPLICATION表:各个datanode节点中,表的数据完全相同,也就是说,插入数据时,会分别在每个datanode节点插入相同数据。读数据时,只需要读任意一个datanode节点上的数据。 建表语法: CREATE TABLE repltab (col1 int, col2 int) DISTRIBUTE BY REPLICATION; DISTRIBUTE :会将插入的数据,按照拆分规则,分配到不同的datanode节点中存储,也就是sharding技术。每个datanode节点只保存了部分数据,通过coordinate节点可以查询完整的数据视图。 CREATE TABLE disttab(col1 int, col2 int, col3 text) DISTRIBUTE BY HASH(col1); 模拟数据插入 任意登录一个coordinate节点进行建表操作 [postgres@gtm ~]$ psql -h xl1 -p 5432 -U postgrespostgres= INSERT INTO disttab SELECT generate_series(1,100), generate_series(101, 200), 'foo';INSERT 0 100postgres= INSERT INTO repltab SELECT generate_series(1,100), generate_series(101, 200);INSERT 0 100 查看数据分布结果: DISTRIBUTE表分布结果 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) REPLICATION表分布结果 postgres= SELECT count() FROM repltab;count -------100(1 row) 查看另一个datanode2中repltab表结果 [postgres@datanode2 pgxl9.5]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT count() FROM repltab;count -------100(1 row) 结论:REPLICATION表中,datanode1,datanode2中表是全部数据,一模一样。而DISTRIBUTE表,数据散落近乎平均分配到了datanode1,datanode2节点中。 新增数据节点与数据重分布 在线新增节点、并重新分布数据。 新增datanode节点 在gtm集群管理节点上执行pgxc_ctl命令 [postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC 在服务器xl3上,新增一个master角色的datanode节点,名称是datanode3 端口号暂定5430,pool master暂定6669 ,指定好数据目录位置,从两个节点升级到3个节点,之后要写3个none none应该是datanodeSpecificExtraConfig或者datanodeSpecificExtraPgHba配置PGXC add datanode master datanode3 xl3 15432 6671 /home/postgres/pgxc/nodes/datanode/datanode3 none none none 等待新增完成后,查询集群节点状态: postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------datanode1 | D | 15432 | xl1 | t | f | 888802358datanode2 | D | 15432 | xl2 | f | f | -905831925datanode3 | D | 15432 | xl3 | f | f | -705831925coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633(4 rows) 节点新增完毕 数据重新分布 由于新增节点后无法自动完成数据重新分布,需要手动操作。 DISTRIBUTE表分布在了node1,node2节点上,如下: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) 新增一个节点后,将sharding表数据重新分配到三个节点上,将repl表复制到新节点 重分布sharding表postgres= ALTER TABLE disttab ADD NODE (datanode3);ALTER TABLE 复制数据到新节点postgres= ALTER TABLE repltab ADD NODE (datanode3);ALTER TABLE 查看新的数据分布: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+--------700122826 | 36-927910690 | 321148549230 | 32(3 rows) 登录datanode3(新增的时候,放在了xl3服务器上,端口15432)节点查看数据: [postgres@gtm ~]$ psql -h xl3 -p 15432 -U postgrespsql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= select count() from repltab;count -------100(1 row) 很明显,通过 ALTER TABLE tt ADD NODE (dn)命令,可以将DISTRIBUTE表数据重新分布到新节点,重分布过程中会中断所有事务。可以将REPLICATION表数据复制到新节点。 从datanode节点中回收数据 postgres= ALTER TABLE disttab DELETE NODE (datanode3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (datanode3);ALTER TABLE 删除数据节点 Postgresql-XL并没有检查将被删除的datanode节点是否有replicated/distributed表的数据,为了数据安全,在删除之前需要检查下被删除节点上的数据,有数据的话,要回收掉分配到其他节点,然后才能安全删除。删除数据节点分为四步骤: 1.查询要删除节点dn3的oid postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316385 | node1 | D | 5433 | datanode1 | f | t | 114854923016386 | node2 | D | 5433 | datanode2 | f | f | -92791069016397 | dn3 | D | 5430 | datanode1 | f | f | -700122826(5 rows) 2.查询dn3对应的oid中是否有数据 testdb= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+-------------------16388 | H | 1 | 1 | 4096 | 16397 16385 1638616394 | R | 0 | 0 | 0 | 16397 16385 16386(2 rows) 3.有数据的先回收数据 postgres= ALTER TABLE disttab DELETE NODE (dn3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (dn3);ALTER TABLEpostgres= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+----------(0 rows) 4.安全删除dn3 PGXC$ remove datanode master dn3 clean 故障节点FAILOVER 1.查看当前集群状态 [postgres@gtm ~]$ psql -h xl1 -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11739 | coord1 | C | 5432 | xl1 | f | f | 188569664316384 | coord2 | C | 5432 | xl2 | f | f | -119710263316387 | datanode2 | D | 15432 | xl2 | f | f | -90583192516388 | datanode1 | D | 15432 | xl1 | t | t | 888802358(4 rows) 2.模拟datanode1节点故障 直接关闭即可 PGXC stop -m immediate datanode master datanode1Stopping datanode master datanode1.Done. 3.测试查询 只要查询涉及到datanode1上的数据,那么该查询就会报错 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;WARNING: failed to receive file descriptors for connectionsERROR: Failed to get pooled connectionsHINT: This may happen because one or more nodes are currently unreachable, either because of node or network failure.Its also possible that the target node may have hit the connection limit or the pooler is configured with low connections.Please check if all nodes are running fine and also review max_connections and max_pool_size configuration parameterspostgres= SELECT xc_node_id, FROM disttab WHERE col1 = 3;xc_node_id | col1 | col2 | col3------------+------+------+-------905831925 | 3 | 103 | foo(1 row) 测试发现,查询范围如果涉及到故障的node1节点,会报错,而查询的数据范围不在node1上的话,仍然可以查询。 4.手动切换 要想切换,必须要提前配置slave节点。 PGXC$ failover datanode node1 切换完成后,查询集群 postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316386 | node2 | D | 15432 | datanode2 | f | f | -92791069016385 | node1 | D | 15433 | datanode2 | f | t | 1148549230(4 rows) 发现datanode1节点的ip和端口都已经替换为配置的slave了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qianglei6077/article/details/94379331。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-30 11:09:03
94
转载
转载文章
...的,原则上,应该首先定义最唯一的列,例如在(COL1,COL2)上的索引与在(COL2,COL1)上的索引是不相同的,因为两个索引的列的顺序不同;为了使查询优化器使用复合索引,查询语句中的WHERE子句必须参考复合索引中第一个列;当表中有多个关键列时,复合索引是非常有用的;使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。 综上所述,我们总结了如下索引使用原则: 1)逻辑主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非成组索引,对任何外键列采用非成组索引。考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用作读写。 2)不要索引memo/note 字段,不要索引大型字段(有很多字符),这样作会让索引占用太多的存储空间。 3)不要索引常用的小型表 4)一般不要为小型数据表设置过多的索引,假如它们经常有插入和删除操作就更别这样作了,SQLS对这些插入和删除操作提供的索引维护可能比扫描表空间消耗更多的时间。 七、大结局 查询是一个物理过程,表面上是SQLS在东跑西跑,其实真正大部分压马路的工作是由磁盘输入输出系统(I/O)完成,全表扫描需要从磁盘上读表的每一个数据页,如果有索引指向数据值,则I/O读几次磁盘就可以了。但是,在随时发生的增、删、改操作中,索引的存在会大大增加工作量,因此,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案。 一般来说建立索引的思路是: (1)主键时常作为where子句的条件,应在表的主键列上建立聚聚集索引,尤其当经常用它作为连接的时候。 (2)有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚聚集索引。 (3)经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。 (4)如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。 (5)在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。 (6)在选择索引字段时,尽量选择那些小数据类型的字段作为索引键,以使每个索引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。 SQLS是一个很复杂的系统,让索引以及查询背后的东西真相大白,可以帮助我们更为深刻的了解我们的系统。一句话,索引就象盐,少则无味多则咸。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_28052907/article/details/75194926。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-30 23:10:07
97
转载
转载文章
...用,实现企业资源优化配置,提升运营效率,增强竞争力的过程。在本文中,企业信息化是软件开发者就业的热门领域之一。 通信领域 , 涉及信息传递与交换技术的行业,包括但不限于电信服务、网络通信、无线通信、数据通信等子领域。在本文背景下,通信领域因门槛高、薪水高等特点吸引了大量软件开发者参与相关技术研发与项目实施,成为开发者主要从业方向之一。 系统集成 , 是指将不同功能、不同品牌或供应商的硬件设备、软件系统以及网络设备等按照一定的架构标准和规范,进行整合、协调和优化,形成一个统一、高效、稳定运行的信息系统解决方案的过程。在本文中,系统集成作为软件开发的重要组成部分,是部分开发者从事的工作内容之一。 高级程序员 , 在软件开发行业中,具备较深厚的专业技能、丰富的项目经验和较高技术水平的编程人员。他们不仅能够独立完成复杂模块的设计与编码工作,还能在项目中起到技术引领与指导作用,对项目的整体质量和进度有直接影响,通常其薪资待遇高于普通程序员。 技术总监(CTO) , Chief Technology Officer 的缩写,是企业中负责技术方向决策、技术团队管理、技术研发规划与实施的关键角色。技术总监需要具有深厚的技术背景、前瞻性的战略眼光以及出色的组织协调能力,确保企业的技术发展方向与业务需求保持一致,并通过技术创新推动企业发展。在本文中,技术总监的角色由于其综合能力和职责要求,在软件行业内占据重要地位,但人数相对较少。
2023-12-24 09:01:26
286
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
date "+%Y-%m-%d %H:%M:%S"
- 显示当前日期时间。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"