继续自己写文章的记叙风格,先把不熟悉的东西感性的介绍下,后面深入研究后再 介绍多一些。
很简单的说就是,在c++11中,某些人就是为了偷懒,对于某些变量的声明变的不耐烦, 想即时使用。这一点和我们经常用的解释性编程语言具有很大的相似性。比如,我们的 python shell都是这种用法,c++11的auto就在感官上做到了这一点。
int main(){
auto str = "yubo and chun \n";
std::cout << str << endl;
}
这里我们就可以清楚了,auto可以节省显式地声明类型了,这一点尤其在自定义的很多 类 容器时具有巨大的帮助
下面这一个例子加强了这种概念:
#include <iostream>
using namespace std;
template<typename T1, typename T2>
float Sum(T1& t1, T2& t2){
auto a = t1 + t2;
return a;
}
int main(){
int a = 3;
long b = 5;
float c = 1.0f;
float d = 2.3f;
auto e = Sum<int, long>(a, b);
auto f = Sum<float, float>(c, d);
cout << e << endl << f << endl;
}
上面这段程序有一个目前我不了解的地方就是,如果定义Sum的返回值类型为int,则f返回int(这一点是对的,那么,怎么可以让函数返回值使用模板呢)
任何东西都有两面性,auto也不例外,目前我使用auto的情况也不是特别多,所以关于这一点,我暂且放下,等到我有更深的认识后再补充上。
其实,对于整个汇编系统而言,在下实在是没有入门,别说riscv了,就是最常用的x86也没有精通一点半星的, 更别说arm了,当然,这里这篇文件介绍 riscv主要是由于工作之中会偶尔遇到,姑且碰上什么就记录什么吧。
(gdb) disassemble /mr main
Dump of assembler code for function main:
7 {
0x0000000000027936 <+0>: 41 11 addi sp,sp,-16
8 int a=10;
9 int b=10;
10 printf("Hello world\n");
0x000000000002793a <+4>: 17 a5 fe ff auipc a0,0xfffea
0x000000000002793e <+8>: 13 05 f5 bf addi a0,a0,-1025 # 0x11539
0x0000000000027942 <+12>: 97 20 02 00 auipc ra,0x22
0x0000000000027946 <+16>: e7 80 20 a2 jalr -1502(ra) # 0x49364 <puts(char const*)>
11 printf("int a+b = %d\n", sum(a, b));
0x000000000002794a <+20>: 17 c5 fe ff auipc a0,0xfffec
--Type <RET> for more, q to quit, c to continue without paging--
0x000000000002794e <+24>: 13 05 55 62 addi a0,a0,1573 # 0x13f6f
0x0000000000027952 <+28>: d1 45 li a1,20
0x0000000000027954 <+30>: 97 20 02 00 auipc ra,0x22
0x0000000000027958 <+34>: e7 80 00 98 jalr -1664(ra) # 0x492d4 <printf(char const*, ...)>
12 return 0;
0x000000000002795c <+38>: 01 45 li a0,0
0x000000000002795e <+40>: a2 60 ld ra,8(sp)
0x0000000000027960 <+42>: 41 01 addi sp,sp,16
0x0000000000027962 <+44>: 82 80 ret
下面是简单的介绍这个汇编出现的指令。 在这里,在下向各位报一声歉,因为我写文章大部分 都是给自己写的,基本上写到哪里就算哪里了,导致文章大部分残缺不全,这也是今后我需要改进 的部分。
The AUIPC instruction, which adds a 20-bit upper immediate to the PC, replaces the RDNPC instruction, which only read the current PC value. This results in significant savings for position-independent code. 这个指令的解释也很清楚,但是在这里想愚蠢的问一下,为什么会送到pc中去,虽然都说pc需要存储 下一条执行的地址。
0x000000000002793a <+4>: 17 a5 fe ff auipc a0,0xfffea
看这样子是将0xfffea(20 bit)放入a0寄存器中,剩下的位自然是填充0.
10060: fe010113 addi sp,sp,-32 # allocate space
10064: 00113c23 sd ra,24(sp) # save $ra
10068: 00813823 sd s0,16(sp) # save $s0
1006c: 02010413 addi s0,sp,32 # set up $s0 as frame pointer
安卓有很多的log系统,以aosp为例,最基本的log定义恐怕是 system\core\base\include\android-base\Logging.h
:
//
// Google-style C++ logging.
//
// This header provides a C++ stream interface to logging.
//
// To log:
//
// LOG(INFO) << "Some text; " << some_value;
//
// Replace `INFO` with any severity from `enum LogSeverity`.
//
// To log the result of a failed function and include the string
// representation of `errno` at the end:
//
那么对应的 log等级有:
num LogSeverity {
VERBOSE,
DEBUG,
INFO,
WARNING,
ERROR,
FATAL_WITHOUT_ABORT,
FATAL,
};
如果可以研究下将INFO改为FATAl(可以带有call stack).
借助这个log系统,aosp 中其他各个子模块可以实现自己的log系统,以art为例:art\libartbase\base\logging.h
#include "android-base/logging.h"
#include "macros.h
请注意这个include文件,我自己并没有在该上层目录的Android.bp文件中找到相关头文件的引用,这块是怎么解决的这个问题,还需要研究。
本系列是根据 SCott Meyers 的大作«overview of the new c++(c++11/14)»进行练习总结。 因为我本身对c++的使用还不是特别的熟悉,所以,在阅读本系列的过程中,在对一些新的特性进行 阐述的时候,可能会显得尤其啰嗦,这也符合我一贯的行文习惯,不好,这点得改。
BTW, 为什么这么着急的去看一些 c++的东西呢? 因为aosp的大部分是C++写的啊,作为AOSP的使用者 ,能不了解C++最新的技术吗?
拷贝构造函数是一种将对象初始化的方式,除了那种带有参数的构造函数外,这也算一种很常见的赋值对象的方式。
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int x1, int y1){x = x1, y = y1 ;} // Directly constructor
// 另一种给构造函数赋初始值的方式是
// 成员列表的形式: Point(int x1, int y1) : x(x1), y(y1){}
// 这种方式只能类内使用
Point(const Point &p2) { x = p2.x, y = p2.y ;} // copy constructor
int getX() { return x ;}
int getY() { return y ;}
};
int main()
{
Point p1(10, 15);
Point p2 = p1; // assign object
cout << p1.getX() << endl;
cout << p2.getY() << endl;
}
std::move是
#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std;
int main(){
std::string str = "hello";
std::vector<std::string> v;
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
v.push_back(std::move(str));
/**
* std::move的使用是将原对象的内容移动至现在的对象,
* 这样的效率很高,代价就是原对象的内容被移动后有可能为空
*/
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are " << v[0] << " the v[1]" << v[0] << std::endl;
}
Output:
After copy, str is "hello"
After move, str is ""
The contents of the vector are hello the v[1]hello
std::move的解释 .下面需要补充几个概念。
一般来说,可以取地址的是左值,不能取地址的是右值。那么,什么对象可以取地址,又有哪些量是不可以取地址的呢?
左值的声明符号为&
,右值的声明符号为&&
。
为什么引入了std::move?不知道在哪里看到过这样的笑话,说是一个hello,world的程序c++的表达式可以复制出200份的内容,这显然夸张,但是稍微复杂的构造函数列表那是真的恐怖,所以,上面的测试你也已经看到了效果,那就是原对象不再保存这份内容了。 主要参考这篇文章
# o以下操作仅针对debian系。
sudo apt install samba
在samba的系统配置文件/etc/samba/smb.conf
中, 添加以下内容:
[share]
path = /home/vimer/pic
public = yes
writable = yes
valid users = vimer
create mask = 0644
force create mode = 0644
directory mask = 0755
force directory mode = 0755
available = yes
配置文件修改完成后,需要使用smbpasswd命令去添加smb的用户,使用手工创建smb 用户的方式是不正确的。
参考 https://blog.csdn.net/qq_29129381/article/details/106826108
service smb start # 来开启服务
smbpasswd -a XXX # 来增加用户名密码
smbpasswd -e xxx # 启用用户名
service smb restart # 重启服务
service iptables stop # 关闭防火墙
注意, debian中使用smbd。
sudo service smbd restart
注意这里的smbd
因为这个耽误自己很长时间的。
在windows中可以在文件管理器
\\ip\dir
,其中dir就是上面配置文件中的设置的分享路径别名,比如,我的上面就是 share.
哈哈 第一个就是 sublime open-> file folder
之前留下了一个大坑,现在慢慢填上。
app_process是安卓系统启动Java虚拟机的程序。首先来回忆下Zygote进程,按照邓凡平老师的书上说的, java世界由Zygote进程创建,通过fork一个一个产生。那么, Zygote又是怎么来的呢?
我们知道,linux系统第一个进程就是init,安卓也不例外。在kernel层,init起来之后,安卓会通过一个脚本 激活Zygote进程。
init的具体实现位于 system/core/init 下, 如果详细介绍init的情况,这篇文章会偏离主旨的,会另外开一篇介绍 这个事情。以system/core/rootdir/init.zygote32.rc为例:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
app_process会接受三类启动参数,分别代表不同的系统服务。在 frameworks/base/cmds/app_process/app_main.cpp文件中
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
最终会以具体的形式调用runtime. runtime实际上是AppRuntime的实例,而Appruntime是AndroidRuntime的派生类。AppRuntime也是定义在app_main.cpp中, 只是重写了setClassNameAndArgs、onVmCreated()、onStarted()、onZygoteInit()、onExit()这几个方法。
我们本文的重点函数AndroidRuntime是一个重点钉子户,涉及的东西比较多,源文件在 frameworks/base/core/jni/AndroidRuntime.cpp 。这个文件处理了很多jni注册的事情。
/** Start the Android runtime,This invokes VM and calling the "static void main(String[], args)" method in the class named
* by "className"
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){
...
// 加载一些环境变量
const char* rootDir = getenv("ANDROID_ROOT"); // getenv.c 在bionic/libc/upstream-openbsd/lib/libc/stdlib/getenv.c中定义
// 这个文件理解起来还不是那么友好的
const char* runtimeRootdir = getenv("ANDROID_RUNTIME_ROOT");
const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
...
JniInvocation jni_invocation; // 这两个是重中之重
jni_invocation.Init(NULL);
}