随着公司的业务不断扩大,咱们在2013年末开始逐步的进入Java体系的阶段,不过谁都没有Java的经验,咱们就决定本身动手丰衣足食的策略,学习,请教和顾问。通过2014年的一年的努力,成功的组建了一个Java团队,并尝试作了一些新业务和基础性组件。虽然比较顺利,但仍是在我心中留下了许多疑问,例如说JVM的安全点和安全区,正好上周出差回来获得了一段时间的放松,又开始阅读了下JVM中的代码。安全
那么收获有什么呢?多线程
纠正了本身之前一个错误的认识,VMThread的是JVM用来完成JVM内的事情的线程,并不是执行OpCode的线程,而这正执行OpCode的线程是JavaThread。函数
发现了JVM进入安全区的一些原理,该原理和Linux的信号以及线程是有很是大的关系的。学习
在Linux的上古时代,Linux的线程技术和POSIX的标准是不一样的,它使用本身的LinuxThread库。这会为咱们带来什么影响呢?spa
首先,咱们说下POSIX是如何定义多线程的,POSIX下一个多线程的进程只有一个PID。从这个定义中,你们可能已经猜出LinuxThread的实现有什么不一样了,对,就是LinuxThread下每一个线程都有一个PID,每一个线程在系统中的表现就如同进程通常。操作系统
其次,现代的Linux的线程已经彻底符合POSIX的标准了。线程
总结就是,咱们能够忽略这件事情(不要丢鸡蛋)。code
随着Linux的内核版本不断提高,Linux的信号如今已经能够按照线程级别的触发了,换句话说就是,每一个线程能够关注本身的信号了,而且能够区别性对待了。那咱们须要注意什么呢?队列
在多线程应用中,咱们应当使用sigaction来代替singal函数,由于按POSIX的说法singal函数并无明肯定义本身在多线程应用中的行为。进程
可使用pthread_sigmask来为每一个线程设置独立的信号掩码。同时在多线程应用中应当避免使用sigprocmask这个函数,缘由也是POSIX中该函数病没有明肯定义本身在多线程应用中的行为。
这个时候,有人会产生疑问了,那么多线程下kill发出的进程级别的信号A怎么办?Linux是这样解决的,它会把这个信号交付给任意一个没有屏蔽信号A的线程。若是这信号没有被任何线程设置handler进行处理,就会触发POSIX规定的默认动做。
接着有人就会问,我怎么向某个线程发消息呢,POSIX为咱们准备了pthread_kill函数,咱们能够直接向特定的线程发送消息。那么若是一个线程收到信号A,可是本身没有安装handler会发生什么?其实和进程级别的信号处理方法同样,直接触发默认动做,一样会结束整个进程。
说到这里了,好像已经没什么可说的了,可是我在看JVM中的时候,发现了一个很是重要的信号SIGSEGV。
这个信号,也许是你们最不想见到,为何呢?咱们看这个信号的定义:
当当前程序对内存的引用无效时,就会产生当前信号,也就是咱们常说的“段违例”。 如下几种状况会产生该信号: 1.进程引用的内存页面不存在(例如,该页面位于堆和栈之间的映射的区域) 2.进程试图更新只读内存页(例如,程序文本段或已经被标记为只读的内存映射区域) 3.进程试图在用户态去访问内核部分的内存
OK,咱们都知道这个信号引起的结果就是进程退出。不过咱们都忽视了一个问题,在现代的Linux上,按照POSIX的定义,
这个信号是系统产生的线程级别的信号。换句话说,若是某个线程A出现了内存引用无效,那么产生的信号,会投递到线程A的信号队列中,而不是像进程级别的信号没法肯定接受者是谁。
若是咱们想让全部Java线程停下来的时候,在JVM的JavaThread执行到你们所知道的test 特定页面的指令时,就会由于更新不可读页面而触发SIGSEGV信号。那么对于那些正在执行native代码的JavaThread该怎么办,JVM中的注释写的很是清楚,native返回JVM时会检查是否能返回的。
好了再多说一句,JVM是若是将特定内存保护起来的呢?这个须要看操做系统的API了,在Linux中是mprotect。
多读读POSIX标准和Intel的CPU体系结构,会让本身在开发变的容易些。
转自TTalkIM,深刻阅读点击 https://ttalk.im/articles/19