Tuesday, July 30, 2024

MQTT Format

CONNECT: $10$15$00$04MQTT$04$02$00$3c$00$09TEST_MQTT

{0x10, 0x15, 0x00, 0x04, 'M', 'Q', 'T', 'T', 0x04, 0x02, 0x00, 0x3c, 0x00, 0x09, 'T', 'E', 'S', 'T', '_', 'M', 'Q', 'T', 'T'}


PUBLIC: $30$07$00$03LEDON

{0x30, 0x07, 0x00, 0x03, 'L', 'E', 'D', 'O', 'N'}


PINGREQ (keep alive): $C0$00 

{0xC0, 0x00}

--> Server respond: $82$08$00$01$00$03$6c$65$64$00


SUBSCRIBE: {0x82, 0x08, 0x00, 0x01, 0x00, 0x03, 0x6c, 0x65, 0x64, 0x00}

{30}{07}{00}{03}{6C}{65}{64}{4F}{4E}

Wednesday, January 24, 2024

ARRAY, VECTOR and LIST

ARRAY, VECTOR and LIST 

STL investigate

iterator

1. iterator là gì?

  • Nó giống như một CON TRỎ
  • Nó được sử dụng để trỏ đến các đối tượng trong STL (vd: array, vector, map, list, ...)

2. Vậy tại sao chúng ta phải sử dụng nó?

  • Nếu lấy array hoặc vector ra làm ví dụ thì không thấy được tầm quan trong của nó. vì những đối tượng này có vùng nhớ tập trung
  • Lấy map, list ra làm ví dụ - vì đối tượng này có vùng nhớ phi tập trung thì sử dụng iterator tiện hơn nhiều so với pointer

3. Sử dụng nó như thế nào:

  • cũng giống với con trỏ, sử dụng nó sẽ có 2 chức năng:
    • chức nâng 1: trỏ đến đối tượng đó.
    • chức nâng 2: truy cập đến đối tượng được trỏ tới.

4. VD: sử dụng trên array:

#include <array>
#include <iterator>

using namespace std;

int main()
{
    array<int, 5> arr = {1,2,3,4,5};
    array<int, 5>::iterator px;     // Khai báo iterator để trỏ đến đối tượng array
    px = arr.begin();               // chức nâng 1: trỏ đến đối tượng đầu tiên của arr
    *px = 10;                       // chức nâng 2: truy cập đến đối tượng được trỏ đến
    px[0] = 10;                     // chức nâng 2: tương tự như ở trên
    px += 1;                        // trỏ đến đối tượng tiếp theo trong array.
}
  • trong iterator có cái gì trong đó:
template <class T>
class iterator
{
    T& operator+(int index);
    /* tương tự cho trừ, nhân, chia */
    T& operator[](int index);
    T& operator*();
}

array

1. array là gì?

  • Nó là class, giống i xì xì array trong C luôn(vùng nhớ của nó cũng không phải ở dynamic). Cũng phải khai báo số lượng phần tử khi khai báo.

2. vậy tại sao chúng ta phải sử dụng array?

  • array trong C, thì khi chuyền đối số vào hàm, thì chương trình sẽ hiểu là con trỏ chứ không phải là một đối tượng array.
int sumArray(int arr[5])        // chú ý, ở đây nó không phải là array 5 đối tượng int, mà chỉ là int*
{
    int sum = 0;
    for(int i = 0; i < 5; i++)
    {
        sum += arr[i];
    }
    return sum;
}
void main()
{
    int arr_1[5] = {1,2,3,4,5};
    int x = sumArray(arr_1);        // trong trường hợp này thì không sao. 

    int arr_2[3] = {1,2,3};
    int y = sumArray(arr_2);        // trong trường hợp này không báo lỗi compiler, mà sẽ là "write access violation"
    
}
  • do đó trong cpp nó tạo ra 1 đối tượng array để cho phép chúng ta làm:
#include <array>

int sumArray(std::array<int, 5> arr)         // ở đây chỉ rỏ luôn, yêu cầu truyền vào 1 array 5 đối tượng int.
{
    int sum = 0;
    for( int i  = 0; i < 5; i++)
    { 
        sum += arr[i];
    }
    return sum;
}

void main()
{
    std::array<int, 5> arr_1 = {1,2,3,4,5};
    int x = sumArray(arr)_1;                  // trường hợp này hợp lệ quá rồi. không có gì để nói, yêu cầu truyền vào array 5 đối tượng int

    std::array<int, 3> arr_2 = {1,2,3};
    int y = sumArray(arr_2);                   // compiler sẽ báo lỗi, vì yêu cầu truyền vào array<int, 5> mà truyền vào array<int, 3>
}

vector

1. vector là gì?

  • Nó giống như array, nhưng xịn xò hơn, chúng ta có thể thêm/bớt phần tử -> mình hay gọi vector là array co dãn.
  • Khác với array, thì vùng nhớ ở DATA/BSS hoặc stack, còn vùng nhớ để lưu dữ liệu của vector thì được lưu ở HEAP (dynamic).
  • Khi chúng ta thêm bớt phần tử thì nó lại malloc để tạo ra vùng nhớ mới, và copy dữ liệu củ qua vùng nhớ mới, đồng thời thêm/bớt đối tượng mới vào. sao đó free vùng nhớ củ
    • dĩ nhiên, khi hàm hủy nó sẽ free vùng nhớ dymanic đó. S- Nhưng dù sao đi nữa thì những đối tượng trong vector vẫn có vùng nhớ liên tiếp nhau -> vùng nhớ chứa dữ liệu là vùng nhớ tập trung.

2. ví du:

#include <vector>
#include <stdio.h>
using namespace std;

int main()
{
	vector<char> vec = { 1,2,3,4 };
	printf("kich thuoc cua vec: %d doi tuong \r\n", vec.size());
	for (int i = 0; i < vec.size(); i++)
	{
		printf("0x%p ", &vec[i]);
	}
	vec.push_back(5);
	printf("\r\nkich thuoc cua vec: %d doi tuong \r\n", vec.size());
	for (int i = 0; i < vec.size(); i++)
	{
		printf("0x%p ", &vec[i]);
	}

	return 0;
}
  • ta đc kết quả:
kich thuoc cua vec: 4 doi tuong
0x009AA588 0x009AA589 0x009AA58A 0x009AA58B
kich thuoc cua vec: 5 doi tuong
0x009B0D08 0x009B0D09 0x009B0D0A 0x009B0D0B 0x009B0D0C
  • chúng ta thấy rằng. khi in địa chỉ của các đối tượng trong vector thì chúng nó cách nhau 1byte - vì đối tượng char. -> vùng nhớ chứa dữ liệu là vùng nhớ tập trung.

  • chúng ta thấy rằng. khi ta push 1 đối tượng vào thêm. thì nó tạo ra vùng nhớ mới, vì địa chỉ của các đối tượng bây giờ đã khác.

  • thêm một ví dụ nữa để chứng minh rằng vector lưu dữ liệu tập trung

#include <list>
#include <vector>
#include <stdio.h>
using namespace std;

int main()
{
	vector<int> v = { 1,2,3,4 };
	vector<int>::iterator px = v.begin();
	for (int i = 0; i< v.size(); i++)
	{
		printf("%d in 0x%p | ", *px, px._Ptr);
		px++;
	}


	return 0;
}
  • ta được kết quả
1 in 0x013CE970 | 2 in 0x013CE974 | 3 in 0x013CE978 | 4 in 0x013CE97C |
  • ta có thể kiểm tra memory ở địa chỉ 0x013CE970
0x013CE970  00000001 00000002 00000003 00000004  ................
0x013CE980  fdfdfdfd dddddddd 48fbaf50 0c00fe1b  ýýýýÝÝÝÝP¯ûH.þ..
0x013CE990  013cf7f0 013ce9c8 569e4818 00000023  ð÷<.Èé<..HžV#...
0x013CE9A0  00000002 00000008 00000066 fdfdfdfd  ........f...ýýýý

-> đó. ta thấy nó tập trung nè.

3. vậy tại sao chúng ta phải sử dụng vector.?

  • đó. nó có thể co dãn đó. điểm nâng cấp lớp từ array rồi còn gì
  • nhưng nó có nhược điểm nè. khi ta push thêm 1 đối tượng vào, thì nó sẽ lại tốn công malloc vùng nhớ mới, rồi copy dữ liệu từ vùng nhớ củ sang vùng nhớ mới. TỐN CÔNG QUÁ

list

1. list là gì?

  • so sánh list với vector và array
    • giống nhau: chúng nó điều là contaner đùng để chứa nhiều đối tượng
    • khác nhau:
      vector/arrylist
      dữ liệu tập trungdữ liệu phân tán
      • cùng đi chứng minh nó phân tán nhé.
        #include <list>
        #include <vector>
        #include <stdio.h>
        using namespace std;
        
        int main()
        {
            list<int> l = { 1,2,3,4 };
            list<int>::iterator px = l.begin();
            for (int i = 0; i< l.size(); i++)
            {
                printf("%d in 0x%p | ", *px, px._Ptr);
                px++;
            }
        
        
            return 0;
        }
        
        • ta sẽ được kết quả:
        1 in 0x01470DB0 | 2 in 0x01471050 | 3 in 0x01470CD0 | 4 in 0x01470BB8 |
        
        -> ta thấy rằng vùng nhớ chúng nó không liên tục
        • kiểm tra vùng nhớ ở đối tượng đầu tiên:
        0x01470DB0  01471050 01470a68 00000001 fdfdfdfd  P.G.h.G.....ýýýý
        
        • kiểm tra đối tượng thứ 2 ở địa chỉ 0x01471050
        0x01471050  01470cd0 01470db0 00000002 fdfdfdfd  Ð.G.°.G.....ýýýý
        
        • kiểm tra đối tượng thứ 3 ở địa chỉ 0x01470cd0
        0x01470CD0  01470bb8 01471050 00000003 fdfdfdfd  ¸.G.P.G.....ýýýý
        
        • kiểm tra đối tượng thứ 4 ở địa chỉ 0x01470bb8
        0x01470BB8  01470a68 01470cd0 00000004 fdfdfdfd  h.G.Ð.G.....ýýýý
        
        • kiểm tra đối tượng thứ 5 ở địa chỉ 0x01470a68
        0x01470A68  01470db0 01470bb8 cdcdcdcd fdfdfdfd  °.G.¸.G.ÍÍÍÍýýýý
        
        • Chúng ta thấy rằng: (nó tạo ra 5 node. mỗi node sẽ chứa)
          • 4 byte đầu là địa chỉ đối tượng tiếp theo,
          • 4 byte tiếp theo là địa chỉ đối tượng ở trước nó
          • những byte tiếp theo là giá trị của đối tượng.
        • Vậy thì khác với vector:
          • do dử liệu nó phi tập trung. nên khi thêm bớt 1 đối tượng vào thì nó sẽ đơn giản hơn. chỉ cần tạo ra một node là xong. cái này nó hơn hẳn vector
          • nhưng khi chúng ta muốn truy cập đến đối tượng thứ 3 (node thứ 3) thì nó sẽ truy cập tới node 0 -> node 1 -> node 2 -> node 3. cái này thì nó thua vector rồi.

Thursday, November 9, 2023

Cách float được lưu trong ARM (theo chuẩn:  IEEE-754)

float có kích thước 32 bits (4bytes)

1. Thành phần

bao gồm 3 thành phần:

sign bit: bit cao nhất (bit 31)

vị trí dấu chấm động: 8 bits tiếp theo (từ bit 30 đến bit 23) [30:23]. và được offset cho 127 (có nghĩa là trừ cho 127)

phần giá trị: từ bit 22 đến bit 0 [23:0].

2. Ví dụ

lấy số: -12.5 -> sẽ được lưu trong memory: 0xC1480000

1100 0001 0100 1000 0000 0000 0000 0000

sign bit: 1 

vị trí dấu chấm động: 100 0001 0= 130; 130 - 127 = 3

phần giá trị: 1100 1000 0000 0000 0000 0000: sẽ tự động được thêm bit 1 vào.

áp vị trí dấu chấm động vào:

1100. 1000 0000 0000 0000 0000

1100b = 12d.

có thể tính theo công thức hồi xưa học tin lớp 11: (1 × 23) + (1 × 22) + (0 × 21) + (0 × 2) = 12

. 1000 0000 0000 0000 0000 phần lẽ: (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + ... = 0.5

Cộng 2 cái này lại với nhau ta có: 12 + 0.5 = 12.5 

kết hợp sign bit 1 (dấu âm) là -12.5



Saturday, September 16, 2023

Linux kernel module

 Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system.

Custom codes can be added to Linux kernels via two methods.

  • The basic way is to add the code to the kernel source tree and recompile the kernel.
  • A more efficient way is to do this is by adding code to the kernel while it is running. This process is called loading the module, where module refers to the code that we want to add to the kernel.

Since we are loading these codes at runtime and they are not part of the official Linux kernel, these are called loadable kernel module(LKM), which is different from the “base kernel”. Base kernel is located in /boot directory and is always loaded when we boot our machine whereas LKMs are loaded after the base kernel is already loaded. Nonetheless, these LKM are very much part of our kernel and they communicate with base kernel to complete their functions.

LKMs can perform a variety of task, but basically they come under three main categories,

  • device driver,
  • filesystem driver and
  • System calls.

So what advantage do LKMs offer?
One major advantage they have is that we don’t need to keep rebuilding the kernel every time we add a new device or if we upgrade an old device. This saves time and also helps in keeping our base kernel error free. A useful rule of thumb is that we should not change our base kernel once we have a working base kernel.
Also, it helps in diagnosing system problems. For example, assume we have added a module to the base kernel(i.e., we have modified our base kernel by recompiling it) and the module has a bug in it. This will cause error in system boot and we will never know which part of the kernel is causing problems. Whereas if we load the module at runtime and it causes problems, we will immediately know the issue and we can unload the module until we fix it.
LKMs are very flexible, in the sense that they can be loaded and unloaded with a single line of command. This helps in saving memory as we load the LKM only when we need them. Moreover, they are not slower than base kernel because calling either one of them is simply loading code from a different part of memory.

**Warning: LKMs are not user space programs. They are part of the kernel. They have free run of the system and can easily crash it.

So now that we have established the use loadable kernel modules, we are going to write a hello world kernel module. That will print a message when we load the module and an exit message when we unload the module.

Code:

/**
 * @file    hello.c
 * @author  Akshat Sinha
 * @date    10 Sept 2016
 * @version 0.1
 * @brief  An introductory "Hello World!" loadable kernel
 *  module (LKM) that can display a message in the /var/log/kern.log
 *  file when the module is loaded and removed. The module can accept
 *  an argument when it is loaded -- the name, which appears in the
 *  kernel log files.
*/
#include <linux/module.h>     /* Needed by all modules */
#include <linux/kernel.h>     /* Needed for KERN_INFO */
#include <linux/init.h>       /* Needed for the macros */
  
///< The license type -- this affects runtime behavior
MODULE_LICENSE("GPL");
  
///< The author -- visible when you use modinfo
MODULE_AUTHOR("Akshat Sinha");
  
///< The description -- see modinfo
MODULE_DESCRIPTION("A simple Hello world LKM!");
  
///< The version of the module
MODULE_VERSION("0.1");
  
static int __init hello_start(void)
{
    printk(KERN_INFO "Loading hello module...\n");
    printk(KERN_INFO "Hello world\n");
    return 0;
}
  
static void __exit hello_end(void)
{
    printk(KERN_INFO "Goodbye Mr.\n");
}
  
module_init(hello_start);
module_exit(hello_end);

Explanation for the above code:
Kernel modules must have at least two functions: a “start” (initialization) function called init_module() which is called when the module is insmoded into the kernel, and an “end” (cleanup) function called cleanup_module() which is called just before it is rmmoded. Actually, things have changed starting with kernel 2.3.13. You can now use whatever name you like for the start and end functions of a module. In fact, the new method is the preferred method. However, many people still use init_module() and cleanup_module() for their start and end functions. In this code we have used hello_start() as init function and hello_end() as cleanup function.
Another thing that you might have noticed is that instead of printf() function we have used printk(). This is because module will not print anything on the console but it will log the message in /var/log/kern.log. Hence it is used to debug kernel modules. Moreover, there are eight possible loglevel strings, defined in the header , that are required while using printk(). We have list them in order of decreasing severity:

  • KERN_EMERG: Used for emergency messages, usually those that precede a crash.
  • KERN_ALERT: A situation requiring immediate action.
  • KERN_CRIT: Critical conditions, often related to serious hardware or software failures.
  • KERN_ERR: Used to report error conditions; device drivers often use KERN_ERR to report hardware difficulties.
  • KERN_WARNING: Warnings about problematic situations that do not, in themselves, create serious problems with the system.
  • KERN_NOTICE: Situations that are normal, but still worthy of note. A number of security-related conditions are reported at this level.
  • KERN_INFO: Informational messages. Many drivers print information about the hardware they find at startup time at this level.
  • KERN_DEBUG: Used for debugging messages.
  • We have used KERN_INFO to print the message.

    Preparing the system to run the code:
    The system must be prepared to build kernel code, and to do this you must have the Linux headers installed on your device. On a typical Linux desktop machine you can use your package manager to locate the correct package to install. For example, under 64-bit Debian you can use:

    akshat@gfg:~$ sudo apt-get install build-essential linux-headers-$(uname -r)
    

    Makefile to compile the source code:

    obj-m = hello.o
    all:
            make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
    clean:
            make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    

    **Note: Don’t forget the tab spaces in Makefile

    Compiling and loading the module:
    Run the make command to compile the source code. Then use insmod to load the module.

    akshat@gfg:~$ make
    make -C /lib/modules/4.2.0-42-generic/build/ M=/home/akshat/Documents/hello-module modules
    make[1]: Entering directory `/usr/src/linux-headers-4.2.0-42-generic'
      CC [M]  /home/akshat/Documents/hello-module/hello.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/akshat/Documents/hello-module/hello.mod.o
      LD [M]  /home/akshat/Documents/hello-module/hello.ko
    make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-42-generic'
    

    Now we will use insmod to load the hello.ko object.

    akshat@gfg:~$ sudo insmod hello.ko
    

    Testing the module:
    You can get information about the module using the modinfo command, which will identify the description, author and any module parameters that are defined:

    akshat@gfg:~$ modinfo hello.ko
    filename:       /home/akshat/Documents/hello-module/hello.ko
    version:        0.1
    description:    A simple Hello world LKM
    author:         Akshat Sinha
    license:        GPL
    srcversion:     2F2B1B95DA1F08AC18B09BC
    depends:        
    vermagic:       4.2.0-42-generic SMP mod_unload modversions
    

    To see the message, we need to read the kern.log in /var/log directory.

    akshat@gfg:~$ tail /var/log/kern.log
    ...
    ...
    Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world
    To unload the module, we run rmmod:
    akshat@gfg:~$ sudo rmmod hello
    Now run the tail command to get the exit message.
    akshat@gfg:~$ tail /var/log/kern.log
    ...
    Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world
    Sep 10 17:45:42 akshat-gfg kernel: [26503.773982] Goodbye Mr.