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
: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
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
");
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
");
}
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
");
sleep(1);
}
} while (pr == 0);
/* 若发现子进程退出,打印相关情况 */
if (pr == pc)
{
printf("Get child exit code: %d
", pr);
}
else
{
printf("Some error occured.
");
}
}
}
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
");
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 '%'
", 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'
", 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
21.Linux系统信号量的使用步骤。p255
- 创建信号量或获取在系统已存在的信号量。(此时需要调用 semget() 函数。不同进程通过使用同一个信号量键值来获取同一个信号量)
- 初始化信号量。(此时使用semctl() 函数的 SETVAL 操作)
- 进行信号量的 PV 操作。(调用 semop() 函数)
- 如果不需要信号量,则从系统中删除它。(使用 semclt() 函数的 IPC——RMID 操作)
22.Linux系统中什么是共享内存?简述实现共享内存的两个步骤。p260
可以说,共享内存是一种最为高效的进程间通信方式。内核留出一片内存,由需要访问的进程映射到自己的私有地址空间,使进程可以直接读取这一内存区,大大提高了效率。
步骤:
- 创建共享内存。(shmget())
- 映射共享内存。(shmat())
23.Linux系统消息队列的使用步骤。p266
- 创建或打开消息队列。(msgget())
- 添加消息。(msgsnd())
- 读取消息。(msgrcv())
- 控制消息队列。(msgctl())
24.使用fork()函数创建一个子进程,父进程返回子进程的PID,声明自己是父进程,子进程返回0值并且声明自己是子进程。
25.信号量编程。
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);
}