frameworks存在大量的java代码,但是vscode原来是不支持java原生跳转的。 但是在这个URL中找到了这个方法,目前看是解决了这个问题。
frameworks/base 里面大多是 Java 代码,VS Code 打开后,你需要告诉它对应的 classpath。这个可以通过创建一个 build.gradle 解决。
# frameworks/base/build.gradle
apply plugin: 'java'
sourceSets {
main {
java {
srcDirs 'core/java'
srcDirs 'graphics/java'
}
}
}
目前的情况是添加了这个文件后,frameworks/base/media/下的java代码是可以跳转啊。
后面如果再不满足的话,自己手动添加吧。
首先使用
./emulator -show-kernel -verbose -nocache -wipe-data
启动模拟器:
adb logcat --regex="ZygoteInit |Entered the Android|StartServic es | phase |set rtc to|Setting time"
thread应该是c++的高级特性了,初学者基本涉及不到对该特性的掌控。 但是目前在工作中已经涉及到了,早点记录下来有助于自己的进步。
提前说明一点,在linux下c++的多进程多依赖pthread库,所以如果以 命令行的形式编译thread相关的程序,需要手动指定pthread的库。例如
g++ -g bar.cpp -o bar -lpthread -std=c++11
这里借花献佛here 更强悍的资料 从学习的路线图来说,使用多线程编程,需要对并行编程(parallel program)有 一个基本的了解
首先借助一个例子:
#include <unistd.h>
#include <pthread.h>
void printids(const char *s){
pid_t pid;
pthread_t tid; // thread id
pid = getpid();
tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
void *rhr_fn(void *arg){
printids("new thread:");
return NULL;
}
int main(){
int err;
pthread_t ntid;
err = pthread_create(&ntid, NULL, rhr_fn, NULL);
if (err != 0)
printf("can not create new thread");
printids("main thread:");
pthread_join(ntid, NULL);
return 0;
}
在涉及到thread相关的事务时,我们最好使用另一个计算机词汇去进行描述:routines
.
pthread_create();
pthread_exit(status);
pthread_cancel(thread);
pthread_attr_init(attr);
pthread_attr_destroy(attr);
所有的routines(与thread相关)都是从main()
函数开始的。pthread_create
就是用来创建新的thread并执行它。其中有四个参数:
void (*)
的形式传递,可以使用NULL
作为默认参数。limit可以查看一个进程最多可以创建多少线程。 ulimit -Hu
pthread_attr_init
和pthread_attr_destroy
分别作为与pthread_attr相关的生死函数。
大体上来说,thread相关的属性包括:
BTW, 这里与thread相关的调度算法包括:FIFO,RR(round-robin),OTHER(OS)
pthread_exit()
主要负责这件事。那么什么情况下可以终止thread呢?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define NUM_THREADS 5
void *print_hello(void *threadid){
long tid;
tid = (long) threadid;
printf("hello, world, It is me, thread #%ld!\n", tid);
pthread_exit(NULL);
}
int main(){
pthread_t threads[NUM_THREADS];
int rc;
long t;
for (t = 0; t < NUM_THREADS; t++){
printf("In main, Createing the thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, print_hello, (void *)t);
if (rc){
printf("error: %d\n", rc);
exit(-1);
}
}
}
我们来看一下结果:
vimer@host:~/src/test/c++/thread$ gcc -g thread_create.c -o test -pthread
vimer@host:~/src/test/c++/thread$ ./test
In main, Createing the thread 0
In main, Createing the thread 1
hello, world, It is me, thread #0!
In main, Createing the thread 2
hello, world, It is me, thread #1!
In main, Createing the thread 3
hello, world, It is me, thread #2!
In main, Createing the thread 4
hello, world, It is me, thread #3!
vimer@host:~/src/test/c++/thread$ ./test
In main, Createing the thread 0
In main, Createing the thread 1
In main, Createing the thread 2
hello, world, It is me, thread #0!
hello, world, It is me, thread #1!
hello, world, It is me, thread #2!
In main, Createing the thread 3
In main, Createing the thread 4
hello, world, It is me, thread #3!
hello, world, It is me, thread #4!
看看,thread的id是不一样的。
首先,我们考虑下之前的有关这类的定义。有一个”左值”(lvalue)是一个指针能够指向的内存位置,那么,” 右值”(rvalue)是不需要指针指向的一类东西。从生命周期的角度考虑,一个rvalue的使用范围很短, 且一个rvalue不指向任何地方;而lvalue自变量进行声明后一直到消亡。
int x = 666;
以这个赋值为例,我们的”666”可以看成一个字符常量,这个字符没有指向任何的地址,所以我们可以 把“666”看成rvalue.而这个数据赋值给了变量x,这个变量x就是一个内存地址的别名,我们可以通过指针 去访问这个x(地址)的内容,所以它是一个lvalue。
int *y = &x; //
我们利用取址符”&”,将变量x的地址取出来,然后赋值给int *的变量y,这里也许有人会问,既然是存储 变量的地址,那么我们使用int y不行吗?至少在我看来,最明显的问题就是类型不一致,因为我们得保障 地址类型(*)的接受变量是一致的。结合我们今天要介绍的知识就是,&的操作数是lvalue,但是产生的 结果是rvalue.
其实,这里就总结出来了左值和右值的一般定义就是: 在”=”赋值符左边的变量就是左值,相反在右边的就是 右值。
int y;
666 = y;
这个程序就算新手也不会这么写,那错在哪里呢?是把y保存到地址666位置上引起系统奔溃吗?从技术角度看 就是,”666”就是一个文字表达式常量(literal constant),实际就是,y没有保存到任何地方(因为编译器 根本识别不了)。
先看一个例子:
int setValue(){
return 6;
}
//...
setValue() = 3;
很明显,这是error,使用上面的例子我们知道了setValue函数的返回值就是一个右值。但是:
int global = 100;
int& setGlobal(){
return global;
}
// ...
setGlobal() = 400;
这个代码是ok的,注意,如果我不指明的话,上面的代码是不是看不出来是c++? 那么,假设这是c的话一点不突兀吧,再想一想自己过去在c++中参数的引用,不就是 这个样子呢?
这个代码返回了一个引用。引用就是一个指向已经存在的变量地址,上述代码就是(global), 因此这也是一个lvalue。注意这里的”&” 不是取地址符而是引用符号。
当然这个用法还是很少用的。
接着看一个例子。
int y = 10;
int& yref = y;
yref++; // Now y is 11
这里,yref的类型是int&,而且是变量y的引用,这个时候就可以正常的改变y的值 通过改变yref.现在可以明确一点的就是,引用必须指向一个具有明确位置的对象,y在这里是满足的,ok。
void func(int& x){
}
int main(){
func(10);// error!
// x = 10;
// func(x);
// is ok
}
output:
rvalue.cpp:14:9: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type int’
func(10);
^
其实这里涉及到了一个rvalue to lvalue conversion.而这种转化是错误的。但是,哈哈,又有意外:
const int& ref = 10; // It is ok
下面的代码也是ok的:
void func(const int& x){
}
int main(){
func(10);
}
为什么这样一改就可以呢?从来没有技术方面的考虑,就是从一个常理考虑的: 使用const修饰表明你不想修改这个参数,对不对。那么,你传递过去一个字符常量是没问题的。
在英文中,”&&”是 Double address operator或者 double ampersand, 假设:
int&& a; // "a" 是一个 r-value reference
而且在常规的用法中,”&&”是经常在函数中被声明一个参数, 再次澄清一遍,右值就是没有内存地址的一个符号。上面已 经说过,右值引用的不能够指向左值。[1]
这个教程是PLCT推出的,强烈推荐。在这篇文章中,我们就简单记录下这个学习重点。
这里是课程的github
计算机硬件组成: 自己画出来。
基本介绍: ISA是底层电路向上提供编程接口的一套规范; 指定规则和规范,让上层用户忽略下层电路的具体实现。IBM 360是第一个提供ISA的。
CISC:
RISC:
ISA的宽度:
ISA riscv 命名规范:
HART: Hardware Thread: 硬件线程。一个hart是虚拟的cpu,单独的thread. 资源的概念与cpu的功能类似。
特权级别: 三个级别: U(user) S(supervisor) M(Machine) 不同级别特权级别下分别对应各自一套的CSR。高级别的特权可以访问低级别的特权指令。
内存保护: 物理内存保护模式 PMP(physical Memory Protection)
异常(exception): 如不可访问的内存。调到特殊的地址来对异常进行处理;处理完之后会继续回到造成异常的指令地址。
中断:中断执行完成后,回到下一条地址。(外设,让你感觉没有发生一样)。
label: 冒号开始, 也是地址。
指令: 原指令。
伪指令:
directive: .xx
的形式。只有在汇编器里看到这个概念。
macro: 采用 .macro
和 .endm
自定义的宏。
要熟悉 域 的格式。 opcode不能唯一决定指令的类型。需要会看表。
主机字节序: Host Byte Order (HBO)
HEX: 0x11 22 33 44. Big-Endian: 低地址保存高位(0x11)
R: 三个field是, 每个5 bits。
I: Immediate: 12 bits.
B:
U:
J:
add: add
addi: addi rd, rs1, imm[31:20]
衍生的伪指令: neg rd,rs; == sub rd, 0, rs.
mv rd, rs; == addi rd, rs, 0
nop == addi x0, x0, 0
addi局限性: 一个寄存器的数值范围只有 [-2048, 2047),如果是大数的话(32位的话),需要自己构造一个: 先使用命令设置高20位,存放在rs1,然后复用现有addi命令补上剩余的低12位。
lui (Load Upper Immediate): imm[31:12] ==> 高20位, rd[11:7], opcode[6:0] lui x5, 0x12345 (x5 = 0x12345 « 12).
练习: 利用LUI+ADDi来加载0x12345678.
# 1
lui x1, 0x12345 # x1 = 0x12345000
addi x1, x1, 0x678
# 2 加载0x12345fff
lui x1, 0x12346 # x1 = 0x12346000
addi x1, x1, -1 # x1 = 0x12345fff(?)
li: li x5, 0x12345678
auipc: auipc x5, 0x12345 (x5=0x12345 « 12 + pc)
la:
and
or
xor
andi
ori
xori
逻辑左移和右移都会自动补0.
算术右移是根据符号位决定的。(没有左移)
load: 内存读指令, 将数据从内存读入寄存器。
store: 内存写指令, 将数据从内存写入寄存器。
LB:
LBU:
LH:
LHU:
LW:
SB:
SH:
SW.