<?xml version="1.0" encoding="utf-8" ?><rss version="2.0"><channel><title><![CDATA[一个白馒头的专栏]]></title><description><![CDATA[]]></description><link>https://blog.youkuaiyun.com/wang_sun_1983</link><language>zh-cn</language><generator>https://blog.youkuaiyun.com/</generator><copyright><![CDATA[Copyright &copy; wang_sun_1983]]></copyright><item><title><![CDATA[Android性能优化（native内存泄漏分析方法）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144956462</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144956462</guid><author>wang_sun_1983</author><pubDate>Mon, 06 Jan 2025 09:58:06 +0800</pubDate><description><![CDATA[malloc_debug有一个缺点，它只有在进程退出时，才回检测没有释放的内存，然后把每内存的申请调用栈打印，所以如果是类似常驻服务的进程，malloc_debug基本没用（第三步我们会介绍一种修改malloc_debug的方法，支持常驻服务的内存泄漏检测）。我们先看一下针对进程正常退出，我们怎么去查看。针对native的内存泄漏，我们经常谈到的有2中方法valgrind和malloc_debug,由于Arm不支持asan的memory leak特性，所以android使用asan是无法检测处内存泄漏的。]]></description><category></category></item><item><title><![CDATA[Android性能优化（native进程启动优化）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144908118</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144908118</guid><author>wang_sun_1983</author><pubDate>Fri, 03 Jan 2025 13:15:43 +0800</pubDate><description><![CDATA[这些是需要link的库，我们可以手动cat一下这些库，确保他们都在文件缓存中，然后再启动，确认是否速度提升，如果提升，推论成立。主函数基本就是打印一句log，但是运行时卡了很久（为了演示方便，我只加了一个头文件，实际情况比这个复杂，可能是各种头文件加载）。我们这样就找到了需要全局变量初始化函数位置，按照这个方法找到所有函数，然后打印log，这样我们就能知道具体是哪个初始化慢了。add x8，x8，#240，就是x8再机上240，那相对地址就是0x10F0。blr x8,跳转到x8，就是跳转到0x10F0。]]></description><category></category></item><item><title><![CDATA[如何优雅的写C++代码(简单函数反射实现二）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144737398</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144737398</guid><author>wang_sun_1983</author><pubDate>Thu, 26 Dec 2024 11:15:24 +0800</pubDate><description><![CDATA[所有的反射函数都会自动生成一个基于__reflect_base__的反射函数类（最后存放在__reflect_methods__中），这个基类主要对外提供一个execution的函数（类似java反射的invoke），将可变参数转换成tuple，传递给子类（具体的反射函数类）。此外，如果是指用if判断，编译时是不会做判断的，导致可能出现函数类型不匹配等问题（模板会将同一种情况在if和非if的分支里都编译，结果就出错了），这里用了C++17的一个新语法特性（if constexpr），要求编译时就进行计算。]]></description><category></category></item><item><title><![CDATA[如何优雅的写C++代码(简单函数反射实现一）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144663935</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/144663935</guid><author>wang_sun_1983</author><pubDate>Mon, 23 Dec 2024 13:48:01 +0800</pubDate><description><![CDATA[和之前申明成员变量反射类似的宏DECLARE_REFLECT_METHOD，testReflect_1是类目，后面可以添加需要反射的函数。应该算是比较简单的申明了。execute函数对应的就是执行被反射的函数。getMethodField函数根据函数名字来找到需要反射的函数。模板中指定返回数据类型，get方法会强转。用起来还是比较方便的。]]></description><category></category></item><item><title><![CDATA[如何优雅的写C++代码( stack-use-after-scope问题）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/140353947</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/140353947</guid><author>wang_sun_1983</author><pubDate>Thu, 11 Jul 2024 15:47:13 +0800</pubDate><description><![CDATA[stack-use-after-scope貌似是说用栈超出生命周期的意思。我们看一下这个代码到底出了什么问题。*号出代码主要是调用了 一个i，然后转string。由于是多线程，所以可能出现这个循环跑完，线程都还没有跑起来，这个时候由于i的生命周期只在for循环内有效，所以当线程启动的时候，i已经没有了。这里直接把i作为参数直接传入lambda函数中。]]></description><category></category></item><item><title><![CDATA[rust学习（字节数组转string）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/139475619</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/139475619</guid><author>wang_sun_1983</author><pubDate>Wed, 05 Jun 2024 16:29:01 +0800</pubDate><description><![CDATA[哈哈，字符串长度竟然是32..按照我们以前写C语言的习惯，字节数据从第二位开始都是0，照理应该算是终结符，所以字符串长度应该是1啊。所以大家转换的时候一定要留意这个问题。哈哈，猜猜最后结果如何？]]></description><category></category></item><item><title><![CDATA[如何优雅的写C++代码（fsanitize导致无法coredump）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138971898</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138971898</guid><author>wang_sun_1983</author><pubDate>Thu, 16 May 2024 18:53:47 +0800</pubDate><description><![CDATA[为了方便调试内存问题，在编译配置里面加入了-fsanitize=address，如果出现内存问题，的确很方便就能根据trace来判断问题。但是我发现有个很诡异的现象，出现crash的时候，coredump没了。最后发现是有asan的环境变量配置导致，使用下面的命令重新配置后coredump正常使用。]]></description><category></category></item><item><title><![CDATA[如何优雅的写C++代码(实例创建接口重构）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138786787</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138786787</guid><author>wang_sun_1983</author><pubDate>Mon, 13 May 2024 09:53:16 +0800</pubDate><description><![CDATA[那就会自动生成一个createFile的函数，这个时候，如果你有其他文件也定义了createFile，那可能会出现冲突...而且，createXXX是通过一个宏进行拼接而成的，所以可能有时候改写类名的时候不容易一下子都改全（ide无法联想到）。可以看到，这样的表达就很清晰，MyData::new()是要生成一个MyData的实例。之前使用createXXX函数主要原因是Obotcha默认所有的实例都是sp（智能指针）的，为了减少创建时候的步骤（不用create的创建方法见下面），所以用了这个方法。]]></description><category></category></item><item><title><![CDATA[rust学习（openssl rsa加解密文件）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138625847</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/138625847</guid><author>wang_sun_1983</author><pubDate>Thu, 09 May 2024 18:47:16 +0800</pubDate><description><![CDATA[2.注意public_decrypt返回的数字是实际解密后的数据长度，例如你传入解密的数据是256字节（正好是rsa key的长度），那你解密后的数据长度（如果padding是PCKS1）就是256-11 = 245.使用也很方便，但是有一点注意public_encrypt这个函数每次返回的加密长度都是256.（我的key生成的时候是2048=256*4）1.解密的原理和加密类似，也是逐个block解密，每个block的长度就是rsa key的长度，这个比加密时候计算方便。上述代码使用公钥加密数据。]]></description><category></category></item><item><title><![CDATA[rust学习（BorrowMut异常）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137884154</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137884154</guid><author>wang_sun_1983</author><pubDate>Wed, 17 Apr 2024 18:42:50 +0800</pubDate><description><![CDATA[一个cell貌似不能同时被借用两次，注意！所以如果出现这类错，也要考虑是否有多线程的问题。但是如果一直拿着borrow_mut的句柄，就会报错。不要长期持有borrow_mut的句柄。]]></description><category></category></item><item><title><![CDATA[rust学习（recursive mutex 实现）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137557930</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137557930</guid><author>wang_sun_1983</author><pubDate>Tue, 09 Apr 2024 16:43:21 +0800</pubDate><description><![CDATA[哈哈，网上查了一下，很多人都说recursive lock是垃圾代码，要修复。鉴于个人能力，很多这样的代码我都改不掉，所以我用Rust实现了一个recursive的锁。4.如果锁的owner不是当前线程，则调用这个m_cond等待。当锁释放的时候，如果计数为0，唤醒m_cond等待的线程。2.锁的owner，每次拿到锁以后记录拿锁线程，防止其他线程调用unlock释放。6.如果当前计数为0，表明锁都已经释放，直接唤醒所有等待锁的队列。1.获取锁计数，如果计数到0的话，唤醒其他等待线程去拿锁。]]></description><category></category></item><item><title><![CDATA[rust学习（tokio中tcp_stream调用的问题）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137511035</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/137511035</guid><author>wang_sun_1983</author><pubDate>Mon, 08 Apr 2024 15:29:12 +0800</pubDate><description><![CDATA[我们涉及了一个socket连接的类，每次收到数据以后，我们都会把tokio::net::TcpStream对应的tcp_stream传递给其他线程。2.由于线程1一直在等数据，所以线程2只能在这里2处卡住。1.stream直接move给TarNetStream。2.回调函数中采用了Arc智能指针。1.没有数据，这里卡死。]]></description><category></category></item><item><title><![CDATA[rust学习（简单handler实现）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136880589</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136880589</guid><author>wang_sun_1983</author><pubDate>Wed, 20 Mar 2024 16:24:56 +0800</pubDate><description><![CDATA[1.message处理的时候调用了ProcessMessage，这里主要考虑到使用的方便（不要再用什么unwrap），所以传出去的数据重新构造了一个新数据，性能上会有损耗。每次有新消息加入的时候，重新针对消息队列排序，然后cond.notify一下，唤醒等待线程。2.target是一个回调实现，每个message都需要带有target，这样message就能由指定的handler处理了。1.消息队列头，为了减少option，这里规定了第一个消息一直不使用，所有消息从head的next开始排队。]]></description><category></category></item><item><title><![CDATA[rust学习（简单链表）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136719198</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136719198</guid><author>wang_sun_1983</author><pubDate>Thu, 14 Mar 2024 19:07:37 +0800</pubDate><description><![CDATA[我们发现，try_borrow_mut貌似只有refcell实现了，所以这个接口返回的是我们需要的值RefMut<>,但是注意一下，由于try_borrow_mut返回的是result，所以我们还需要unwrap一下。一个是BorrowMut，一个是RefCell的。目前我就遇到一个比较神奇的问题：使用borrow_mut之后，无法找到成员变量..仔细对比了2个borrow_mut函数，你会发现，我去，完全是2个接口~编写一个简单链表，主要遇到的问题就是next指针（按照C的写法）的数据如何定义。]]></description><category></category></item><item><title><![CDATA[rust学习（手动写一个线程池）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136525791</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136525791</guid><author>wang_sun_1983</author><pubDate>Thu, 07 Mar 2024 10:10:26 +0800</pubDate><description><![CDATA[1.创建一个Condition，用智能指针控制，这样计算和等待的线程都能使用。3.cond：用来等待计算完成的condition，和Task结构体中的cond是同一个，这样task完成后就能唤醒等待了。1.results：用来存放结果，其中u32是一个future的id，T表示存放的数据。1.id：这个future的id，通过这个id可以在map中找到结果。2.将task存放到任务队列，计算线程就是从这个队列中获取任务的。只有一个get函数，如果有数据，就返回该数据，否则就等待唤醒。]]></description><category></category></item><item><title><![CDATA[rust学习（模拟Future）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136503166</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136503166</guid><author>wang_sun_1983</author><pubDate>Wed, 06 Mar 2024 13:20:02 +0800</pubDate><description><![CDATA[4.MTask::future:存储了future的信息，用了智能指针，主要是等待线程需要持有一份，处理线程也需要持有一份，2个线程是通过future里面的cond来唤醒。3.生成一个future，这里i32::getDefault()获取默认数据，模板玩的不溜，这里直接写死了。原理比较简单，从future中获取数据元，如果元组第一个为true，则表明计算完成，直接返回数据，否则就调用condvar来等待。2.创建一个lambda函数，这里简单写了一个直接返回i32的函数，实际场景可能会是比较负责的函数。]]></description><category></category></item><item><title><![CDATA[rust学习（tokio future分析）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136479509</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136479509</guid><author>wang_sun_1983</author><pubDate>Tue, 05 Mar 2024 15:09:14 +0800</pubDate><description><![CDATA[貌似调用block_on之后会回调一次future的poll，如果这个时候还没有计算出结果，那就直接卡死了，所以需要找个地方能唤醒。关键是在poll返回pending的时候，获取waker。1.当调用block_on的时候，会直接调用一次future的poll确认是否运算结束。所以我们现在要加一个标志位来表示运算结束，这样block_on应该就能正常走下去了吧。2.当调用waker.wake的时候貌似还会调用一次poll确认是否运算结束。自己编写一个impl future来看一下Tokio的是如何实现的。]]></description><category></category></item><item><title><![CDATA[rust学习（tokio协程分析二）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136404278</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136404278</guid><author>wang_sun_1983</author><pubDate>Fri, 01 Mar 2024 19:29:54 +0800</pubDate><description><![CDATA[之前用过C语言的libco，它的原理是通过注册了libc的大部分posix的io操作函数，然后通过epoll来实现异步io来实现。例如读取socket的数据，如果没有数据，由于是异步io，所以直接切换到下一个调用栈。哈哈，可以看到，线程最后启动了一个和之前毫无关系的协程代码，里面只是一个sleep（注意！这个是tokio的sleep），这个sleep的作用就是满足线程有一个协程切换的入口点。根据代码1的情况，我们考虑是否可以保持线程不退出（如代码2），这样是否可以执行到协程（doSayHi）呢？]]></description><category></category></item><item><title><![CDATA[rust学习（tokio协程分析一）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136369968</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/136369968</guid><author>wang_sun_1983</author><pubDate>Thu, 29 Feb 2024 14:17:11 +0800</pubDate><description><![CDATA[从blocking队列中获取数据，注意这个blocking队列使用了mutex和condvar，所以当condvar卡住的时候，实际上是整个线程直接卡住，由于协程只是在线程中来回切换调用栈，所以协程中所有的等待处理实际上都是切换调用栈，但是这个condvar不是切换调用栈，所以就会卡住。这样实际上协程的runtime会创建线程，让协程跑在这些线程上，如上述代码，我们通过worker_threads创建了一个线程，所有的协程都会跑在这个线程上。doAsyncPrint一直没有被调用。]]></description><category></category></item><item><title><![CDATA[我和我的同事们（二）]]></title><link>https://blog.youkuaiyun.com/wang_sun_1983/article/details/131525214</link><guid>https://blog.youkuaiyun.com/wang_sun_1983/article/details/131525214</guid><author>wang_sun_1983</author><pubDate>Sun, 21 Jan 2024 22:29:24 +0800</pubDate><description><![CDATA[我去，有人和我抢江边的座位/我先点菜了，你们慢慢来...”,每隔几分钟，D就更新一下进展，感觉比我们当年bringup时候汇报的还勤快。D是真得能说，在遇到他之前，我完全没有想到过一个理工科男生会有这么好的文学底蕴。聊着聊着，我仿佛感觉D还是我当年遇到的那个样子，一个28岁的男生，干活疯疯火火，各种事情都能和你聊。第二天九点多，还在吃早饭，D已经发来消息，他已经开始排队抢位置了。额～～，在吃这块，D的行动力的确无与伦比。于是我不得不开始加快我的步伐，快点吃完早饭，快点出们，快点去火锅店，快点去见D～～。]]></description><category></category></item></channel></rss>