从Unix创世纪说起

创世纪一词来自于希伯来语:בראשית‎,意为“在开始之时”。每种文化都有它的创世纪一说,比如《创世纪》便是《圣纪》的第一卷,讲述了神创造,撒但败坏,人堕落,耶和华应许拯救的思想和故事。

对于Unix或类Unix系统,它关心时间从哪里开始,这便是它的创世纪。关于Unix和C语言创立背后的故事,我们在这里不重点介绍。

Unix操作系统的创世纪可以从中文Wikipedia(或英文wikipedia)中找到:

UNIX,一种计算机操作系统,具有多任务、多用户的特征。于1969年,在美国AT&T公司的贝尔实验室开发出来,参与开发的人有肯·汤普逊、丹尼斯·里奇等。
目前它的商标权由国际开放标准组织所拥有,只有匹配单一UNIX规范的UNIX系统才能使用UNIX这个名称,否则只能称为类UNIX(UNIX-like)。

Wikipedia中的图片更直观说明了整个Unix的发展和繁衍

由于Unix是从1970年开始广泛应用于商业和学术界,1970年被定义为Unix或类Unix系统的元纪。所有系统都以1970年1月1日 0点0分0秒作为时间的基准点,用秒数来表示系统时间,也即当前系统时间是从基准时间(1970年1月1日 0点0分0秒)走过多少秒之后的时间。

用简单公式来表即: 系统时间 = 基准时间 + 秒数

那么这个秒数应该保存在多宽的类型中呢?当时那个年代,16位字宽已是很大了。认为32位已经是“足够大”了,因此在POSIX标准中,将表示秒数的类型定义为time_t,而它是32位有符号整数类型。

下面是秒数与绝对时间的对照表:

time_t类型的秒数值 系统时间(绝对时间)
0 1970-01-01 00:00:00
1 1970-01-01 00:00:01
0x7ffffffff 2038-01-19 03:14:07

在32位系统上,time_t能表示的最大值为0x7ffffffff,当time_t取最大值时表示系统时间为2038-01-19 03:14:07,但时间再往后走时,那time_t会溢出变成一个负值,此时系统时间会倒流回到1901年,届时操作系统和上层软件都会运行错出。

下图同样来自于Wikipedia,它展示32系统time_t溢出前后系统时间的倒流:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpnyI1nw-1591166703136)( https://upload.wikimedia.org/wikipedia/commons/e/e9/Year_2038_problem.gif)]

如果时间将近2038年时,还存在32位机器在世界中运行,那将会受到2038年问题的影响。

2038年问题冲击波

当前世界时钟走到了2016年,离2038年还有21年有多,估计很多人会持乐观态度。也许到2038年,32位的机器早已不存在了,2038年问题自动消失。然而,世界没有这么美好。

操作系统运行时影响

对于服务器来说,早早就换到了64系统操作系统,2038年问题不复存在。而对于嵌入式设备来说,现在还有大量32位系统在全球各地运行,谁也无法保证这些系统在2038年之前就能光荣退役。

另外对于64位操作系统,上面还会运行着32位的应用程序,它的2038年问题一样对人们造成威胁,不可小视。

所以32位的time_t问题,必须要解决,无法自动消失

持久化数据

一般听到2038年问题,想的最多的是time_t类型问题。事实上,2038年问题的范围远不止于此。
前面谈到的操作系统time_t类型问题是系统运行时表示数据的溢出,但还有一些数据是静静在躺在某个磁盘上,当时间走到2038之后再把它它们翻读出来,一样会出现问题。

我们知道类Unix下文件都有几种时间属性,比如创建时间最后一次访问时间最后一次修改时间。如果该时间类型也是32位有符号数(也即time_t的等价类型),那在2038之后的某个早晨,试想一下你和朋友喝着咖啡,回忆起2038年以前的某次旅游,你兴高采烈说着之前见闻,并拿出手提电脑打开之前拍下的照片,这时扫兴的事情将会发生,文件打不出或者出错。

下表是Linux社区对Linux下所有支持文件系统的时间类型和溢出时间作了分析和对比,为了不影响阅读,只取出重要的文件系统:

file system time type expiration year
btrfs signed 64-bit seconds, 32-bit ns never
cramfs fixed 1970
ext2 signed 32-bit seconds 2038
ext3 signed 32-bit seconds 2038
ext4 (good old inodes) signed 32-bit seconds 2038
ext4 (new inodes 34 bit seconds / 30-bit ns (but broken) 2038
fat 7-bit years since 1980, 2s resolution 2107
fuse 64-bit second/32-bit ns never
gfs2 u64 seconds/u32 ns never
jffs2 unsigned 32-bit seconds 2106
logfs signed 64-bit ns 2262
nfsv2,v3 unsigned 32-bit seconds/ns 2106
nfsv4 u64 seconds/u32 ns never
nfsd unsigned 32-bit seconds/ns 2106
ntfs 64-bit 100ns since 1601 30828
pstore ascii seconds 2106
squashfs unsigned 32-bit seconds 2106
sysv unsigned 32-bit seconds 2106
xfs signed 32-bit seconds/ns 2106

从上述表格中看到依然少数几个文件系统受2038年问题的影响。文件系统的时间类型和单位是由文件系统自己定义的,可以与系统的time_t以及基准时间不相同。如果一个文系统选定义的单位为秒数,并且使用32位有符号整数表示时间,那么尽管该文件系统运行在64位的操作系统之下,依然会有2038年问题。

Linux最广泛使用的文件系统要数ext系统(ext2/ext3/ext4),上表显示它们也存在2038年问题题。但我在查看系统代码时,发现表示时间的类型为32位无符号整数,所以它们的溢出时间应该是2106年。

无论如何,2038年问题要想彻底解决,文件系统脱不了干系。

协议交互

除于系统运行的数据表示,以及持久化数据,还有一类是需要关心的,那就是机器与机器之间通信约定是否有2038年问题,如果有那将会造成灾难。

前一段时间对部分开源软件代码做time_t搜索,没有发现协议相关的代码使用time_t作为协议类型,在google上搜索2038年问题也没有找到跟协议相关的说明。

从目前分析来看,协议交互不涉及2038年问题。

涅槃重生还是曲线解国

2038年问题的根源就是使用了32位有符号整数来表示时间,看起来它的解决方案非常的简单,直接粗暴地将time_t从32位有符号整数 修改成 64位有符号整数

如果真的这样做,那对这个世界会产生什么影响呢? 在修复2038年问题那一天,估计全世界人已都在做同一件事情:

  1. 所有应用程序统统重新编写代码,至少得重新编译才能在新系统上运行
  2. 所有受影2038年影响的文件系统对应的分区,得统统格式化掉
  3. 在那天有的互联网服务都统统下线了,整个应用网络处于瘫痪状态
  4. 更离奇的是,你在银行的存款被清零了;对于那个贷款的家伙来说是个好事情,因为他们不用向银行还钱了

所以,解决方案不是这么简单的,无法创建一个新世界,直接抛弃旧世界。

Linux社区在讨论2038年问题时谈到OpenBSD的解决方案还真是这样干, 它直接将time_t类型从32位修改成64位。OpenBSD之所以能这么干,是因为整个系统是自包含的,内核和应用程序是一起编译的,不存在软件供应商发行二进制场景,所以没有二进制兼容性问题。

而Linux却无法这样做。一旦将time_t从32位修改成64位之后,在此之前发布的所有应用程序几乎不能在新系统上运行。对于文件系统来说,一旦存储格式上做修改(不是扩展),那磁盘上的老数据必须要统统格掉(格式化)才能在新文件系统上运行。

所以Linux的解决方案必须是要解决兼容性问题

对于ttime_t类型的改造,既要支持旧的应用程序可以在新系统上运行,也要支持新开发(或者新编译的)应用程 序能解决2038年问题,那意味着要保留老(32位time_t)的二进制应用程序接口(ABI),同时要新增一套64位time_t的ABI接口。

很明显Linux的解决方案如下图所示:

原来整个系统中原来32位time_t的所有系统调用都保留下来。新增一个time64_t类型,与之相关的系统调用都提供一套新的64位系统调用ABI。

老用户态程序由于在之前已经编译老了,在新系统运行时,它执行的系统调用依然还是原来老的32位系统调用。 而新编译的应用程序,尽管在源代码层面上看到还是time_t类型,但是在新系统上,它已变成64位了,编译出来的二进制程,它实际上调用的函数者是64位的系统调用。

其实在Linux时间相关的函数中,不单单只有time_t一个类型,还有struct timeval等等。所有这些类型,都需要提供64位的定义。

时间相关的系统调用其实也一大堆,比如直接为时间操作函数(gettimeofday/settimeofday/adjtimex/clock_gettime/clock_settime/clock_adjtime/clock_nanosleep),也有文件操作相关的函数也带上时间属性(stat/lstat/fstatat),同样还有些大杂烩函数诸如ioctl很多命令字出现时间相关的参数,都需要实现64位的版本。

当前Linux社区进展

正如前面所说,当前离2038时间还有20+年,Linux社区最近几年才慢慢开始着手解决2038年问题。

linux kernel newbies专项门有个2038年项目来跟踪此问题。整个项目有分成以下几部分:

  1. 用户态glibc对64位新型time_t以及相应函数的支持
  2. 内核态对64位新型time_t以及系统调用实现的支持
  3. 文件系统解决2038年问题

上面3个部分之中,第2点是重为重要的,也是整个解决方案的基石所在。当前 Arnd Bergmann 正着手解决提出第2部分,可以从他的git看到他的解决方案。至文件系统部分,一直都有人有解决,但解决时间还有待进一步了解。

glibc部分支持必须要等内核的解决方案经过评审完成并合入到内核主线之后,glibc社区才能根据最终敲定的ABI来实现64位time_t和相关函数。

总结

2038年问题与之前的千年虫问题的杀伤力是不一样的,千年虫属于应用程序的问题,而2038年问题却是系统级的,有更大的杀伤力。幸好当前离2038还有20年时间,并且整个Linux社区已经开始解决的,离目标不远了,曙光在望。

聊聊Linux2038年问题相关推荐

  1. 面向对象设计原则_聊聊面向对象的6大设计原则

    程序员都知道编程有 3 大类:面向过程.面向对象.面向函数.面向对象是被讨论的最多的,个人认为,这是因为 Java 之类的编程语言有强大的用户基础,本质还是因为比较符合人的直觉. 说到面向对象,大家可 ...

  2. python邮件发送哪个好_最全总结!聊聊 Python 发送邮件的几种方式

    1. 前言 邮件,作为最正式规范的沟通方式,在日常办公过程中经常被用到 我们都知道 Python内置了对 SMTP 的支持,可以发送纯文本.富文本.HTML 等格式的邮件 本文将聊聊利用 Python ...

  3. 今天聊聊分布式锁 No.86

    好了切入正题,一直在工作中会聊到很多锁的问题,今天跟大家一起闲聊一下,究竟什么是锁,为什么需要锁,以及分布式的情况下,怎么设计和实现锁. 什么是锁? 明·魏禧<大铁椎传>上是这样解释的: ...

  4. 聊聊找工作中的项目经验问题(推荐系统和智能问答)

    在求职过程中有这么一句话叫做"金九银十",也就是说,很多时候,求职的黄金时期就是在九月份和十月份,这两个月份中企业需求是最多的,求职的成功率也是最高的.但是随着AI方面的人才越来越 ...

  5. 聊聊Service Mesh:linkerd

    [编者的话]随着企业逐渐将传统的单体应用向微服务或云原生应用的转变,虽然微服务或者云原生应用能给企业带来更多的好处,但也会带来一些具有挑战的问题,如怎么管理从单体应用转向微服务所带来的服务间通讯的复杂 ...

  6. 庖丁解牛迭代器,聊聊那些藏在幕后的秘密

    0x00 前言 \\ 在我之前的一篇博客<细说C#:不是"栈类型"的值类型,从生命周期聊存储位置>的最后,我以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够 ...

  7. 聊聊Unsafe的一些使用技巧

    记得初学 Java 那会,刚学完语法基础,就接触到了反射这个 Java 提供的特性,尽管在现在看来,这是非常基础的知识点,但那时候无疑是兴奋的,瞬间觉得自己脱离了"Java 初学者" ...

  8. 聊聊 TCP 长连接和心跳那些事

    1 前言 可能很多 Java 程序员对 TCP 的理解只有一个三次握手,四次挥手的认识,我觉得这样的原因主要在于 TCP 协议本身稍微有点抽象(相比较于应用层的 HTTP 协议):其次,非框架开发者不 ...

  9. 聊聊算法在面试中的地位

    前段时间,有一位好友找到我,向我打听阿里社招笔试是否看重算法题的考察,我给予了肯定的答复.他表现的有些沮丧,表示自己工程底子很扎实,框架源码也研究地很透彻,唯独算法能力不行,leetcode 上的简单 ...

  10. 聊聊未来的程序员该如何选公司和谋规划

    聊聊未来的程序员该如何选公司和谋规划 前言 嗨喽,读者朋友们大家好,由hsm_computer老师给大家讲解,写好简历后,如何选公司投简历,如何对自己的未来做规划. 正文 过了年以后,新的一轮春招又要 ...

最新文章

  1. jquery 和js 还有 jq 挂事件
  2. 怎样把一个项目加入微服务器,构建微服务:快速搭建Spring Boot项目
  3. 工作310:uni-初始获取数据onload
  4. 【转】什么是 Azure 资源管理器
  5. 将Tomcat添加进服务启动
  6. 使用mysqlsla 分析 mysql logs
  7. FD.io VPP基本介绍-理解VPP软件架构
  8. Java和线性代数的关系_高等数学,线性代数与计算机的关系?
  9. 90 后美女学霸:出身清华姚班,成斯坦福 AI 实验室负责人高徒!
  10. 领导力、愚狂和改变:爱因斯坦是否错了?
  11. 公司计算机程序员英语怎么说,程序员英语怎么说
  12. 国际品牌会员俱乐部VTN甄选全球好物 把握消费升级趋势 引领品牌高质量发展
  13. Simulink中步长、powergui采样时间、模块采样时间、控制周期的关系
  14. 面试经验分享,论如何入职心仪的企业
  15. Linux云服务器基础学习
  16. 张勇用最严厉的内部信,敲打阿里云,也在提振阿里士气
  17. 在KubeSphere中部署微服务(阡陌)+ DevOps
  18. 计算机语言圆周率,使用Java如何计算圆周率
  19. linux加载模块失败,linux 第二次加载netlink模块时,内核创建sock失败
  20. 完全背包与01背包的区别

热门文章

  1. ckeditor5富文本数学化学方程式
  2. idea打字光标不跟随解决
  3. 王一博、肖战、吴宣仪、杨紫,2019明星沸点榜,谁的颜值更高,Python告诉你
  4. python用函数判断一个数是否为素数_【转载】Python脚本判断一个数是否为素数的几种方法...
  5. 最好的离子应用程序模板
  6. Unity修改编辑器
  7. 一款免费好用的英文润色软件(1Checker没错,这是免费的)
  8. 时间管理工具推荐,助你实现高效能,慢生活
  9. 计算机一级考试试题在线操作,计算机一级考试题库操作题
  10. 第六章 算法algorithms