Linux中的各种锁, 自旋锁/spin lock, 排队自旋锁、MCS锁、CLH锁,

Linux中的各种锁, 自旋锁/spin lock, 排队自旋锁、MCS锁、CLH锁 Linux中的各种锁 互斥锁 文件锁 读写锁 Linux作为典型的多用户、多任务、抢占式内核调度的操作系统,为了提高并行处理能力,无论在内核层面还是在用户层面都需要特殊的机制来确保任务的正确性和系统的稳定运行,就如同一个国家需要各种法律条款来约束每个公民的行为,才能有条不紊地运转。 在内核层面涉及到各种软硬件中断、进线程睡眠、抢占式内核调度、多处理器SMP架构等,因此内核在完成自己工作的时候一直在处理这些资源抢占的冲突问题。 在用户层面的进程,虽然Linux作为虚地址模式操作系统,为每个进程开辟了独立的虚拟地址空间,伪独占式拥有资源,但是仍然存在很多场景不得不产生多个进程共享资源的问题,来完成进程间的通信,但是在Go语言中进程间的通信使用消息来完成,处理地更优雅一些。 在线程层面,线程作为进程的一部分,进程内的多个线程只拥有自己的独立堆栈等少量结构,大部分的资源还是过线程共享,因此多线程的资源占用冲突比进程更加明显,所以多线程编程的线程安全问题是个重难点。综上可知,无论在kernel还是user space都必须有一些机制来确保对于资源共享问题的解决,然后这个机制就是接下来要说的: 同步和互斥。 同步和互斥机制 基本概念 同步和互斥的概念有时候很容易混淆,可以简单地认为同步是更加宏观角度的一种说法,互斥是冲突解决的细节方法。所谓同步就是调度者让任务按照约定的合理的顺序进行,但是当任务之间出现资源竞争,也就是竞态冲突时,使用互斥的规则强制约束允许数量的任务占用资源,从而解决各个竞争状态,实现任务的合理运行。 同步和互斥密不可分,有资料说互斥是一种特殊的同步,对此我不太理解,不过实际中想明白细节就行,文字游戏没有意义。 简单来说: 同步与互斥机制是用于控制多个任务对某些特定资源的访问策略 同步是控制多个任务按照一定的规则或顺序访问某些共享资源 互斥是控制某些共享资源在任意时刻只能允许规定数量的任务访问 角色分类 整个协调流程涉及的角色本质上只有三类: 不可独占的共享资源 多个使用者 调度者 调度者需要为多个运行任务制定访问使用规则来实现稳定运行,这个调度者可以是内核、可以是应用程序,具体场景具体分析。 重要术语 要很好地理解同步和互斥,就必须得搞清楚几个重要术语: 竞争冒险(race hazard)或竞态条件(race condition) 最早听说这个术语是在模电数电的课程上,门电路出现竞态条件造成错误的结果,在计算机里面就是多个使用者同时操作共享的变量造成结果的不确定。 临界区 临界区域critical section是指多使用者可能同时共同操作的那部分代码,比如自加自减操作,多个线程处理时就需要对自加自减进行保护,这段代码就是临界区域。 Linux中常用的锁 在说锁之前还需要知道几个东西:信号量和条件变量。这两个东西和锁有一定的联系和区别,在不同的场合单独使用或者配合实现来说实现安全的并发,至于网上很多说互斥锁是一种信号量的特例,对于这种特例理解不了也罢。信号量和互斥锁的场景不一样,信号量主要是资源数量的管理(池化),实际用的频率远不如互斥锁,文字游戏着实无趣,实用主义至上,掌握高频工具的特点正确使用即可,大可不必过于学术派。在使用锁时需要明确几个问题: 锁的所有权问题 谁加锁 谁解锁 解铃还须系铃人 锁的作用就是对临界区资源的读写操作的安全限制 锁是否可以被多个使用者占用(互不影响的使用者对资源的占用) 占用资源的加锁者的释放问题 (锁持有的超时问题) 等待资源的待加锁者的等待问题(如何通知到其他等着资源的使用者) 多个临界区资源锁的循环问题(死锁场景) 带着问题明确想要达到的目的,我们同样可以根据自己的需求设计锁,Linux现有的锁如果从上面几个问题的角度去理解,就非常容易了。 自旋锁 spinlock 自旋锁的主要特征是使用者在想要获得临界区执行权限时,如果临界区已经被加锁,那么自旋锁并不会阻塞睡眠,等待系统来主动唤醒,而是原地忙轮询资源是否被释放加锁,自旋就是自我旋转,这个名字还是很形象的。自旋锁有它的优点就是避免了系统的唤醒,自己来执行轮询,如果在临界区的资源代码非常短且是原子的,那么使用起来是非常方便的,避免了各种上下文切换,开销非常小,因此在内核的一些数据结构中自旋锁被广泛的使用。 互斥锁 mutex 使用者使用互斥锁时在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作,谁加锁谁释放,其他使用者没有释放权限。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。 区别于自旋锁,互斥锁无法获取锁时将阻塞睡眠,需要系统来唤醒,可以看出来自旋锁自己原地旋转来确定锁被释放了,互斥锁由系统来唤醒,但是现实并不是那么美好的,因为很多业务逻辑系统是不知道的,仍然需要业务线程执行while来轮询是否可以重新加锁。考虑这种情况: 解锁时有多个线程阻塞,那么所有该锁上的线程都被变成就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待,对其他线程而言就是虚假唤醒。 在这种方式下,只有一个线程能够访问被互斥锁保护的资源。 读写锁, 共享互斥锁, rwlock 读写锁也叫共享互斥锁: 读模式共享和写模式互斥,本质上这种非常合理,因为在数据没有被写的前提下,多个使用者读取时完全不需要加锁的。读写锁有读加锁状态、写加锁状态和不加锁状态三种状态,当读写锁在写加锁模式下,任何试图对这个锁进行加锁的线程都会被阻塞,直到写进程对其解锁。 读优先的读写锁: 读写锁 rwlock 默认的也是读优先,也就是:当读写锁在读加锁模式先,任何线程都可以对其进行读加锁操作,但是所有试图进行写加锁操作的线程都会被阻塞,直到所有的读线程都解锁,因此读写锁很适合读次数远远大于写的情况。这种情况需要考虑写饥饿问题,也就是大量的读一直轮不到写,因此需要设置公平的读写策略。在一次面试中曾经问到实现一个写优先级的读写锁,感兴趣的可以想想如何实现。 ...

2014-12-05 · 2 min · 326 words · -

Genymotion

Genymotion Genymotion是一套完整的工具集,旨在为Android提供虚拟环境。开发人员、测试人员、销售人员甚至是游戏玩家都能从中获得众多实用功 能。 它可用于大多数操作系统: Windows、Linux以及MacOS X。它既易于安装,又具备强大的功能: 遵循简单的安装流程指引、选择一款Android虚拟设备、启动工具,大功告成! http://mobile.51cto.com/android-405002.htm Genymotion Android 模拟器一直以速度奇慢无比著称,基本慢到不可用。本文介绍我一直在用的 Genymotion,速度不亚于真机。而且功能齐全,使用简单。 Genymotion 来自于 AndroVM 这个开源项目,基于 x86 和 VirtualBox,支持 OpenGL 加速,可以用于Mac/Win/Linux。最近发布了新版,支持了 Android2.3/4.3,新增了拖拽安装 apk,移除了 Google 市场 (后面提供解决方案) 。另外增加了功能更丰富的付费版,个人可以继续使用免费版。 https://www.imququ.com/post/genymotion.html http://mobile.51cto.com/android-405002.htm

2014-12-04 · 1 min · 29 words · -

Genymotion

Genymotion Genymotion是一套完整的工具集,旨在为Android提供虚拟环境。开发人员、测试人员、销售人员甚至是游戏玩家都能从中获得众多实用功 能。 它可用于大多数操作系统: Windows、Linux以及MacOS X。它既易于安装,又具备强大的功能: 遵循简单的安装流程指引、选择一款Android虚拟设备、启动工具,大功告成! http://mobile.51cto.com/android-405002.htm Genymotion Android 模拟器一直以速度奇慢无比著称,基本慢到不可用。本文介绍我一直在用的 Genymotion,速度不亚于真机。而且功能齐全,使用简单。 Genymotion 来自于 AndroVM 这个开源项目,基于 x86 和 VirtualBox,支持 OpenGL 加速,可以用于Mac/Win/Linux。最近发布了新版,支持了 Android2.3/4.3,新增了拖拽安装 apk,移除了 Google 市场 (后面提供解决方案) 。另外增加了功能更丰富的付费版,个人可以继续使用免费版。 https://www.imququ.com/post/genymotion.html

2014-12-04 · 1 min · 28 words · -

jvm Warmup

jvm Warmup Warmup Warmup 是指在实际进行 benchmark 前先进行预热的行为。为什么需要预热?因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译成为机器码从而提高执行速度。所以为了让 benchmark 的结果更加接近真实情况就需要进行预热。 http://blog.dyngr.com/blog/2016/10/29/introduction-of-jmh/

2014-12-03 · 1 min · 15 words · -

gradle cmd utf8 error

gradle cmd utf8 error export GRADLE_OPTS="-Dfile.encoding=utf-8" add tasks.withType(JavaCompile) { options.encoding = "UTF-8" } The file.encoding system property needs to be set right when the JVM executing the Gradle build (e.g. the Gradle Daemon) starts up. One way to achieve this is with export GRADLE_OPTS="-Dfile.encoding=utf-8". Another way that might work is to add systemProp.file.encoding=utf-8to gradle.properties. Of course this assumes that the build script files are actually using utf-8 encoding. To see what your platform’s (and therefore Gradle’s) default encoding is, print out the system property’s value in a build script. ...

2014-12-03 · 1 min · 91 words · -

Gradle的Properties

Gradle的Properties http://hugozhu.myalert.info/2014/07/23/47-use-gradle-properties-to-set-alternative-android-build-tools.html 目录: 问题背景 解决方案 修改build.gradle使用变量设置版本号 在setting.gradle中设置缺省的版本 在gradle.properties文件中重载版本号 命令行使用 参考链接 问题背景 团队一起在开发一个Android项目,工程师有的使用Eclipse,有个使用Intellij IDEA,有的使用Android Studio。每个人安装的Android SDK build-tools可能都不一样,有的是19.0.3,有的是19.1.0,不同版本的build-tools对Gradle Plugin也有相应的要求,如19.0.3对应的是com.android.tools.build:gradle:0.10.+,19.1.0对应的是com.android.tools.build:gradle:0.12.+,下面是一个典型的build.gradle配置文件。 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.10.+' } } apply plugin: 'android-library' android { compileSdkVersion 19 buildToolsVersion 19.0.3 defaultConfig { minSdkVersion 8 targetSdkVersion 19 } } 在合作开发中遇到的一个尴尬的问题是,IDEA最新版还不能很好的支持Gradle Plugin 0.12+,而Android Studio最新版则要求使用0.12+。大家又共用一个Git仓库。可能的解决方案是, 从 Git checkout 出来的项目需要有一个基础的版本号,但是开发者可以在本地通过一处文件 (不check in到git) 来重载版本号。 解决方案 Gradle支持三种Properties, 这三种Properties的作用域和初始化阶段都不一样,下面分别列出了其部分特点。: ...

2014-12-03 · 1 min · 191 words · -

Create spring mvc project

Create spring mvc project gradle plugin: apply plugin: ‘idea’ apply plugin: ’eclipse' apply plugin: ‘war’ gradle dependencies: compile(“org.springframework:spring-core:$versionSpring”, “org.springframework:spring-context:$versionSpring”, “org.springframework:spring-web:$versionSpring”, “org.springframework:spring-webmvc:$versionSpring”, “org.springframework:spring-jdbc:$versionSpring”, “org.springframework:spring-orm:$versionSpring” ) web.xml create web.xml in …/webapp/WEB-INF/ http://www.wiloon.com/?p=3459 add context loader listener org.springframework.web.context.ContextLoaderListener spring schema index http://www.springframework.org/schema/beans/ Create controller /JavaEEX/src/main/java/com/wiloon/javaeex/controller/FooController.java create springMvc.xml for eclipse project convert project to web project

2014-12-03 · 1 min · 52 words · -

POSIX

POSIX Pronunciation: /ˈpɒz.ɪks/, pozz-icks POSIX代表 “可移植操作系统接口” Portable Operation System Interface。 在网上查到对于POSIX的介绍,来自《Linux内核设计与实现 (第2版) 》的第5章,就这部分来看,这本书估计不错。 一般情况下,应用程序通过应用编程接口(API)而不是直接通过 系统调用 来编程。这点很重要,因为应用程序使用的这种编程接口实际上并不需要和内核提供的系统调用对应。 一个API定义了一组应用程序使用的编程接口。它们可以实现成一个系统调用,也可以通过调用多个系统调用来实现, 而完全不使用任何系 统调用也不存在问题。 实际上,API可以在各种不同的操作系统上实现,给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。 在Unix世界中,最流行的应用编程接口是基于POSIX标准的。从纯技术的角度看,POSIX是由IEEE的一组标准组成,其目标是提供一套大体上基于Unix的可移植操作系统标准。Linux是与POSIX兼容的。 POSIX是说明API和系统调用之间关系的一个极好例子。在大多数Unix系统上,根据POSIX而定义的API函数和系统调用之间有着直接关 系。实际上,POSIX标准就是仿照早期Unix系统的界面建立的。另一方面,许多操作系统,像Windows NT,尽管和Unix没有什么关系,也提供了与POSIX兼容的库。 Linux的系统调用像大多数Unix系统一样,作为C库的一部分提供 , C库实现了Unix系统的主要API,包括标准C库函数和系统调用。所有的C程序都可以使用C库,而由于C语言本身的特点,其他语言也可以很方便地把它们封装起来使用。此外,C库提供了POSIX的绝大部分API。 图5-1 调用printf()函数时,应用程序、C库和内核之间的关系 从程序员的角度看,系统调用无关紧要;他们只需要跟API打交道就可以了。相反,内核只跟系统调用打交道;库函数及应用程序是怎么使用系统调用不是内核所关心的。 ———这是分割线———– 简单总结: 完成同一功能,不同内核提供的系统调用 (也就是一个函数) 是不同的,例如创建进程,linux下是fork函数,windows下是creatprocess函数。好,我现在在linux下写一个程序,用到fork函数,那么这个程序该怎么往windows上移植?我需要把源代码里的fork通通改成creatprocess,然后重新编译… posix标准的出现就是为了解决这个问题。linux和windows都要实现基本的posix标准,linux把fork函数封装成posix_fork (随便说的) ,windows把creatprocess函数也封装成posix_fork,都声明在unistd.h里。这样,程序员编写普通应用时候,只用包含unistd.h,调用posix_fork函数,程序就在源代码级别可移植了。 POSIX解决什么问题 POSIX解决什么问题 一般情况下,应用程序通过应用编程接口(API)而不是直接通过系统调用来编程(即并不需要和内核提供的系统调用来编程)。一个API定义了一组应用程序使用的编程接口。它们可以实现成调用一个系统,也可以通过调用多个系统来实现,而完全不使用任何系统调用也不存在问题。实际上,API可以在各种不同的操作系统上实现给应用程序提供完全相同的接口,而它们本身在这些系统上的实现却可能迥异。如下图,当应用程序调用printf()函数时,printf函数会调用C库中的printf,继而调用C库中的write,C库最后调用内核的write()。 应用程序、C库和内核之间的关系 从程序员的角度看,系统调用无关紧要,只需要跟API打交道。相反,内核只跟系统调用打交道,库函数及应用程序是怎么系统调用不是内核所关心的。 完成同一功能,不同内核提供的系统调用 (一个函数) 是不同的,例如创建进程,linux下是fork函数,windows下是creatprocess函数。好,我现在在linux下写一个程序,用到fork函数,那么这个程序该怎么往windows上移植?我需要把源代码里的fork通通改成creatprocess,然后重新编译… 主流的操作系统有两种,一种是Windows系统,另一种是Linux系统。由于操作系统的不同,API又分为Windows API和Linux API。在Windows平台开发出来的软件在Linux上无法运行,在Linux上开发的软件在Windows上又无法运行,这就导致了软件移植困难,POSIX(Protabl Operation System 可移植操作系统规范)应运而生。 posix标准的出现就是为了解决这个问题。linux和windows都要实现基本的posix标准,linux把fork函数封装成posix_fork (随便说的) ,windows把creatprocess函数也封装成posix_fork,都声明在unistd.h里。这样,程序员编写普通应用时候,只用包含unistd.h,调用posix_fork函数,程序就在源代码级别可移植了。 posix 是什么? 可移植操作系统接口Portable Operating System Interface of UNIX,POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称。 系统调用 (OS call) 系统调用,我们可以理解是操作系统为用户提供的一系列操作的接口 (API) ,这些接口提供了对系统硬件设备功能的操作。这么说可能会比较抽象,举个例子,我们最熟悉的 hello world 程序会在屏幕上打印出信息。程序中调用了 printf() 函数,而库函数 printf 本质上是调用了系统调用 write() 函数,实现了终端信息的打印功能。 ...

2014-12-01 · 2 min · 225 words · -

websocket

websocket https://docs.oracle.com/javaee/7/tutorial/doc/websocket.htm http://blog.csdn.net/chifengxin/article/details/14521093 https://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/ 如何理解 TCP/IP, SPDY, WebSocket 三者之间的关系? http://www.zhihu.com/question/20097129

2014-11-29 · 1 min · 10 words · -

dotnet

dotnet dotnet-sdk-8.0 (开发工具) ├── dotnet-runtime-8.0 (运行时) ├── dotnet-targeting-pack-8.0 (编译支持) ├── dotnet-templates-8.0 (模板) ├── dotnet-apphost-pack-8.0 (发布支持) └── dotnet-host-8.0 (主机) └── dotnet-hostfxr-8.0 (解析器) # 1. 下载并安装Microsoft GPG密钥和仓库配置 wget https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb # wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb rm packages-microsoft-prod.deb # 2. 更新包列表 sudo apt update # 3. 安装.NET 7.0 SDK sudo apt install dotnet-sdk-7.0 ### ------------------------------- sudo dpkg -i packages-microsoft-prod.deb sudo apt-get install -y dotnet-sdk-7.0 sudo apt-get update && sudo apt-get install -y apt-transport-https dotnet --version dotnet --info apt-cache policy dotnet-sdk-8.0 sudo apt install dotnet-sdk-7.0 -y dotnet --list-runtimes dotnet build --verbosity minimal dotnet run --dry-run

2014-11-29 · 1 min · 96 words · -

canvas

canvas 是html5出现的新标签,像所有的dom对象一样它有自己本身的属性、方法和事件,其中就有绘图的方法,js能够调用它来进行绘图

2014-11-29 · 1 min · 2 words · -

java copy file

java copy file http://www.oschina.net/question/565065_58510 private static void nioTransferCopy(File source, File target) { FileChannel in = null; FileChannel out = null; FileInputStream inStream = null; FileOutputStream outStream = null; try { inStream = new FileInputStream(source); outStream = new FileOutputStream(target); in = inStream.getChannel(); out = outStream.getChannel(); in.transferTo(0, in.size(), out); } catch (IOException e) { e.printStackTrace(); } finally { close(inStream); close(in); close(outStream); close(out); } }

2014-11-27 · 1 min · 62 words · -

Java Callable, Future 和 FutureTask

Java Callable, Future 和 FutureTask 创建线程有两种方式,一种是直接继承 Thread,另外一种就是实现 Runnable 接口。 这两种方式都有一个缺陷就是: 在执行完任务之后无法获取执行结果。 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。 而自从Java 1.5 开始,JDK 提供了 Callable 和 Future, 通过它们可以在任务执行完毕之后得到任务执行结果。 今天我们就来讨论一下 Callable、Future 和 FutureTask 三个类的使用方法。以下是本文的目录大纲: Callable 与 Runnable Future FutureTask 使用示例 Callable 与 Runnable java.lang.Runnable 是一个接口,里面只声明了一个 run() 方法 public interface Runnable { public abstract void run(); } 由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。 Callable 位于 java.util.concurrent 包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做 call(): public interface Callable<V> { V call() throws Exception; } 可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。 那么怎么使用 Callable 呢? 一般情况下是配合 ExecutorService 来使用的,在 ExecutorService 接口中声明了若干个 submit 方法的重载版本: ...

2014-11-26 · 2 min · 396 words · -

Java assert

Java assert http://lavasoft.blog.51cto.com/62575/43735 一、概述 在C和C++语言中都有assert关键,表示断言。 在Java中,同样也有assert关键字,表示断言,用法和含义都差不多。 二、语法 在Java中,assert关键字是从JAVA SE 1.4 引入的,为了避免和老版本的Java代码中使用了assert关键字导致错误,Java在执行的时候默认是不启动断言检查的 (这个时候,所有的断言语句都将忽略!) ,如果要开启断言检查,则需要用开关-enableassertions或-ea来开启。 assert关键字语法很简单,有两种用法: assert <boolean表达式> 如果<boolean表达式>为true,则程序继续执行。 如果为false,则程序抛出AssertionError,并终止执行。 assert <boolean表达式> : <错误信息表达式> 如果<boolean表达式>为true,则程序继续执行。 如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。 三、应用实例 下面给出一个例子,通过例子说明其用法: public class AssertFoo { public static void main(String args[]) { //断言1结果为true,则继续往下执行 assert true; System.out.println(“断言1没有问题,Go!”); System.out.println("\n-----------------\n"); //断言2结果为false,程序终止 assert false : "断言失败,此表达式的信息将会在抛出异常的时候输出!"; System.out.println("断言2没有问题,Go!"); } } 保存代码到C:\AssertFoo.java,然后按照下面的方式执行,查看控制台输出结果: 编译程序: C:>javac AssertFoo.java 默认执行程序,没有开启-ea开关: C:>java AssertFoo 断言1没有问题,Go! 断言2没有问题,Go! 开启-ea开关,执行程序: C:>java -ea AssertFoo 断言1没有问题,Go! Exception in thread “main” java.lang.AssertionError: 断言失败,此表达式的信息将 会在抛出异常的时候输出! at AssertFoo.main(AssertFoo.java:10) ...

2014-11-26 · 1 min · 85 words · -

mutex, 锁

mutex, 锁 Mutual exclusion (或者锁) 的实现有硬件实现和软件实现, 软件实现是通过一些特别的算法譬如 Peterson’s algorithm,这类软件实现通常比硬件实现需要更多的内存,而且由于现代计算机的乱序执行,需要手动加memory barrier来保证memory ordering,这里暂时不做讨论纯软件实现。CPU如果提供一些用来构建锁的atomic指令,一般会更高效一些。 锁的本质 所谓的锁,在计算机里本质上就是一块内存空间。当这个空间被赋值为1的时候表示加锁了,被赋值为0的时候表示解锁了,仅此而已。多个线程抢一个锁,就是抢着要把这块内存赋值为1。在一个多核环境里,内存空间是共享的。每个核上各跑一个线程,那如何保证一次只有一个线程成功抢到锁呢?你或许已经猜到了,这必须要硬件的某种 guarantee。具体的实现如下。 硬件 CPU如果提供一些用来构建锁的 atomic 指令, 譬如 x86 的 CMPXCHG (加上LOCK prefix), 能够完成 atomic 的 compare-and-swap (CAS), 用这样的硬件指令就能实现 spin lock. 本质上 LOCK 前缀的作用是锁定系统总线 (或者锁定某一块cache line) 来实现atomicity,可以了解下基础的缓存一致协议譬如MSEI。简单来说就是,如果指令前加了LOCK前缀,就是告诉其他核,一旦我开始执行这个指令了,在我结束这个指令之前,谁也不许动。缓存一致协议在这里面扮演了重要角色,这里先不赘述。这样便实现了一次只能有一个核对同一个内存地址赋值。 操作系统 mutex在linux内核中由 futex 系统调用支撑,如果没有竞争不需要陷入内核;内核的主要是 futex_wait/wake 函数配合上层完成业务逻辑; 一个 spin lock 就是让没有抢到锁的线程不断在 while 里面循环进行 compare-and-swap, 燃烧CPU, 浪费青春, 直到前面的线程放手 (对应的内存被赋值0) 。这个过程不需要操作系统的介入,这是运行程序和硬件之间的故事。如果需要长时间的等待,这样反复CAS轮询就比较浪费资源,这个时候程序可以向操作系统申请被挂起,然后持锁的线程解锁了以后再通知它。这样CPU就可以用来做别的事情,而不是反复轮询。 但是OS切换线程也需要一些开销,所以是否选择被挂起,取决于大概是否需要等很长时间,如果需要,则适合挂起切换为别的线程。线程向操作系统请求被挂起是通过一个系统调用,在linux上的实现就是 futex, 宏观来讲, OS需要一些全局的数据结构来记录一个被挂起线程和对应的锁的映射关系,这样一个数据结构天然是全局的,因为多个OS线程可能同时操作它。所以,实现高效的锁本身也需要锁。有没有一环套一环的感觉?futex的巧妙之处就在于,它知道访问这个全局数据结构不会太耗时,于是futex里面的锁就是spin lock。linux上pthread mutex 的实现就是用的 futex 。更多精彩内存参考talk: https://www.infoq.com/presentations/go-locks/ 用户态的锁 像Goroutine线程就是go runtime来调度的,而不是OS,所以go routine线程切换的开销要远小于OS线程切换的开销。Goroutine的本质就是一个coroutine,这种轻量的线程在很多语言的runtime里面都有实现。最近Java也有了 (project loom) 。 ...

2014-11-19 · 3 min · 484 words · -

java 对象

java 对象 http://www.jianshu.com/p/ebaa1a03c594 Java程序执行时,第一步系统创建虚拟机进程,然后虚拟器用类加载器Class Loader加载java程序类文件到方法区。 方法区放哪些东西? 存放加载过的类信息、常量、静态变量、及jit编译后的代码 (类方法) 等数据的内存区域。它是线程共享的。 方法区存放的信息包括: 类的基本信息、运行时常量池、变量字段信息、方法信息等。这部分的详细介绍看下面链接的文章。 详细Java程序运行的内存结构介绍 点此处 简要过程: 类加载完成后,主线程运行static main () 时在虚拟机栈中建栈帧,压栈。 执行到new Object () 时,在堆heap里创建对象。 对象创建的过程就是堆上分配实例对象内容空间的过程,在堆中对象内存空间的具体结构如下: 对象头 这个头包括两个部分,第一部分用于存储自身运行时的数据例如GC标志位、哈希码、锁状态等信息。第二部分存放指向方法区类静态数据的指针。 实例变量 存放类的属性数据信息,包括父类的属性信息。如果是数组的实例部分还包括数组的长度。这部分内存按4字节对齐。 填充数据 这是因为虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。HotSpot VM的自动内存管理要求对象起始地址必须是8字节的整数倍。对象头本身是8的倍数,当对象的实例变量数据不是8的倍数,便需要填充数据来保证8字节的对齐。另外,堆上对象内存的分配是并发进行的. 然后执行类的构造函数初始化。 Java虚拟机规范规定该区域可抛出OutOfMemoryError。 详细步骤 例如: Dog dog= new Dog(); 当虚拟机执行到new指令时,它先在常量池中查找"Dog",看能否定位到Dog类的符号引用;如果能,说明这个类已经被加载到方法区了,则继续执行。如果没有,就让Class Loader先执行类的加载。 然后,虚拟机开始为该对象分配内存,对象所需要的内存大小在类加载完成后就已经确定了。这时候只要在堆中按需求分配空间即可。具体分配内存时有两种方式,第一种,内存绝对规整,那么只要在被占用内存和空闲内存间放置指针即可,每次分配空间时只要把指针向空闲内存空间移动相应距离即可,当某对象被GC回收后,则需要进行某些对象内存的迁移。第二种,空闲内存和非空闲内存夹杂在一起,那么就需要用一个列表来记录堆内存的使用情况,然后按需分配内存。 对于多线程的情况,如何确保一个线程分配了对象内存但尚未修改内存管理指针时,其他线程又分配该块内存而覆盖的情况?有一种方法,就是让每一个线程在堆中先预分配一小块内存 (TLAB本地线程分配缓冲) ,每个线程只在自己的内存中分配内存。但对象本身按其访问属性是可以线程共享访问的。 内存分配到后,虚拟机将分配的内存空间都初始化为零值(不包括对象头)。实例变量按变量类型初始化相应的默认值 (数值型为0,boolan为false) ,所以实例变量不赋初值也能使用。接着设置对象头信息,比如对象的哈希值,GC分代年龄等。 从虚拟机角度,此时一个新的对象已经创建完成了。但从我们程序运行的角度,新建对象才刚刚开始,对象的构造方法还没有执行。只有执行完构造方法,按构造方法进行初始化后,对象才是彻底创建完成了。 构造函数的执行还涉及到调用父类构造器,如果没有显式声明调用父类构造器,则自动添加默认构造器。 到此,new运算符可以返回堆中这个对象的引用了。 此刻,会根据dog这个变量是实例变量、局部变量或静态变量的不同将引用放在不同的地方: 如果dog局部变量,dog变量在栈帧的局部变量表,这个对象的引用就放在栈帧。 如果dog是实例变量,dog变量在堆中,对象的引用就放在堆。 如果dog是静态变量,dog变量在方法区,对象的引用就放在方法区。 Java有三种方法可以创建对象实例。 new 通常都是使用java的关键字new来创建对象实例。 若有一个Something类,则可以通过下面的语句创建Something类的对象实例并指定到变量obj。 Something something New = new Something(); 通过new创建对象实例必须把类名写在原代码里面。 clone 若程序写成如下,则可以根据当前对象 (this) 建立一个新实例对象 (没有调用构造函数) . ...

2014-11-19 · 2 min · 271 words · -

Chrome 运行Android应用

Chrome 运行Android应用 说到Chrome运行android程序,不得不说一个东西,那就是"Android Runtime for Chrome (ARC) “,ARC是Google最新推出的 Chrome OS 运行Android程序的运行时。ARC基于Google的Native Client(NaCl)功能,其允许通过浏览器来运行原生代码(通常是C或C++),同时具备Chrome所提供的同等安全性。显然,NaCl扩展是可以做到跨平台的,这意味着它能够在PC、Mac、以及Linux等系统的桌面版Chrome浏览器上运行。 但遗憾的是,ARC已经被打上了"Chrome OS专属"的标记,只能运行在Chrome上,并且只能运行Google提供的四款Android App,不能运行其他的。所以一般人无法在桌面版Chrome浏览器上使用。值得庆幸的是,一名叫做 Vladikoff 的黑客,已经突破了这些限制。首先,他实现了如何让Chrome OS能加载任何Android App,而不仅仅局限于官方指定的四款App,而现在,他取得了更大的突破,让Android App工作在Windows,Mac和Linux操作系统当中。 Vladikoff做了一个定制版本的ARC,称之为"ARChon”,可以在Windows,Mac和Linux操作系统当中Chrome37及以上版本的Chrome浏览器中可运行任何Android应用程序,但是,ARC不支持原始的Android应用程序包 (APK) ,它们需要被转换成一个Chrome扩展,好在Vladikoff提供了一个名为chromeos-apk 的转换工具,可以把Apk文件转换成Chrome扩展。 http://my.oschina.net/fants/blog/323672

2014-11-19 · 1 min · 25 words · -

DMA

DMA DMA的基本定义 DMA,全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。 DMA的主要特征 ·每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发,这些功能通过软件来配置。 ·在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推)。 ·独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。 ·支持循环的缓冲器管理。 ·每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。 ·存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输。 ·闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。 ·可编程的数据传输数目:最大为65535(0xFFFF)。 STM32F411x系列芯片DMA控制器 DMA的工作框图如下图所示。DMA控制器和Cortex™-M4核心共享系统数据总线,执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求会暂停CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。 DMA控制器传输作为AHB主设备操作直接存储器,它可以控制AHB总线的控制矩阵以启动AHB传送。它可以执行以下信息交换: •外设到内存 •内存到外设 •内存到内存 DMA控制器提供两个AHB主端口:AHB内存端口(用于连接到内存)和AHB外设端口(用于连接到外设)。 但是,为了允许内存到内存的传输,AHB外设端口也必须可以访问内存。AHB从端口用于对DMA控制器的编程控制(仅支持32位访问)。 DMA处理 对于两个DMA控制器的系统实现如下图所示。DMA1控制器AHB外设端口没有像DMA2控制器那样连接到总线矩阵,所以只有DMA2能够执行内存到内存的数据传输。 DMA事务由给定数量的数据传输序列组成,可以传输的数据项的数量及其宽度(8位,16位或32位)可以通过软件编程实现。每个DMA传输都包含三个操作步骤: ·从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元; ·存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元; ·执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。 通道选择 事件发生后,外设向DMA控制器发送请求信号,DMA控制器根据通道优先级来处理请求。当DMA控制器访问外围设备时,确认信号即由DMA控制器发送到外围设备,一旦外围设备从DMA控制器收到确认信号,它就会释放其请求,当外设取消了该请求后,DMA控制器将释放确认信号。 如果外设还有更多请求,它可以启动下一轮请求操作。 每个数据流流都与一个DMA请求相关,该DMA请求可以从8个可能的通道请求中选择,由DMA_SxCR寄存器中的CHSEL [2:0]位控制。 来自外设(TIM,ADC,SPI,I2C等)的8个请求独立连接到每个通道如下图所示: 仲裁器 仲裁器根据通道请求的优先级来启动外设/存储器的访问。优先权管理分2个阶段: ·软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:最高优先级、高优先级、中等优先级、低优先级; ·硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。比如:如果软件优先级相同,通道2优先于通道4。 注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。 DMA通道数据量 每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535(0xFFFF)。包含要传输的数据项数量的寄存器,在每次传输后递减。外设和存储器的传输数据量可以通过DMA_CCRx寄存器中的PSIZE和MSIZE位编程得到。 DMA传输模式 数据传输源和目的地都可以寻址整个4 GB区域中的外围设备和存储器,其地址介于0x0000 0000和0xFFFF FFFF之间。传输方向使用DMA_SxCR寄存器中的DIR [1:0]位进行配置,并提供三种可能性:存储器到外围设备,外围设备到存储器或存储器到存储器的传输。 外设到存储器模式 使能此模式时(通过将DMA_SxCR寄存器中的EN位置1),每次发生外设请求时,数据流都会启动传输从数据源来填充到FIFO。当达到FIFO的阈值水平时,FIFO的内容被清空并存储到目标地址。 当外设请求结束传输时(对于外设流控制器),或当DMA_SxNDTR寄存器达到零时或将DMA_SxNDTR寄存器中的EN位清零,则传输停止。 当赢得了相应流的仲裁时,该数据流通道才可以访问AHB源或目标端口,使用DMA_SxCR寄存器中的PL [1:0]位,为每个数据流通道的优先级进行仲裁。 存储器到外设模式 使能该模式时(通过将DMA_SxCR寄存器中的EN位置1),该数据流通道立即启动传输,以完全填充FIFO。每次发生外设请求时,FIFO的内容都会被清空并存储到目的地。当FIFO未满时,会从内存中重新加载数据。 当外设请求结束传输时(对于外设流控制器),或者当DMA_SxNDTR寄存器达到零时或将DMA_SxNDTR寄存器中的EN位清零,则传输停止。 当赢得了相应流的仲裁时,该数据流通道才可以访问AHB源或目标端口,使用DMA_SxCR寄存器中的PL [1:0]位,为每个数据流通道的优先级进行仲裁。 存储器到存储器模式 配置同上。存储器到存储器模式不能与循环模式同时使用。 循环模式 循环模式可用于处理循环缓冲区和连续数据流(例如ADC扫描模式)。 可以使用DMA_SxCR寄存器中的CIRC位来启用此功能。使能循环模式后,将在数据流通道配置阶段使用初始值自动装载要传输的数据,并且DMA请求将继续。 中断 每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。 对于每个DMA数据流通道,可以在以下事件上产生中断: •达到半转移 •转移完成 •传输错误 ...

2014-11-17 · 1 min · 171 words · -

Google Guava

Google Guava Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你的JAVa代码更加优雅,更加简洁,让你工作更加轻松愉悦。下面我们就开启优雅Java编程学习之旅! **项目相关信息: ** 官方首页: http://code.google.com/p/guava-libraries 官方下载: http://code.google.com/p/guava-libraries/downloads/list 官方文档: http://docs.guava-libraries.googlecode.com/git/javadoc http://www.ostools.net/apidocs/apidoc?api=guava **源码包的简单说明: ** com.google.common.annotations: 普通注解类型。 com.google.common.base: 基本工具类库和接口。 com.google.common.cache: 缓存工具包,非常简单易用且功能强大的JVM内缓存。 com.google.common.collect: 带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合。 com.google.common.eventbus: 发布订阅风格的事件总线。 com.google.common.hash: 哈希工具包。 com.google.common.io: I/O工具包。 com.google.common.math: 原始算术类型和超大数的运算工具包。 com.google.common.net: 网络工具包。 com.google.common.primitives: 八种原始类型和无符号类型的静态工具包。 com.google.common.reflect: 反射工具包。 com.google.common.util.concurrent: 多线程工具包。 **类库使用手册: ** **一. 基本工具类: **让使用Java语言更令人愉悦。 使用和避免 null: null 有语言歧义, 会产生令人费解的错误, 反正他总是让人不爽。很多 Guava 的工具类在遇到 null 时会直接拒绝或出错,而不是默默地接受他们。 前提条件: 更容易的对你的方法进行前提条件的测试。 ...

2014-11-12 · 1 min · 211 words · -

Google Guava

Google Guava Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你的JAVa代码更加优雅,更加简洁,让你工作更加轻松愉悦。下面我们就开启优雅Java编程学习之旅! **项目相关信息: ** 官方首页: http://code.google.com/p/guava-libraries 官方下载: http://code.google.com/p/guava-libraries/downloads/list 官方文档: http://docs.guava-libraries.googlecode.com/git/javadoc http://www.ostools.net/apidocs/apidoc?api=guava **源码包的简单说明: ** com.google.common.annotations: 普通注解类型。 com.google.common.base: 基本工具类库和接口。 com.google.common.cache: 缓存工具包,非常简单易用且功能强大的JVM内缓存。 com.google.common.collect: 带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合。 com.google.common.eventbus: 发布订阅风格的事件总线。 com.google.common.hash: 哈希工具包。 com.google.common.io: I/O工具包。 com.google.common.math: 原始算术类型和超大数的运算工具包。 com.google.common.net: 网络工具包。 com.google.common.primitives: 八种原始类型和无符号类型的静态工具包。 com.google.common.reflect: 反射工具包。 com.google.common.util.concurrent: 多线程工具包。 **类库使用手册: ** **一. 基本工具类: **让使用Java语言更令人愉悦。 使用和避免 null: null 有语言歧义, 会产生令人费解的错误, 反正他总是让人不爽。很多 Guava 的工具类在遇到 null 时会直接拒绝或出错,而不是默默地接受他们。 前提条件: 更容易的对你的方法进行前提条件的测试。 ...

2014-11-12 · 1 min · 211 words · -