首页 文章 精选 留言 我的

精选列表

搜索[原理],共10002篇文章
优秀的个人博客,低调大师

[转载] Spark Streaming 设计原理

本文转自:https://zhuanlan.zhihu.com/p/47838090. 本站转载已经过作者授权。如需转载,请和原作者联系。 最近两年流式计算又开始逐渐火了起来,说到流式计算主要分两种:continuous-based 和 micro-batch。最近在使用基于 micro-batch 模式的 Spark Streaming,正好结合论文介绍一下。这里说的论文是 2013 年发布的 《Discretized Streams: Fault-Tolerant Streaming Computation at Scale》,虽然是 2013 年发表的论文,但是系统的核心逻辑基本没怎么变化,对于理解 Spark Streaming 的系统设计、工作方式还是很有帮助的。注:Spark 在 2016 年推出了 Structur

优秀的个人博客,低调大师

SpringBoot 原理之 yaml 解析

导入 SpringBoot 的 snakeyaml 解析包 编写 yaml 文件 编写 yaml 文件对应的 class 编写 main 进行获取 导入 SpringBoot 的 snakeyaml 解析包 <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.23</version> </dependency> 编写 yaml 文件 id: 110 name: 'stuName' gender: '男' clas: id: 110 name: 'classesName' describe: 'describe' 编写 yaml 文件对应的 class public class Student { private Integer id; private String name; private String gender; private Classes clas; // getter(), setter(), toString(); } class Classes { private Integer id; private String name; private String describe; // getter(), setter(), toString(); } 编写 main 进行获取 public static void main(String[] args) { Yaml yaml = new Yaml(); Student result = yaml.loadAs(thisClass.class.getClassLoader() .getResourceAsStream("springboot.yaml"), Student.class); System.out.println(result); }

优秀的个人博客,低调大师

Kubernetes API server工作原理

作为Kubernetes的使用者,每天用得最多的命令就是kubectl XXX了。 kubectl其实就是一个控制台,主要提供的功能: 1. 提供Kubernetes集群管理的REST API接口,包括认证授权、数据校验以及集群状态变更; 2. 提供其他模块之间的数据交互和通信的枢纽(其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd) 也就是说,我们在终端里输入的每个kubectl命令,实际上都是一个发往Kubernetes API server的Restful API调用。 我们可以做个实验: kubectl get secret -v=9, 通过-v=9设置最高级别的trace: 从输出观察到为了取回所有的secret而进行的API server的调用url:https://xxxx/api/v1/

优秀的个人博客,低调大师

java NIO 运行原理介绍

开篇 回想研究生期间在H3C做项目的时候第一次接触epoll的异步事件,心血来潮看了下java的NIO的实现,希望同样感兴趣的人一起看看。Netty是java NIO的集大成者,一定要看看。 java NIO server demo socket server端工作标准流程 创建socket: 创建ServerSocketChannel,通过ServerSocketChannel.open()方法。 绑定socket:ServerSocketChannel绑定端口,通过serverSocketChannel.bind()方法。 前置准备: 创建selector对象,通过Selector.open()方法。 前置准备: 注册Channel到selector并绑定事件,通过serverSocketChannel.register()。 监听端口号: 通过listen()方法开始进入监听。 处理事件: while循环中等待select操作返回区分连接还是数据进行不同处理。 public class NIOServer { private Selector selector; public void initServer(int port) throws IOException { // 获得一个ServerSocketChannel通道 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 设置通道为非阻塞 serverSocketChannel.configureBlocking(false); // 将该通道对应的ServerSocket绑定到port端口 serverSocketChannel.bind(new InetSocketAddress(port)); // 获得一个通道管理器 this.selector = Selector.open(); // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { System.out.println("服务端启动成功!"); // 轮询访问selector while (true) { // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞 selector.select(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); if (key.isAcceptable()) {// 客户端请求连接事件 ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 获得和客户端连接的通道 SocketChannel channel = server.accept(); // 设置成非阻塞 channel.configureBlocking(false); // 在这里可以给客户端发送信息哦 channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息") .getBytes("utf-8"))); // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) {// 获得了可读的事件 read(key); } } } } public void read(SelectionKey key) throws IOException { // 服务器可读取消息:得到事件发生的Socket通道 SocketChannel channel = (SocketChannel) key.channel(); // 创建读取的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(512); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服务端收到信息:" + msg); ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8")); channel.write(outBuffer);// 将消息回送给客户端 } public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } } ServerSocketChannel和Selector初始化过程 在java NIO Server的标准过程中,有两个核心的操作需要深入分析一下,分别是ServerSocketChannel.open() 和 Selector.open()两个过程,这里针对这两个对象的初始化流程进行下细致的分解。 // ServerSocketChannel的初始化过程 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // Selector的初始化过程 selector = Selector.open(); 通用逻辑抽取 ServerSocketChannel.open()=SelectorProvider.provider().openServerSocketChannel() Selector.open()=SelectorProvider.provider().openSelector() 两者有共同点在于都调用了SelectorProvider.provider()方法,所以先把相同部分进行分析。 public abstract class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel { protected ServerSocketChannel(SelectorProvider provider) { super(provider); } public static ServerSocketChannel open() throws IOException { return SelectorProvider.provider().openServerSocketChannel(); } } public abstract class Selector implements Closeable { protected Selector() { } public static Selector open() throws IOException { return SelectorProvider.provider().openSelector(); } } SelectorProvider对象创建 SelectorProvider.provider()方法会在内部创建唯一的SelectorProvider对象,通过锁来保证创建唯一对象。 SelectorProvider对象通过DefaultSelectorProvider.create()方法进行创建。 DefaultSelectorProvider.create()方法内部根据实际系统创建不同的对象,以linux环境中EPollSelectorProvider对象为例继续分析。 public abstract class SelectorProvider { private static final Object lock = new Object(); private static SelectorProvider provider = null; public static SelectorProvider provider() { synchronized (lock) { if (provider != null) return provider; return AccessController.doPrivileged( new PrivilegedAction<SelectorProvider>() { public SelectorProvider run() { if (loadProviderFromProperty()) return provider; if (loadProviderAsService()) return provider; provider = sun.nio.ch.DefaultSelectorProvider.create(); return provider; } }); } } } public class DefaultSelectorProvider { public static SelectorProvider create() { String osname = AccessController.doPrivileged( new GetPropertyAction("os.name")); if ("SunOS".equals(osname)) { return new sun.nio.ch.DevPollSelectorProvider(); } // use EPollSelectorProvider for Linux kernels >= 2.6 if ("Linux".equals(osname)) { String osversion = AccessController.doPrivileged( new GetPropertyAction("os.version")); String[] vers = osversion.split("\\.", 0); if (vers.length >= 2) { try { int major = Integer.parseInt(vers[0]); int minor = Integer.parseInt(vers[1]); if (major > 2 || (major == 2 && minor >= 6)) { return new sun.nio.ch.EPollSelectorProvider(); } } catch (NumberFormatException x) { // format not recognized } } } return new sun.nio.ch.PollSelectorProvider(); } } EPollSelectorProvider的操作过程 SelectorProvider.provider().openServerSocketChannel()调用EPollSelectorProvider的openServerSocketChannel()方法返回EPollSelectorImpl对象。 SelectorProvider.provider().openSelector()调用EPollSelectorProvider的openSelector()方法返回ServerSocketChannelImpl对象。 继续分析ServerSocketChannelImpl对象和EPollSelectorImpl对象。 public class EPollSelectorProvider extends SelectorProviderImpl { public AbstractSelector openSelector() throws IOException { return new EPollSelectorImpl(this); } public Channel inheritedChannel() throws IOException { return InheritedChannel.getChannel(); } } public abstract class SelectorProviderImpl extends SelectorProvider { public DatagramChannel openDatagramChannel() throws IOException { return new DatagramChannelImpl(this); } public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { return new DatagramChannelImpl(this, family); } public Pipe openPipe() throws IOException { return new PipeImpl(this); } public abstract AbstractSelector openSelector() throws IOException; public ServerSocketChannel openServerSocketChannel() throws IOException { return new ServerSocketChannelImpl(this); } public SocketChannel openSocketChannel() throws IOException { return new SocketChannelImpl(this); } } EPollSelectorImpl对象 EPollSelectorImpl构造函数创建内部通信的socket对IOUtil.makePipe(false)。 EPollSelectorImpl的doSelect方法负责返回事件到来的fds。 EPollSelectorImpl的fdToKey的map保存fd和SelectionKey的映射。 class EPollSelectorImpl extends SelectorImpl { // File descriptors used for interrupt protected int fd0; protected int fd1; // The poll object EPollArrayWrapper pollWrapper; // Maps from file descriptors to keys private Map<Integer,SelectionKeyImpl> fdToKey; // True if this Selector has been closed private volatile boolean closed = false; // Lock for interrupt triggering and clearing private Object interruptLock = new Object(); private boolean interruptTriggered = false; EPollSelectorImpl(SelectorProvider sp) { super(sp); long pipeFds = IOUtil.makePipe(false); fd0 = (int) (pipeFds >>> 32); fd1 = (int) pipeFds; pollWrapper = new EPollArrayWrapper(); pollWrapper.initInterrupt(fd0, fd1); fdToKey = new HashMap<Integer,SelectionKeyImpl>(); } protected int doSelect(long timeout) throws IOException { if (closed) throw new ClosedSelectorException(); processDeregisterQueue(); try { begin(); // 等待事件到来,收集事件到来的socket的fd并用来处理 pollWrapper.poll(timeout); } finally { end(); } processDeregisterQueue(); // 更新需要写入的keys int numKeysUpdated = updateSelectedKeys(); if (pollWrapper.interrupted()) { // Clear the wakeup pipe pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0); synchronized (interruptLock) { pollWrapper.clearInterrupted(); IOUtil.drain(fd0); interruptTriggered = false; } } return numKeysUpdated; } private int updateSelectedKeys() { int entries = pollWrapper.updated; int numKeysUpdated = 0; for (int i=0; i<entries; i++) { int nextFD = pollWrapper.getDescriptor(i); SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD)); // ski is null in the case of an interrupt if (ski != null) { int rOps = pollWrapper.getEventOps(i); if (selectedKeys.contains(ski)) { if (ski.channel.translateAndSetReadyOps(rOps, ski)) { numKeysUpdated++; } } else { ski.channel.translateAndSetReadyOps(rOps, ski); if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) { // selectedKeys保存ski也就是事件到的socket连接 // ski的对象数据结构需要好好研究一下 selectedKeys.add(ski); numKeysUpdated++; } } } } return numKeysUpdated; } } ServerSocketChannelImpl对象 ServerSocketChannelImpl extends ServerSocketChannel ServerSocketChannel extends AbstractSelectableChannel ServerSocketChannelImpl对象提供bind()&accept()方法 ServerSocketChannelImpl的accept方法内部创建新连接的SocketChannelImpl对象返回 class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl { private final Object stateLock = new Object(); private SocketAddress localAddress; ServerSocket socket; ServerSocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); this.fd = Net.serverSocket(true); this.fdVal = IOUtil.fdVal(fd); this.state = ST_INUSE; } ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) throws IOException { super(sp); this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_INUSE; if (bound) localAddress = Net.localAddress(fd); } @Override public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { // 省略相关代码 } public SocketChannel accept() throws IOException { // 省略相关代码 } } public abstract class AbstractSelectableChannel extends SelectableChannel { protected AbstractSelectableChannel(SelectorProvider provider) { this.provider = provider; } public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { synchronized (regLock) { if (!isOpen()) throw new ClosedChannelException(); if ((ops & ~validOps()) != 0) throw new IllegalArgumentException(); if (blocking) throw new IllegalBlockingModeException(); SelectionKey k = findKey(sel); if (k != null) { k.interestOps(ops); k.attach(att); } if (k == null) { // New registration synchronized (keyLock) { if (!isOpen()) throw new ClosedChannelException(); k = ((AbstractSelector)sel).register(this, ops, att); addKey(k); } } return k; } } } select过程 select执行过程 执行selector.select()操作时实际是调用了子类实现的doSelect()方法。 进一步跟进子类的doSelect()方法。 abstract class SelectorImpl extends AbstractSelector { // 保存事件到来的keys protected Set<SelectionKey> selectedKeys; protected HashSet<SelectionKey> keys; private Set<SelectionKey> publicKeys; // Immutable private Set<SelectionKey> publicSelectedKeys; // Removal allowed, but not addition protected abstract int doSelect(long timeout) throws IOException; private int lockAndDoSelect(long timeout) throws IOException { synchronized (this) { if (!isOpen()) throw new ClosedSelectorException(); synchronized (publicKeys) { synchronized (publicSelectedKeys) { return doSelect(timeout); } } } } public int select(long timeout) throws IOException { if (timeout < 0) throw new IllegalArgumentException("Negative timeout"); return lockAndDoSelect((timeout == 0) ? -1 : timeout); } public int select() throws IOException { return select(0); } public Set<SelectionKey> selectedKeys() { if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException(); return publicSelectedKeys; } } pollWrapper.poll(timeout)以超时等待的形式等待epoll的消息通知。 通过updateSelectedKeys方法收集有事件到达的fds保存到selectedKeys。 class EPollSelectorImpl extends SelectorImpl { protected int doSelect(long timeout) throws IOException { if (closed) throw new ClosedSelectorException(); processDeregisterQueue(); try { begin(); // 等待事件到来,收集事件到来的socket的fd并用来处理 pollWrapper.poll(timeout); } finally { end(); } processDeregisterQueue(); // 更新需要写入的keys int numKeysUpdated = updateSelectedKeys(); if (pollWrapper.interrupted()) { // Clear the wakeup pipe pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0); synchronized (interruptLock) { pollWrapper.clearInterrupted(); IOUtil.drain(fd0); interruptTriggered = false; } } return numKeysUpdated; } private int updateSelectedKeys() { int entries = pollWrapper.updated; int numKeysUpdated = 0; for (int i=0; i<entries; i++) { int nextFD = pollWrapper.getDescriptor(i); SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD)); // ski is null in the case of an interrupt if (ski != null) { int rOps = pollWrapper.getEventOps(i); if (selectedKeys.contains(ski)) { if (ski.channel.translateAndSetReadyOps(rOps, ski)) { numKeysUpdated++; } } else { ski.channel.translateAndSetReadyOps(rOps, ski); if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) { // selectedKeys保存ski也就是事件到的socket连接 // ski的对象数据结构需要好好研究一下 selectedKeys.add(ski); numKeysUpdated++; } } } } return numKeysUpdated; } } accept过程 accept的过程很简单就是accept新socket并创建SocketChannelImpl返回即可。 SocketChannelImpl对象后面需要注册到Selector当中所以需要进一步分析。 public SocketChannel accept() throws IOException { // 省略相关代码 try { // 省略相关代码 // 新accept的socket放在newfd当中 n = accept0(this.fd, newfd, isaa); } } IOUtil.configureBlocking(newfd, true); InetSocketAddress isa = isaa[0]; // 通过SocketChannelImpl包装newfd对象 sc = new SocketChannelImpl(provider(), newfd, isa); // 省略相关代码 return sc; } } SocketChannelImpl对象 SocketChannelImpl可以理解为普通Socket的封装,包括read/write等方法 SocketChannelImpl extends SocketChannel extends AbstractSelectableChannel AbstractSelectableChannel提供register到selector对象的方法 class SocketChannelImpl extends SocketChannel implements SelChImpl { SocketChannelImpl(SelectorProvider sp) throws IOException { super(sp); this.fd = Net.socket(true); this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; } SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) throws IOException { super(sp); this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; if (bound) this.localAddress = Net.localAddress(fd); } SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress remote) throws IOException { super(sp); this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_CONNECTED; this.localAddress = Net.localAddress(fd); this.remoteAddress = remote; } public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { // 读数据的逻辑 } public int write(ByteBuffer buf) throws IOException { // 写数据的逻辑 } } register过程 register过程并没有调用epollCtl方法添加fd到selector当中 register过程真正是保存fd到待绑定的列表当中 在SelectorImpl中执行pollWrapper.poll(timeout)方法先把fd列表执行epollCtl添加selector当中,在通过epollWait获取事件到来 public abstract class AbstractSelectableChannel extends SelectableChannel { public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { synchronized (regLock) { if (!isOpen()) throw new ClosedChannelException(); if ((ops & ~validOps()) != 0) throw new IllegalArgumentException(); if (blocking) throw new IllegalBlockingModeException(); SelectionKey k = findKey(sel); if (k != null) { k.interestOps(ops); k.attach(att); } if (k == null) { // New registration synchronized (keyLock) { if (!isOpen()) throw new ClosedChannelException(); k = ((AbstractSelector)sel).register(this, ops, att); addKey(k); } } return k; } } } abstract class SelectorImpl extends AbstractSelector { protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment) { if (!(ch instanceof SelChImpl)) throw new IllegalSelectorException(); SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this); k.attach(attachment); synchronized (publicKeys) { implRegister(k); } k.interestOps(ops); return k; } } abstract class AbstractPollSelectorImpl extends SelectorImpl { protected void implRegister(SelectionKeyImpl ski) { synchronized (closeLock) { if (closed) throw new ClosedSelectorException(); // Check to see if the array is large enough if (channelArray.length == totalChannels) { // Make a larger array int newSize = pollWrapper.totalChannels * 2; SelectionKeyImpl temp[] = new SelectionKeyImpl[newSize]; // Copy over for (int i=channelOffset; i<totalChannels; i++) temp[i] = channelArray[i]; channelArray = temp; // Grow the NativeObject poll array pollWrapper.grow(newSize); } channelArray[totalChannels] = ski; ski.setIndex(totalChannels); // 核心的将channel添加到pollWrapper当中 pollWrapper.addEntry(ski.channel); totalChannels++; keys.add(ski); } } } class EPollArrayWrapper { int poll(long timeout) throws IOException { updateRegistrations(); updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd); for (int i=0; i<updated; i++) { if (getDescriptor(i) == incomingInterruptFD) { interruptedIndex = i; interrupted = true; break; } } return updated; } void updateRegistrations() { synchronized (updateList) { Updator u = null; while ((u = updateList.poll()) != null) { SelChImpl ch = u.channel; if (!ch.isOpen()) continue; // if the events are 0 then file descriptor is put into "idle // set" to prevent it being polled if (u.events == 0) { boolean added = idleSet.add(u.channel); // if added to idle set then remove from epoll if registered if (added && (u.opcode == EPOLL_CTL_MOD)) epollCtl(epfd, EPOLL_CTL_DEL, ch.getFDVal(), 0); } else { // events are specified. If file descriptor was in idle set // it must be re-registered (by converting opcode to ADD) boolean idle = false; if (!idleSet.isEmpty()) idle = idleSet.remove(u.channel); int opcode = (idle) ? EPOLL_CTL_ADD : u.opcode; epollCtl(epfd, opcode, ch.getFDVal(), u.events); } } } } }

优秀的个人博客,低调大师

Java常用算法原理剖析

用Java实现的所有算法(用于教育) 这些只是为了演示的目的。在Java标准库中有许多不同类型的实现,由于性能原因这些要好得多。 排序算法 气泡 从维基百科气泡排序,叫做下沉排序,是一种简单的排序算法,反复遍历要排序的列表,比较每一对相邻的项目,并在排序错误的情况下交换。遍历列表将被重复,直到不需要交换,这表明列表已被排序。 特性 最差情况下的性能O(n^2) 最佳案例表现O(N) 平均病例性能O(n^2) 查看算法行动 插入 从维基百科插入排序是一种简单的排序算法,每次构建最终排序数组(或列表)。在大型列表中,效率要比更高级的算法(如快速排序、堆排序或合并排序)低得多。 特性 最差情况下的性能O(n^2) 最佳案例表现O(N) 平均病例性能O(n^2) 查看算法行动 合并 合并排序(通常也是拼写合并)是一种高效的、通用的、基于比较排序算法。大多数实现都会产生稳定的排序,实现在排序的输出中保留相同元素的输入顺序。Mergesort是由JohnvonNeumann于1945年发明的分而治之的算法。 特性 最坏的情况性能O(N Log N)(典型) 最佳情况性能O(N Log N) 平均情况性能O(N Log N) 查看算法行动 速战速决 从维基百科快速排序(有时称为分区-交换排序)是一种有效的排序算法,是一种系统的方法,用于排列数组的元素。 特性 最差情况下的性能O(n^2) 最佳情况下O(N Log N)或O(N)具有三向分区 平均病例性能O(n^2) 查看算法行动 选择 将输入列表分为两个部分:已经排序项的子列表(在列表的前面(左)从左到右建立)和占据列表其余部分的待排序项的子列表。最开始排序子列表是空的,未排序子列表是整个输入列表。该算法通过查找未排序子列表中最小的(或最大的,取决于排序顺序)元素,将其与最左边的未排序元素交换(按排序顺序排列),并将子列表边界向右移动。 特性 最差情况下的性能O(n^2) 最佳案例性能O(n^2) 平均病例性能O(n^2) 查看算法行动 壳 ShellSort是插入排序的一种推广,允许交换相距很远的项。思路是安排元素列表,以便从任何地方开始,考虑到每个第n个元素都会给出一个排序列表。这样的列表叫做h排序。等效地,可以被认为是h交错列表,每个元素都是单独排序的。 特性 最坏的性能O(Nlog 2 2n) 最佳情况性能O(N Log N) 平均病例性能取决于间隙序列 查看算法行动 时间紧图 比较排序算法(气泡排序、插入排序、选择排序)的复杂性 复杂性图 搜索算法 线性 线性搜索或顺序搜索是在列表中查找目标值的一种方法。会依次检查列表中的每个元素的目标值,直到找到匹配或搜索所有元素为止。线性搜索在最坏的线性时间运行,最多进行n个比较,其中n是列表的长度。 特性 最坏的性能O(N) 最佳案例表现O(1) 平均个案表现O(N) 最坏情况下空间复杂度O(1)迭代 二进制 此算法也叫半间隔搜索或对数搜索算法,查找目标值在排序数组中的位置。将目标值与数组的中间元素进行比较;如果不相等,则消除目标数组的一半,并在其余的一半上继续搜索,直到成功为止。 特性 最坏的性能O(Log N) 最佳案例表现O(1) 平均案例性能O(Log N) 最坏情况空间复杂度O(1) ShellSort是插入排序的一种推广,允许交换相距很远的项。思路是安排元素列表,便于任何地方开始,考虑到每个第n个元素都会给出一个排序列表。这样的列表叫做h排序。等效地,可以被认为是h交错列表,每个元素都是单独排序的。 特性 最坏的性能O(Nlog 2 2n) 最佳情况性能O(N Log N) 平均病例性能取决于间隙序列 查看算法行动 与其他算法的链接 转换 动态规划 密码 杂类 任何基地到任何基地 硬币兑换 凯撒沙拉 堆排序 任何基到十进制 蛋滴 柱状转位密码 回文素校验器 二进制到十进制 斐波纳契 RSA 很快.。 二进制到十六进制 Kadane算法 更多的很快就会到来.。 二进制到八进制 背包 十进制到任意基 最长公共子序列 十进制到二进制 最长增长子序列 十进制到十六进制 棒材切割 还有更多.。 还有更多.。 数据结构 图 堆 列表 排队 BFS 空堆异常 圆链表 通用数组列表队列 外勤部 堆 双链表 排队 图 堆元素 单链表 Kruskals算法 最大堆 矩阵图 民堆 PrimMST 堆叠 树 节点堆栈 AVL树 链表堆栈 二叉树 堆叠 还有更多.。 袋 缓冲器 HashMap 矩阵

优秀的个人博客,低调大师

linux系统分区原理

windows系统 如图: 概念: 硬盘本身并不存在分区的说法,分区是操作系统的逻辑概念。 1、挂载:操作系统目录 与 硬盘分区建立联系的过程。 2、挂载点,被挂载的操作系统目录 就是挂载点 例如:C/D/E 等目录 3.、挂载类型:自动、手动 windows系统的挂载类型都是自动的 4、根目录:有多个(C/D/E等都是) 5、文件占据磁盘空间 各自挂载点目录下文件占据对应挂载点本身的磁盘空间 Linux系统 如图: 1、 挂载:操作系统目录 与 硬盘分区建立联系的过程。 2、 挂载点:被挂载的操作系统目录 就是挂载点 例如: /根目录、/Efile目录、/Cfile目录、/video目录 3、挂载类型:自动、手动 自动:系统安装创建的挂载点,后期使用会自动与硬盘分区建立联系。 手动:系统运行过程中,临时添加的U盘、移动硬盘不会被系统应用起来,需要手动创建一个文件目录并使其与该硬件进行联系挂载。 4、根目录:只有一个,名称是“/”根目录 5、 文件占据分区空间:会占据与其上边挨着最近挂载点对应的分区空间 6、与新硬件形成联系挂载 ① 把挂载点目录内部的旧的文件释放出去 ② 再进行挂载操作 7、文件存储占用空间 Dfile,/file根目录,存储的资源占用的是根目录的空间 viedo目录,Efile目录,Cfile目录,/根目录存储的资源占各自挂载所在的空间资源。

优秀的个人博客,低调大师

结构体对齐原理【转】

转自:http://diheshu.blog.sohu.com/145802264.html 一、什么是对齐,以及为什么要对齐:1. 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 一、字节对齐作用和原因: 对齐的作用和原因:各个硬件平台对存储空间的处理上有 很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种 架构下编程必须保证字节对齐,其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些 平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇 地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据,显然在读取效率上下降很多。 一.为什么要对齐? 《Windows核心编程》里这样说:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数 的内存地址是0时,数据是对齐的。例如:WORD值应该是总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始,数据对齐不是内存结构 的一部分,而是CPU结构的一部分。当CPU试图读取的数值没有正确的对齐时,CPU可以执行两种操作之一:产生一个异常条件;执行多次对齐的内存访问, 以便读取完整的未对齐数据,若多次执行内存访问,应用程序的运行速度就会慢。在最好的情况下,是两倍的时间,有时更长。 二、对齐的实现通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访 问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。对齐的算法:由于各个平台和编译器的不同,现以本人使用的gcc version 3.2.2编译器(32位x86平台)为例子,来讨论编译器对struct数据结构中的各成员如何进行对齐的。设结构体如下定义:struct A { int a; char b; short c;};结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐。所以使用sizeof(strcut A)值为8。现在把该结构体调整成员变量的顺序。struct B { char b; int a; short c;};这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12。下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。#progma pack (2) /*指定按2字节对齐*/struct C { char b; int a; short c;};#progma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct C)值是8。修改对齐值为1:#progma pack (1) /*指定按1字节对齐*/struct D { char b; int a; short c;};#progma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct D)值为7。对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。 这里面有四个概念值:1)数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。2)指定对齐值:#pragma pack (value)时的指定对齐值value。3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。 有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就 是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是 数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整 数倍,结合下面例子理解)。这样就不难理解上面的几个例子的值了。例子分析:分析例子B;struct B { char b; int a; short c;};假 设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指 定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为 4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐 值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存 放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;同理,分析上面例子C:#pragma pack (2) /*指定按2字节对齐*/struct C { char b; int a; short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/第 一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在 0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以 C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.有 了以上的解释,相信你对C语言的字节对齐概念应该有了清楚的认识了吧。在网络程序中,掌握这个概念可是很重要的喔,在不同平台之间(比如在Windows 和Linux之间)传递2进制流(比如结构体),那么在这两个平台间必须要定义相同的对齐方式,不然莫名其妙的出了一些错,可是很难排查的哦^_^。 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/arethe/archive/2008/06/15/2548867.aspx 嵌入式开发普遍比较重视性能,所以对齐的问题,有3种不同的处理方法:1)有一种使用空间换时间做法是显式的插入reserved成员: struct A{ char a; char reserved1[3]; //使用空间换时间 int b;}a; ==>感觉此种编码方式比较专业,有显式提醒代码阅读者与维护者的功能...2)随便怎么写,一切交给编译器自动对齐。3)还有一种将逻辑相关的数据放在一起定义。代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。下面举个例子:unsigned int i = 0x12345678;unsigned char *p=NULL;unsigned short *p1=NULL;p=&i;*p=0x00;p1=(unsigned short *)(p+1);*p1=0x0000;最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error 二、字节对齐规则: 四个重要的概念: 1.数据类型自身的对齐值:对于char型的数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4个字节。 2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。 3.指定对齐值:#pragma pack (value)时指定的对齐value。 4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。 补充: 1).每个成员分别按自己的方式对齐,并能最小化长度。2).复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。3).对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。 一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在 特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访 问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。 二.字节对齐对程序的影响: 先让我们看几个例子吧(32bit,x86环境,gcc编译器):设结构体如下定义:struct A{ int a; char b; short c;};struct B{ char b; int a; short c;};现在已知32位机器上各种数据类型的长度如下:char:1(有符号无符号同) short:2(有符号无符号同) int:4(有符号无符号同) long:4(有符号无符号同) float:4 double:8那么上面两个结构大小如何呢?结果是:sizeof(strcut A)值为8sizeof(struct B)的值却是12 结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:#pragma pack (2) /*指定按2字节对齐*/struct C{ char b; int a; short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct C)值是8。修改对齐值为1:#pragma pack (1) /*指定按1字节对齐*/struct D{ char b; int a; short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct D)值为7。后面我们再讲解#pragma pack()的作用. 三.编译器是按照什么样的原则进行对齐的? 先让我们看四个重要的基本概念:1.数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。3.指定对齐值:#pragma pack (value)时的指定对齐值value。4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。有 了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是 表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数 据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数 倍,结合下面例子理解)。 ARM下的对齐处理 from DUI0067D_ADS1_2_CompLib 3.13 type qulifiers 有部分摘自ARM编译器文档对齐部分 对齐的使用:1.__align(num) 这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时 就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。 这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节 对齐,但是不能让4字节的对象2字节对齐。 __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。 2.__packed __packed是进行一字节对齐1.不能对packed的对象进行对齐2.所有对象的读写访问都进行非对齐访问3.float及包含float的结构联合及未用__packed的对象将不能字节对齐4.__packed对局部整形变量无影响5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为packed。 __packed int* p; //__packed int 则没有意义6.对齐或非对齐读写访问带来问题__packed struct STRUCT_TEST{char a;int b;char c;} ; //定义如下结构此时b的起始地址一定是不对齐的 //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]//将下面变量定义成全局静态不在栈上 static char* p;static struct STRUCT_TEST a;void Main(){__packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以 p = (char*)&a; q = (int*)(p+1); *q = 0×87654321; /* 得到赋值的汇编指令很清楚ldr r5,0×20001590 ; = #0×12345678[0xe1a00005] mov r0,r5[0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函数 [0xe5c10000] strb r0,[r1,#0] //函数进行4次strb操作然后返回保证了数据正确的访问[0xe1a02420] mov r2,r0,lsr #8[0xe5c12001] strb r2,[r1,#1][0xe1a02820] mov r2,r0,lsr #16[0xe5c12002] strb r2,[r1,#2][0xe1a02c20] mov r2,r0,lsr #24[0xe5c12003] strb r2,[r1,#3][0xe1a0f00e] mov pc,r14*/ /*如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败[0xe59f2018] ldr r2,0×20001594 ; = #0×87654321[0xe5812000] str r2,[r1,#0]*/ //这样可以很清楚的看到非对齐访问是如何产生错误的//以及如何消除非对齐访问带来问题//也可以看到非对齐访问和对齐访问的指令差异导致效率问题} http://blog.csdn.net/hongdatong/archive/2009/03/18/3997899.aspx http://blog.csdn.net/factor2000/archive/2009/02/25/3936668.aspx 一.为什么要对齐? 《Windows核心编程》里这样说:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数 的内存地址是0时,数据是对齐的。例如:WORD值应该是总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始,数据对齐不是内存结构 的一部分,而是CPU结构的一部分。当CPU试图读取的数值没有正确的对齐时,CPU可以执行两种操作之一:产生一个异常条件;执行多次对齐的内存访问, 以便读取完整的未对齐数据,若多次执行内存访问,应用程序的运行速度就会慢。在最好的情况下,是两倍的时间,有时更长。http://blog.csdn.net/mudboy/archive/2006/04/14/663430.aspx 【作者】 张昺华 【出处】 http://www.cnblogs.com/sky-heaven/ 【博客园】 http://www.cnblogs.com/sky-heaven/ 【新浪博客】 http://blog.sina.com.cn/u/2049150530 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【新浪微博】 张昺华--sky 【twitter】 @sky2030_ 【facebook】 张昺华 zhangbinghua 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册