嵌入式Linux應用程序開發


1.Linux成員命令的使用。

切換超級用戶:su 例:su - root

用戶管理:useradd:添加用戶賬號 passwd:設置賬號密碼 例:useradd liu;passwd liu

顯示進程:ps:顯示當前用戶運行進程列表 例:ps -ef

殺死進程:kill:輸出特定的信號給特定的PID(進程號) 例:kill -9 7421(kill -9為強制中止)

列出所有可用信號:kill -l

磁盤命令:fdisk -1(列出文件系統的分區情況,需root)

掛載分區:mount -t vfat /dev/hda1 /mnt/win/c(mount -t后面接上文件類型)

切換路徑:cd 例:cd /home/david(移至主目錄home下的david目錄)

cd -:回到前次目錄 ./:當前目錄 ../:上一級目錄

顯示文件:ls -l詳細信息 -a所有文件

新建目錄:mkdir 例:mkdir -m 777 ./why(777為文件權限) mkdir -p ./home/why

連接並顯示多個文件:cat 例:cat -n hello1.c hello2.c

復制移動刪除:cp -a 、mv -f 、rm -rf

例:cp -a ./why/mhy ./(將why/mhy下的文件復制到當前目錄下,保留原目錄下的文件)

mv -i ./why/mhy ./(將why/mhy目錄下的文件移動到當前目錄下,刪除原目錄下的文件)

rm -rf .why/mhy(刪除目錄下的文件,不需確認直接刪除)

更改三兄弟:chown(修改文件所有者和組別)、chgrp(修改文件的組所有權)、chmod(修改文件權限)

例:chown root hello.c(hello.c文件所有者改為root)

chgrp root hello.c(文件用戶組改為root)

chmod 777 hello.c(對文件擁有者(u)、所屬的用戶組(g)、系統里其他用戶(o)都有rwx(讀寫運行)權限)

搜索文件中的內容:grep 例:grep “hello” / -r(-r表示搜索范圍包括子目錄,若為-d skip 則忽略子目錄)

指定目錄搜文件:find 例:find ./ -name hello*.c(可以指定目錄,-name也可改為-user,后接用戶名)

查找文件(主要用於數據庫):locate 例:locate issue -U ./(在指定位置新建數據庫)

創建鏈接文件(為一個文件在另一個位置創建符號鏈接以節省空間):ln

例:ln -s ../hello1.c ./hello(當ls -l 時文件權限變為了lrwxrwxrwx,前面的l代表着文件為符號鏈接)

壓縮和解壓縮(只能壓縮單個文件,對多個文件需要打包后再壓縮,用的少):gzip

例:gzip hello.c(ls后文件名為hello.c.gz)

gzip -l hello.c(加入-l會顯示壓縮前和壓縮后的文件大小、壓縮百分比及壓縮前的名字)

壓縮和解壓縮:tar -zcvf壓縮 -zxvf解壓

例:tar -zcvf hello.c tar -zxvf hello.c.gz

文件差異:diff -f(很長很啰嗦)

打補丁:patch -pnum

網絡修改:ifconfig 例:ifconfig eth0 192.168.126.131(修改IP地址)

2.Linux系統中文件的類型和特點。p7

  • 普通文件(-):包括文本文件,shell 腳本, 二進制的可執行程序和各種類型的數據

  • 目錄文件(d):目錄也是文件,包含文件名和子目錄以及指向那些文件和子目錄的指針

  • 鏈接文件(l):類似於快捷方式,但是可以實現對不同的目錄、文件系統甚至不同機器上的文件直接訪問,並且不必重新占用磁盤

  • 設備文件(c, b):Linux 把設備當文件處理,方便了用戶的使用。設備相關文件放在 /dev 下

    • 字符設備(c):串行端口的接口設備

    • 塊設備(b):指數據的讀寫,是以塊為單位設備

3.位操作。

& 與操作

| 或操作

^ 異或操作
取反操作

.>>右移操作

<< 左移操作

4.Vi,gcc,gdb的使用。

Vim:i 插入

/name: 查找name字符串

N : 向下移動N行

:wq 保存退出

:q! 強制退出不保存

gcc:-E 只進行預編譯,不做其他處理 例:gcc -E hello.c -o hello.i(.i為預處理的C程序)

-S: 只編譯不匯編,生成匯編代碼 例:gcc -S hello.i -o hello.s

-c: 只編譯不鏈接,生成目標文件.o 例:gcc -c hello.s -o hello.o

-o: 自定義可執行文件名稱

-g: 可調試

-I dir: 頭文件搜索目錄

gdb:調試程序。gdb [可執行文件] 例:gdb hello

b N: 在N行設置斷點

l(list): 查看代碼

info b: 查看斷點

r(run): 運行程序

p n/i: 查看變量n/i

n: 步過(單步運行時不進入函數)

s: 步進(單步運行時進入函數)

c: 繼續(continue)

5.Makefile文件的編寫。

規則:

target: dependency_files
		command   /* 該行必須以 Tab 鍵開頭 */

示例:

david: kang.o yul.o
		gcc kang.o bar.o myprog
kang.o: kang.c kang.h head.h
		gcc -Wall -O -g -c kang.c -o kang.o
yul.o: bar.c head.h
		gcc -wall -O -g -c yul.c -o yul.o

改進:

OBJS= kang.o yul.o
CC = gcc
CFLAGS = -Wall -O -g
david: $(OBJS)
		$(CC) $(OBJS) -o david
kang.o: kang.c kang.h
		$(CC) $(OBJS) -c kang.c -o kang.o
yul.o:yul.c yul.h
		$(CC) $(CFLAGS) -C YUL.C -o yuil.o

makefile 中常見的自動變量

OBJS = kang.o yul.o
CC = gcc
CFLAGS= -Wall -O -g
david: $(OBJS)
		$(CC) $^ -O $@
kang.o: kang.c kang.h
		$(CC) $(CFLAGS) -c $< -o $@
yul.o: yul.c yul.h
		$(CC) $(CFLAGS) -C $< -O $@

隱含規則:$(CC) -c $(CPPFLAGS) $(CFLAGS)

OBJS= kang.o yul.o
CC = gcc
CFLAGS = -Wall -O -g
david: $(OBJS)
		$(CC) $^ -o $@

模式規則:

OBJS= kang.o yul.o
CC = gcc
CFLAGS = -Wall -O -g
david: $(OBJS)
		$(CC) $^ -o $@
%.o: %.c
		$(CC) $(CFLAGS) -c $< -o $@

6.嵌入式軟件設計,什么是交叉編譯?什么是交叉調試?p110

交叉編譯:在一個平台上生成另一個平台上執行的代碼。

交叉調試:在宿主機和目標機之間進行,調試器運行圖宿主機的通用操作系統之上,被調試的進程運行在特定硬件平台的嵌入式操作系統中,調試器和被調試進程通過串口或者網絡進行通信,調試器可以控制、訪問被調試進程,讀取被調試進程的當前狀態,並能夠改變被調試進程的運行狀態。

7.嵌入式開發常用的調試手段有哪幾種?簡述它們的優缺點。P111

嵌入式軟件經過編譯和連接后,進入調試階段。

嵌入式軟件開發,調試采用的是在宿主機和目標機之間進行交叉調試

交叉調試有很多方法,主要分為軟件方式和硬件方式:

軟件方式:

主要是通過插入調試樁的方式來進行的。是通過目的操作系統和調試器內分別加入某些功能模塊,兩者之間互通消息來進行調試。該方式的典型調試器有gdb調試器。

  • 優點:成本低、簡單、軟件調試能力強

  • 缺點:必須和宿主機建立通信連接,需要將軟件燒錄在目標板上(即只能調試運行在目標操作系統上的應用程序,不宜調試目標操作系統的內核代碼及啟動代碼),工作能力有限。

硬件調試:

  • ROM監視器

    • 優點:ROM監視器功能強大,能夠完成設置斷點、單步執行、查看寄存器、修改內存單元等各項調試功能。
    • 缺點:同軟件調試一樣,使用ROM監視器目標機必須和宿主機 通信連接。
  • ROM仿真器

    • 優點:避免了每次修改程序后都必須重新燒寫到目標機的ROM中。

    • 缺點:ROM仿真器本身比較昂貴,功能相對單一,只適用於某些特定場合。

  • 在線仿真器

    • 優點:功能強大,軟硬件都可做到完全實時在線調試。

    • 缺點:價格昂貴。

  • JTAG

    • 優點:連接簡單,成本低。
    • 缺點:特性受制於芯片廠商。

8.什么是優先級?簡述優先級反轉及其解決辦法。

優先級:優先級是計算機分時操作系統在處理多個作業程序時,決定各個作業程序接受系統資源的優先等級的參數。

分為:靜態優先級,動態優先級

優先級反轉:高優先級進程因為所需資源被低優先級進程占用而被阻塞,占用該資源的低優先級進程因其優先級低於其他進程也無法執行而釋放資源,造成最高優先級進程反而在一段時間內無法執行,系統性能下降的情況。

解決方法:

  • 優先級繼承

  • 設置優先級上限

9.簡述Linux的系統調用,用戶編程接口(API)、系統命令,以及它們之間的關系。p152

系統調用:操作系統給用戶提供的一組特殊接口,使用戶程序可以通過此接口獲得操作系統內核提供的服務

用戶編程接口:實際使用中程序員調用的接口,程序員通過調用用戶編程接口(API)來達到使用系統調用的目的,其再通過軟中斷機制向內核提交要求,獲取內核服務的接口。

系統命令:是可執行程序,內部引用了用戶編程接口實現相應功能

關系:P153 圖6.1

10.利用Linux的底層文件I/O操作函數,實現把一源文件src.txt的最后10KB復制成文件dst.txt。p157

/* copy_file.c */
/* 22. 利用Linux 的底層文件 I/O 操作函數,實現把一源文件 src.txt 的最后 10KB 復制成文件 dst.txt */

#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/type.c>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024                        /* 每次讀寫緩存大小,影響運行效率 */
#define SRC_FILE_NAME "src.txt"                 /* 源文件名 */
#define DEST_FILE_NAME "dst.txt"                /* 目標文件名 */
#define OFFSE 10240                             /* 復制的數據大小 */

int main() 
{
    int src_file, dest_file;
    unsigned char buff[BUFFER_SIZE];
    int real_ren_len;

    /* 以只讀的方式打開源文件 */
    src_file = open(SRC_FILE_NAME, O_RDONLY);

    /* 以只寫的方式打開目標文件 */
    dest_file = open(DEST_FILE_NAME, o_WRONLY|O_CREATE, S_IRUSR|S_iWUSR|S_IRGRP|S_IPOTH);
    
    if(src_file < 0 || dest_file < 0)
    {
        printf("Open file error\n");
        exit(1);
    }

    /* 將源文件的讀寫執行移動到最后 10KB 的起始位置 */
    lseek(src_file, -OFFSE, SEEK_END);

    /* 讀取源文件的最后 10KB 數據,並寫到目標文件中,每次讀寫 1KB */
    while((real_ren_len = read(src_file, buffer, sizeof(buff))) > 0)
    {
        write(dest_file, buff, real_ren_len);
    }
    close(dest_file);
    close(src_file);
}

11.Linux的文件鎖的類型和特點。p158

  • 建議性鎖:要求每一個上鎖文件的進程都要檢查是否有鎖的存在,並且尊重已有鎖(lockf()、fcntl())。
  • 強制性鎖:由內核執行,當一個文件被上鎖進行寫入操作的時候,內核將阻止其他文件對其進行讀寫操作(fcntl())

12.Linux的多路復用。p163

Linux多路復用通常指的是I/O多路復用,I/O多路復用通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。I/O 多路復用技術是為了解決進程或線程阻塞到某個I/O系統調用而出現的技術,使進程不阻塞於某個特定的 I/O 系統調用。

目前支持I/O多路復用的系統調用函數有select、poll、epoll等。

13.簡述串口的三種工作模式,每種工作模式的特點。p172

規范模式:輸入基於行處理

非規范模式:所有輸入即時生效,不可進行行編輯

原始模式:特殊的非規范模式,所有輸入以字節為單位處理

14.簡述Linux下進程的定義,結構和運行狀態。p203

進程是一個程序的一次執行過程,是資源分配的最小單元

結構:數據段、代碼段、堆棧段

  • 數據段:存放的是全局變量、常數以及動態數據分配的數據空間。
  • 代碼段:存放的是程序代碼的數據
  • 堆棧段:存放的是子程序的返回地址、子程序的參數以及程序的局部變量等。

運行狀態:執行、就緒、等待

  • 執行態:該進程正在執行,即進程正在占用CPU
  • 就緒態:進程已經具備執行的一切條件,正在等待分配CPU的處理時間片
  • 等待態:進程不能使用CPU,若等待事件發生(等待的資源分配到)則可被喚醒。

15.使用fork()創建一個子進程,然后讓子進程暫停5s(使用sleep()函數)。接下來對原有的父進程使用waitpid()函數,並使用參數WNOHANG使父進程不阻塞。若有子進程退出,則waitpid()返回0,並且父進程每隔一秒判斷一次。p216

/* waitpid.c */

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() 
{    
    pid_t pc, pr;

    pc = fork();
    if (pc < 0)
    {
        printf("Error fork\n");
    }
    else if(pc == 0)        /* 子進程 */
    {
        /* 子進程暫停 5s */
        sleep(5);
        /* 子進程正常退出 */
        exit(0);
    }
    else /* 父進程 */
    {
        /* 循環測試子進程是否退出 */
        do
        {
            /* 調用 waitpid, 且父進程不阻塞 */
            pr = waitpid(pc, NULL, WNOHANG);

            /* 若父進程還未退出,則父進程暫停 1s */
            if(pr == 0)
            {
                printf("The child process has not exited \n");
                sleep(1);
            }
        } while (pr == 0);

        /* 若發現子進程退出,打印相關情況 */
        if (pr == pc)
        {
            printf("Get child exit code: %d\n", pr);
        }
        else 
        {
            printf("Some error occured.\n");
        }
    }
}

16.嵌入式Linux系統中,什么是守護進程?如何編寫守護進程?p217

守護進程:是linux中的后台服務進程,獨立於控制終端並且周期性執行某種任務或者等待處理某些事件。守護進程常常系統載入時啟動,在系統關閉時中止。

編寫守護進程:

a) 創建子進程,父進程退出

b) 子進程創建新會話

c) 改變當前的目錄為根目錄

d) 重設文件權限掩碼

e) 關閉文件描述符

17.Linux中。兩種管道的各自的特點。p234

無名管道:

a) 只能用於具有親緣關系的進程間通信。

b) 半雙工的通信模式,具有固定的讀端和寫端。

c) 看成是特殊的文件,只存在於內核的內存空間中。

有名管道:

a) 互不相干的進程間通信

b) 可以通過路徑名支出,並且在文件系統中是可見的

18.創建管道,然后父進程使用fork()函數創建子進程,父進程關閉讀描述符,子進程關閉寫描述符,父子進程通過管道通信(通信內容自行定義)。p236

/* pipe.c */

#include <stdio.h>

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdilb.h>

#define MAX_DATA_LEN 256
#define DELAY_TIME 1

int main()
{
    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN];
    const char data[] = "Pipe Test Program";
    int real_read, real_write;

    memset((void *) buf, 0, sizeof(buf));
    
    /* 創建管道 */
    if(pipe(pipe_fd) < 0)
    {
        printf("pipe create error\n");
        exit(1);
    }

    /* 創建一個子進程 */
    if((pid = fork()) == 0)
    {
        /* 子進程關閉描述符,並通過子進程暫停 1s 等待父進程已關閉相應的讀描述符 */
        close(pipe_fd[1]);
        sleep(DELAY_TIME * 3);

        /* 子進程讀取管道內容 */
        if((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
        {
            printf("%d bytes read from the pipe is '%'\n", real_read, buf);
        }

        /* 關閉子進程讀描述符 */
        close(pipe_fd[0]);
        exit(0);
    }

    else if (pid > 0)
    {
        /* 父進程關閉描述符,並通過使父進程暫停 1s 等待子進程已關閉相應的寫描述符 */
        close(pipe_fd[0]);
        sleep(DELAY_TIME);

        if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
        {
            printf("Parent wrote %d bytes : '%s'\n", real_write, data);
        }

        /* 關閉父進程寫描述符 */
        close(pipe_fd[1]);

        /* 收集子進程退出信息 */
        waitpid(pid, NULL, 0);
        exit(0);
    }
}

19.Linux系統中信號的生命周期,對信號的處理方式。p243

信號的生命周期可以分為三個階段,三個階段由四個重要事件刻畫:信號產生、信號在進程中注冊、信號在進程中注銷、執行信號處理函數。相鄰兩個事件的時間間隔構成信號生命周期的一個階段。

生命周期:

  • 內核進程(信號的產生)

  • 用戶進程(信號注冊,信號注銷)

  • 信號處理

處理方式:

a) 忽略信號:不做任何處理,有兩個信號不能忽略,即SIGKILL和SIGSTOP。

b) 捕捉信號:信號發生時執行相應的自定義處理函數。

c) 執行默認操作:Linux對每種信號都規定了默認操作。

20.編寫程序,父子進程之間通過信號量實現同步,子進程睡眠3秒后打印出提示信息,然后父進程才能輸出信息。子進程調用raise()函數暫停,父進程給子進程發送SIGKILL信號后等待子進程終止,子進程終止后,父進程退出。p245

image-20210621111504361.png

21.Linux系統信號量的使用步驟。p255

  1. 創建信號量或獲取在系統已存在的信號量。(此時需要調用 semget() 函數。不同進程通過使用同一個信號量鍵值來獲取同一個信號量)
  2. 初始化信號量。(此時使用semctl() 函數的 SETVAL 操作)
  3. 進行信號量的 PV 操作。(調用 semop() 函數)
  4. 如果不需要信號量,則從系統中刪除它。(使用 semclt() 函數的 IPC——RMID 操作)

22.Linux系統中什么是共享內存?簡述實現共享內存的兩個步驟。p260

可以說,共享內存是一種最為高效的進程間通信方式。內核留出一片內存,由需要訪問的進程映射到自己的私有地址空間,使進程可以直接讀取這一內存區,大大提高了效率。

步驟:

  1. 創建共享內存。(shmget())
  2. 映射共享內存。(shmat())

23.Linux系統消息隊列的使用步驟。p266

  1. 創建或打開消息隊列。(msgget())
  2. 添加消息。(msgsnd())
  3. 讀取消息。(msgrcv())
  4. 控制消息隊列。(msgctl())

24.使用fork()函數創建一個子進程,父進程返回子進程的PID,聲明自己是父進程,子進程返回0值並且聲明自己是子進程。

image-20210621111728416.png

25.信號量編程。

image-20210621111708152.png

26.共享內存編程。p262

/*
    1. 首先創建一個共享內存區(鍵值為 IPC_PRIVATE), 之后創建子進程,在父子進程
        中將共享內存分別映射到各自的進程地址空間中
    2. 父進程等待用戶輸入,然后將用戶輸入的字符串寫入到共享內存中,之后往共享內
        存內部的頭部寫入 "WROTE" 字符串,表示父進程已成功寫入數據
    3. 子進程一直等到共享內存的頭部字符串為 "WROTE", 然后將共享內存的有效數據(父
        進程中輸入的數據)在屏幕上打印。
    4. 父子兩個進程完成以上工作后,分別解除與共享內存的映射關系
    5. 最后,在子進程中刪除共享內存。
/*

/*  shmem.c  */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 2048

int main()
{
    pid_t pid;
    int shmid;
    char *shm_addr;
    char flag[] = "WORTE";
    char *buff;

    /* 創建共享內存  */
    if((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
    {
        perror("shmget");
        exit(1);
    }
    else
    {
        printf("Create shared-memory:%d\\n", shmid);
    }

    /* 顯示共享內存情況 */
    system("ipcs -m");

    pid = fork();
    if(pid == -1)
    {
        perror("fork");
        exit(1);
    }
    else if(pid == 0)               /* 子進程處理  */
    {
        /* 映射共享內存 */
        if((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
        {
            perror("Child:shmat");
            exit(1);
        }
        else
        {
            printf("Child:Attach shared-memory:%p\\n", shm_addr);
        }
        system("ipcs -m");

        /* 通過檢查在共享內存的頭部是否標志字符串 “WROTE"來確認父進程已經向內存中寫入有效數據 */
        while(strncmp(shm_addr, flag, strlen(flag)))
        {
            printf("Child:Wait for emable data...\\n");
            sleep(5);
        }

        /* 獲取共享內存的有效數據並顯示 */
        strcpy(buff, shm_addr + strlen(flag));
        printf("Child:shared_memory:%s\\n", buff);

        /* 解除共享內存映射 */
        if((shmdt(shm_addr)) < 0)
        {
            perror("shmdt");
            exit(1);
        }
        else 
        {
            printf("Child:Deattach shared-memory\\n");
        }
        system("ipcs -m");

        /* 刪除共享內存  */
        if(shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("Child:shmctl(IPC_RMID)\\n");
            exit(1);
        }
        else
        {
            printf("Delete shared-memory\\n");
        }
        system("ipcs -m");
    }
    else /* 父進程處理  */
    {
        /* 映射共享內存 */
        if((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
        {
            perror("Parent: shmat");
            exit(1);
        }
        else
        {
            printf("Parent: Attach shared-memory:%p\\n", shm_addr);
        }

        sleep(1);
        printf("\\nInput some string:\\n");
        fgets(buff, BUFFER_SIZE, stdin);
        strncpy(shm_addr + strlen(flag), buff, strlen(buff));
        strncpy(shm_addr, flag, strlen(flag));

        /* 解除共享內存的映射 */
        if((shmdt(shm_addr)) < 0)
        {
            perror("Parent: shmdt");
            exit(1);
        }
        else
        {
            printf("Parent: Deattach shared-memory\\n");
        }
        system("ipcs -m");

        waitpid(pid, NULL, 0);
        printf("Finished\\n");
    }
    exit(0);
}

27.消息隊列編程。

image-20210621111639345.png

image-20210621111646221.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM