arm指令集

学习arm指令之前复习一下关于位运算的知识
16位数值0x1234中0x12是高位0x34是低位
大端序Big Endian
高位字节在前低地址低位字节在后高地址
存储顺序0x12 0x34
左移和右移
左移右移便是将数据的二进制数据向左/向右移动若干位同时进行填充
实际应用

  • 提取高位
    int x = 0x1234; // 二进制0001 0010 0011 0100
    int high = x >> 8; // 高位右移 8 位0000 0000 0001 00100x12
  • 提取低位
    int x = 0x1234; // 二进制0001 0010 0011 0100
    int low = x & 0xFF; // 低位与掩码按位与0011 01000x34

Read More

进程-信号量

介绍信号量之前我们先介绍一下资源这些事
当多个进程同时访问一块共享空间的时候会出现资源争夺的情况最终导致数据混乱
临界资源只不能被多个进程同时访问的资源
同步与互斥
互斥即同一时间只能有一个进程访问资源
同步在互斥的基础上增加不同进程对资源的访问顺序
实现以上的手段便是信号量
信号量是什么
信号量: 由内核维护的整数,其值被限制为大于或等于0


信号量集合的创建

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);  

初始化信号量

int semctl(int semid, int semnum, int cmd, …);  

信号量的操作

int semop(int semid, struct sembuf *sops, size_t nsops);
//信号量操作函数<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>用于占用信号量<span class="bd-box"><h-char class="bd bd-beg"><h-inner>、</h-inner></h-char></span>释放信号量<span class="bd-box"><h-char class="bd bd-beg"><h-inner>、</h-inner></h-char></span>设置信号量等待  

信号量集合的删除

//信号量集合调用 semctl 函数<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>设置命令为 IPC_RMID
ret = semctl(semid,IPC_RMID,NULL);  

信号量同步

进程-消息队列与共享内存

消息队列
消息队列就是一个消息的列表,进程可以在消息队列中添加消息和读取消息
消息队列具有一定的FIFO特性,具有无名管道与有名管道的各自的优势,可以支持任意两个进程的进程间通讯

创建消息队列
用法

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
//key : 由 ftok 函数合成
//msgflg : 消息队列标志

删除消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/*
msqid : 消息队列 ID
msgp : 消息结构体指针
msgsz : 消息内容的长度
msgflg : 消息队列标志<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>默认可以填 0
IPC_NOWAIT : 可以设置非阻塞
*/

接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/*
msqid : 消息队列 id
msgp : 消息结构指针
msgtyp : 消息类型
msgflg : 消息队列标志<span class="bd-box"><h-char class="bd bd-beg"><h-inner>,</h-inner></h-char></span>默认可以填0
IPC_NOWAIT : 可以设置非阻塞
*/

共享内存
共享内存是将分配的物理空间直接映射到进程的用户虚拟地址空间中,减少数据在内核空间缓存
共享内存是一种效率较高的进程间通讯的方式
共享内存创建

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
//创建一个共享内存,并返回 ID

共享内存删除

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

共享内存映射

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);//将进程地址空间映射到共享内存上

共享内存解除映射

int shmdt(const void *shmaddr);//解除进程地址空间与共享内存的映射

完整代码
shm_write.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATHNAME "."
#define PRO_ID 100

#define SZ 256


int main(void)
{
key_t key;
int shmid,ret;
void *addr = NULL;

key = ftok(PATHNAME,PRO_ID);
if(key == -1){
perror("[ERROR] key(): ");
exit(EXIT_FAILURE);
}

shmid = shmget(key,SZ,IPC_CREAT|0666);
if(shmid == -1){
perror("shmid(): ");
exit(EXIT_FAILURE);
}

printf("shmid = %d\n",shmid);
addr = shmat(shmid, NULL, 0);
if (addr == (void *)-1){
perror("[ERROR] shmat(): ");
return -1;
}

memset(addr,'A',10);

shmdt(addr);

return 0;
}


shm_read.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATHNAME "."
#define PRO_ID 100

#define SZ 256


int main(void)
{
key_t key;
int shmid,ret,i;
char buffer[10] = {0};
void *addr = NULL;

key = ftok(PATHNAME,PRO_ID);
if(key == -1){
perror("[ERROR] key(): ");
exit(EXIT_FAILURE);
}

shmid = shmget(key,SZ,IPC_CREAT|0666);
if(shmid == -1){
perror("shmid(): ");
exit(EXIT_FAILURE);
}

printf("shmid = %d\n",shmid);
addr = shmat(shmid, NULL, 0);
if (addr == (void *)-1){
perror("[ERROR] shmat(): ");
return -1;
}

memcpy(buffer,addr,10);

for (i = 0;i < 10;i++){
printf(" %c ",buffer[i]);
}

putchar('\n');


shmdt(addr);

return 0;
}

arm基础(1)

ARM核(ARMv7)的寄存器资源

User模式寄存器是基础寄存器集
当处理器运行在其他模式如 Supervisor 或 IRQ 模式如果对应的模式没有自己独立的寄存器版本那么就会直接使用 User 模式的寄存器例如 R0_usr 到 R12_usr

某些模式有独立的寄存器版本
快速中断模式FIQ
FIQ 模式为了提高中断处理效率设计了独立的寄存器集R8_fiq 到 R12_fiq

空白表示共享 User 模式的寄存器
在 SupervisorAbort 等模式中R0-R12 的寄存器列表是空白的这意味着这些模式下仍然使用 User 模式的寄存器版本例如 R0_usr 到 R12_usr


ARM工作模式

  • User用户模式普通的用户态程序运行模式
  • System系统模式类似用户模式但拥有更高的权限
  • Supervisor管理模式处理系统调用和异常比如 SVC
  • Abort终止模式访问无效内存时进入的模式
  • Undefined未定义模式运行未定义指令时进入的模式
  • IRQ中断模式处理普通中断
  • FIQ快速中断模式处理快速中断
  • Monitor监控模式安全扩展相关的模式
  • Hyp虚拟化模式虚拟化扩展相关的模式

寄存器用途分析

QThread

关于继承QThread线程对象在哪个线程中的判断挺简单只要记住对象在哪个线程中被创建存在自身实例对象就在哪个线程中
在Qt中一个对象的槽函数在被信号触发调用的时候槽函数在那个线程中执行取决于它的对象属于那个线程中

默认情况一下我们在代码中创建的对象都属于主线程这个对象的槽函数在调用的时候占用的都是主线程的时间我们也可以将一个QObject类型的对象或子类对象通过moveToThread移动到子线程中去这样当这个对象的槽函数被信号触发调用的时候槽函数占用的就是子线程的时间
这个方法会更改此对象及其子对象的线程关联性如果对象有父对象则不能移动该对象

槽函数可以跟任意线程的任意信号建立连接

在Qt中QThread类用于创建和管理线程QThread类提供了一个run()方法该方法定义了线程要执行的任务然而直接调用run()方法并不会在一个单独的线程中执行而是在当前线程中执行这是因为QThread的run()方法实际上是一个普通的成员函数而不是开启一个新的线程

为了在一个单独的线程中执行任务应该调用QThread的start()方法start()方法会在一个新的线程中执行run()方法从而实现并发执行这是因为start()方法会在内部创建一个新的系统线程并将run()方法在该线程中执行

QDataStream数据流

QDataStream类用于将任意常见类型的数据以二进制写入到指定的目标中同时也可以按写入的顺序从目标中读取这些类型的数据,实现数据的序列化

这个操作在通信中很有用例如:我需要将一个学生对象传递给对方我只需通过QDataStream把学生对象序列化成一个二进制数据发送给对方对方通过QDataStream反序列化就可以得到这个学生对象的数据

数据流写入

当使用运算符重载<<或者使用writeBytes函数时写入QString QByteArray,QPicture,const char *类型的数据时qt会自动在数据前面添加4个字节的长度信息

QTextStream文本流

用法

1
2
3
4
5
6
7
8
9
10
QTextStream()  
QTextStream(QIODevice *device)
QTextStream(FILE *fileHandle,
QIODevice::OpenMode openMode = QIODevice::ReadWrite)
QTextStream(QString *string,
QIODevice::OpenMode openMode = QIODevice::ReadWrite)
QTextStream(QByteArray *array,
QIODevice::OpenMode openMode = QIODevice::ReadWrite)
QTextStream(const QByteArray &array,
QIODevice::OpenMode openMode = QIODevice::ReadOnly)

例子

1
2
3
4
5
6
QFile data("output.txt");
if (data.open(QFile::WriteOnly | QFile::Truncate)) {
QTextStream out(&data);
out << "Result: " << qSetFieldWidth(10) << left << 3.14 << 2.7;
// writes "Result: 3.14 2.7 "
}

需要格外注意的是如果使用了运算符重载<<进行数据写入需要用.flush()方法来刷新缓存才能成功写入

QString的使用

字符串拼接

QString对于很多运算符做了重载可以直接用+号或者.append方法有时候想要用占位符替代的话需要以下这种形式

QString str1 = QString("%1---------------%2").arg("hello").arg(" world!");

这样输出str1的内容就是hello world!

字符串长度计算

str1.size() / str1.count() / str1.length() 都可以一个中文算一个字符

字符串判断为空

.isNull() 只有当未被赋值的时候才为真若 QString str1 = “”; 返回false
如果是.isEmpty() 返回真 若未被赋值, .isEmpty()也返回真

字符串提取子字符串

1
2
3
4
5
6
7
8
QString str;
QString csv = "forename,middlename,surname,phone";
QString path = "/usr/local/bin/myapp"; // First field is empty
QString::SectionFlag flag = QString::SectionSkipEmpty;

str = csv.section(',', 2, 2); // str == "surname"
str = path.section('/', 3, 4); // str == "bin/myapp"
str = path.section('/', 3, 3, flag); // str == "myapp"

qt-事件过滤器

作为事件过滤器的要求是QOject对象监听的事件为QEvent的对象
事件过滤器对象需要重写eventFilter()函数函数的参数不需要自己传目标对象会自动传前提是使用了installEventFilter函数