嵌入式软件设计
-
Linux系统中文件的类型和特点
-
普通文件(-)
是用户日常使用最多的文件
-
包括:
文本文件
shell脚本
二进制的可执行程序
各种类型的数据
-
-
目录文件(d)
Linux中存储文件名的唯一地方
-
包括:
文件名
子目录名
指向文件和子目录的指针
-
-
链接文件(l)
可以实现对不同目录、文件系统、机器上的文件的直接访问,并且不必重新占用磁盘空间
-
设备文件(c, b)
-
包括:
-
块设备文件
以块为单位的设备(硬盘)
完成数据读写功能
-
字符设备文件
串行端口的接口设备
-
-
-
-
嵌入式开发常用的调试手段有哪几种?简述它们的优缺点
-
软件方式
-
插入调试桩方式
-
优点:
能与主机的调试器进行交互
-
缺点:
无法调试目标操作系统的内核代码及启动代码
造成调试版冗余
-
-
-
硬件方式
-
ROM监视器
-
优点:
能够完成设置断点、单步执行、查看寄存器、修改内存空间等各项调试功能
-
缺点:
使用ROM监视器目标机和宿主机必须建立通信连接
-
-
ROM仿真器
-
优点:
避免了每次修改程序后都必须重新烧写到目标机的ROM中
-
缺点:
本身比较昂贵,功能单一,只适用于特定场合
-
-
ICE仿真器
-
优点:
功能强大、软硬件都可做到完全实时在线调试
-
缺点:
价格昂贵
-
-
JTAG小板
-
优点:
连接简单,成本低
-
缺点:
特性受制于芯片厂商
-
-
-
-
什么是优先级?简述优先级反转及其解决办法
-
优先级:
-
静态优先级:
指在创建任务时即确定任务的优先级,并保持不变到任务运行结束
-
动态优先级:
指在创建任务时赋予给进程的优先级,在任务运行过程中可以动态改变,以便获得更高的调度性能
-
-
优先级反转:
-
定义:
高优先级进程因为所需资源被低优先级进程占用而被阻塞,占用该资源的低优先级进程因其优先级低于其他进程也无法执行而释放资源,造成最高优先级进程反而在一段时间内无法执行,系统性能下降的情况。
-
解决办法:
- 当低优先级进程占用某个资源时,将低优先级提高到能访问该资源的最高优先级
- 先来的进程访问某个资源,后来的进程也访问该资源,进行优先级判断,若后来的访问进程优先级高,将先来的进程优先级提高到和后来的进程同一级别
-
-
-
Linux文件锁的类型和特点
-
建议性锁
要求每个上锁文件的进程都要检查是否有锁存在,并且尊重已有的锁
-
强制性锁
是由内核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作
-
-
Linux的多路复用
select()和poll()的I/O多路转接模型是处理I/O复用的一个高效的方法。它可以具体设置程序中每一个所关心的文件描述符的条件、希望等待的时间等,从select()和poll()函数返回时,内核会通知用户已准备好的文件描述符的数量、已准备好的条件等。通过使用select()和poll()函数的返回结果,就可以调用相应的I/O处理函数。
-
Linux系统信号量的使用步骤
① 创建信号量或获得在系统已存在的信号量,此时需要调用semget()函数。
② 初始化信号量
③ 进行信号量的PV操作
④ 如果不需要信号量,则从系统中删除它
-
Linux系统消息队列的使用步骤
创建或打开消息队列 msgget()
添加消息 msgsnd()
读取消息 msgrcv()
控制消息队列 msgctl()
-
Linux系统中什么是共享内存?简述实现共享内存的两个步骤
-
定义
该内存区可以由需要访问的进程将其映射到自己的私有地址空间
-
步骤
创建共享内存:shmget() 从内存中获得一段共享内存区域
映射共享内存:shmat() 把创建的共享内存映射到具体的进程空间中
ps:撤销映射:shmdt()
-
-
Linux中,两种管道各自的特点
-
无名管道
用于具有亲缘关系的进程之间的通信
是一个半双工的通信模式,具有固定的独端和写端
-
有名管道
可以使互不相关的两个进程实现彼此通信
严格遵循先进先出规则
-
-
简述Linux的系统调用,用户编程接口(API)、系统命令以及它们之间的关系
-
系统调用:
指操作系统提供给用户程序调用的一组特殊接口,用户程序可以通过这组特殊接口来获得操作系统内核提供的服务
-
API:
操作系统的系统调用编程接口
-
系统命令:
是一个可执行程序
-
关系:
系统命令引用了API实现相应功能,API和系统调用共同完成函输的功能,API也可以独立完成函数的功能
-
-
简述串口的三种工作模式,每种工作模式的特点
-
规范模式
所有输入基于行进行处理
允许行编辑
-
非规范模式
即时有效的输入
不允许行编辑
read()函数有四种不同情况
-
原始模式
是一种特殊的非规范模式
所有输入数据以字节处理
终端不可回显
所有特定的输入/输出控制处理不可用
-
-
简述Linux下进程的定义,结构和运行状态
-
定义:
进程是一个程序的一次执行过程,是资源分配的最小单元
-
结构:
堆栈段(存放子程序返回地址、子程序的参数以及程序的局部变量)
数据段(存放全局变量、常数、动态数据分配的数据空间)
代码段(存放程序代码的数据)
-
运行状态:
执行态:进程正在占用CPU
就绪态:等待分配CPU的处理时间片
等待态:进程不能使用CPU,若等到资源分配,将其唤醒
-
-
Linux中信号的生命周期,对信号的处理方式
-
生命周期:
3个重要阶段,4个重要事件
内核进程:信号产生
↓
用户进程:信号在进程中注册、信号在进程中注销
↓
执行信号处理函数
-
对信号的处理方式:
-
忽略信号
对信号不做任何处理,但不能忽略SIGKILL和SIGSTOP
-
捕捉信号
定义信号处理函数
-
执行默认操作
Linux对每种信号都规定了默认操作
-
-
-
嵌入式Linux系统中,什么是守护进程?如何编写守护进程?
-
定义:
是LInux中的一个生存期较长的后台服务进程,独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件
-
如何编写:
- fork()创建子进程,exit()父进程退出
- setsid()在子进程中创建新会话
- chdir("/")改变当前目录为根目录
- umask(0)重设文件权限掩码
- close()关闭文件描述符
-
-
嵌入式软件设计,什么是交叉编译?什么是交叉调试?
-
交叉编译:
在一个平台上生成可以在另一个平台上执行的代码
-
交叉调试:
在宿主机(调试器)和目标机(被调试的进程)之间进行的调试方式
-
-
创建管道,接着父进程使用fork()函数创建子进程,父进程关闭读描述符,子进程关闭写描述符,父子进程通过管道通信(通信内容自行定义)
#include<unistd.h> #include<sys/types.h> #include<errno.h> #include<stdio.h> #include<stdlib.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 '%s' \\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); //运行正常退出程序 } }
-
使用fork()创建一个子进程,然后让子进程暂停5s(使用sleep()函数)。接下来对原有的父进程使用waitpid()函数并使用参数WNOHANG使父进程不阻塞。若有子进程退出,则waitpid()返回0,并且父进程每隔一秒判断一次
#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"); } } }
-
使用fork()函数创建一个子进程,父进程返回子进程的PID,声明自己是父进程,子进程返回0值并且声明自己是子进程
#include<sys/types.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> int main(void) { pid_t result; /* 调用fork()函数 */ result = fork(); /* 通过reult的值来判断fork()函数的返回情况。首先进行出错处理 */ if(result == -1) { printf("Fork error\n"); } else if(result == 0) //子进程 { printf("The returned value is %d\n In child process!!\n My PID is %d\n", result, getpid()); } else //父进程 { printf("The returned value is %d\n In father process!!\n My PID is %d\n", result, getpid()); } return result; }
-
利用Linux的底层文件I/O操作函数,实现把一源文件src.txt的最后10kb复制成文件dst.txt
#include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdlib.h> #include<stdio.h> #define BUFFER_SIZE 1024 #define SRC_FILE_NAME "src.txt" #define DEST_FILE_NAME "dst.txt" #define OFFSET 10240 int main() { int src_file, dest_file; unsigned char buff[BUFFER_SIZE]; int real_read_len; /* 以只读方式打开源文件 */ src_file = open(SRC_FILE_NAME, O_RDONLY); /* 以只写方式打开目标文件,若此文件不存在则创建该文件,访问权限值为644 */ dest_file = open(DEST_FILE_NAME, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IRGRP | S_IROTH); if(src_file < 0 || dest_file < 0) { printf("Open file error\n"); exit(1); } /* 将源文件的读写指针移到最后10kb的起始位置 */ lseek(src_file, -OFFSET, SEEK_END); /* 读取源文件的最后10kb数据并写到目标文件中,每次读写1kb */ while((real_read_len = read(src_file, buff, sizeof(buff))) > 0) { write(dest_file, buff, real_read_len); } close(dest_file); close(src_file); return 0; }
-
编写程序,父子进程之间通过信号量实现同步,子进程睡眠3秒后打印出提示信息,然后父进程才能输出信息。子进程调用raise()函数暂停,父进程给子进程发送SIGKILL信号后等待子进程终止,子进程终止后,父进程退出
-
信号量编程
/* sem_com.c */ #include"sem_com.h" /* 信号量初始化(赋值)函数 */ int init_sem(int sem_id, int init_value) { union semun sem_union; sem_union.val = init_value; //init_value为初始值 if(semctl(sem_id, 0, SETVAL, sem_union) == -1) { perror("Initialize semaphore"); return -1; } return 0; } /* 从系统中删除信号量的函数*/ int del_sem(int sem_id) { union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) { perror("Delete semaphore"); return -1; } } /* p操作函数 */ int sem_p(int sem_id) { struct sembuf sem_b; sem_b.sem_num = 0;// 单个信号量的编号应该为0 sem_b.sem_op = -1;// 表示p操作 sem_b.sem_flg = SEM_UNDO;// 系统自动释放将会在系统中残留的信号量 if(semop(sem_id, &sem_b, 1) == 1) { perror("p operation"); return -1; } return 0; } /* v操作函数 */ int sem_v(int sem_id) { struct sembuf sem_b; sem_b.sem_num = 0;// 单个信号量的编号应该为0 sem_b.sem_op = 1;// 表示v操作 sem_b.sem_flg = SEM_UNDO;// 系统自动释放将会在系统中残留的信号量 if(semop(sem_id, &sem_b, 1) == -1) { perror("V operation"); return -1; } return 0; }
/* fork.c */ #include<sys/types.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #define DELAY_TIME 3 int main(void) { pid_t result; int sem_id; sem_id = semget(ftok(".", 'a'), 1, 0666 | IPC_CREAT);// 创建一个信号量 init_sem(sem_id, 0); /* 调用fork()函数 */ result = fork(); if(result == -1) { perror("Fork\n"); } else if(result == 0)// 子进程 { printf("Child process will wait for some seconds...\n"); sleep(DELAY_TIME); printf("The returned value is %d in the child process(PID = %d)\n", result, getpid()); sem_v(sem_id); } else// 父进程 { sem_p(sem_id); printf("The returned value is %d in the father process(PID = %d)\n", result, getpid()); sem_v(sem_id); del_sem(sem_id); } exit(0); }
-
共享内存编程
/* 1. 首先创建一个共享内存区(键值为 IPC_PRIVATE), 之后创建子进程,在父子进程 中将共享内存分别映射到各自的进程地址空间中 2. 父进程等待用户输入,然后将用户输入的字符串写入到共享内存中,之后往共享内 存内部的头部写入 "WROTE" 字符串,表示父进程已成功写入数据 3. 子进程一直等到共享内存的头部字符串为 "WROTE", 然后将共享内存的有效数据(父 进程中输入的数据)在屏幕上打印。 4. 父子两个进程完成以上工作后,分别解除与共享内存的映射关系 5. 最后,在子进程中删除共享内存。 /* /* shmem.c */ #include <stdio.h> #include <syspes.h> #include <sys/ipc.h> #include <sysm.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); }
-
消息队列编程
/* msgsnd.c */ #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #define BUFFER_SIZE 512 struct message { long msg_type; char msg_text[BUFFER_SIZE]; }; int main() { int qid; key_t key; struct message msg; /* 根据不同的路径和关键字产生标准的key */ if((key = ftok(".", 'a')) == 1) { perror("ftok"); exit(1); } /* 创建消息队列*/ if((qid = msgget(key, IPC_CREAT | 0666)) == -1) { perror("msgget"); exit(1); } printf("Open queue %d\n", qid); while(1) { printf("Enter some message to the queue:"); if((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL) { puts("no message"); exit(1); } msg.msg_type = getpid(); /* 添加消息到消息队列 */ if((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0) { perror("message posted"); exit(1); } if(strncmp(msg.msg_text, "quit", 4) == 0) { break; } } exit(0); }
/* msgrcv.c */ #include <syspes.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define BUFFER_SIZE 512 struct message { long msg_type; char msg_text[BUFFER_SIZE]; }; int main() { int qid; key_t key; struct message msg; /* 根据不同路径和关键字产生标准的 key */ if((key = ftok(".", "a")) == -1) { perror("ftok"); exit(1); } /* 创建消息队列 */ if((qid == msgget(key, IPC_CREAT|0666)) == -1) { perror("msgget"); exit(1); } printf("Open queue %d\n", qid); do { /* 读取消息队列 */ memset(msg.msg_text, 0, BUFFER_SIZE); if(msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0) { perror("msgrcv"); exit(1); } printf("The message from process %d : %s", msg.msg_type, msg_text); } while(strncmp(msg.msg_text, "quit", 4)); /* 从系统内核中移走消息队列 */ if((msgctl(qid, IPCRMID, NULL)) < 0) { peror("msgctl"); exit(1); } exit(0); }
Ps:
Linux成员命令的使用、位操作、Vi,gcc,gdb的使用、Makefile文件的编写不做过多赘述(懒得写了)