简述bootloader的定义.为什么需要进行bootloader移植?
BootLoader就是在操作系统运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。 种不同的CPU体系结构都有不同的BootLoader。除了依赖于CPU的体系结构外,BootLoader 还依赖于具体的嵌入式板级设备的配置,比如板卡的硬件地址分配,外设芯片的类型等。这也就是说,对于两块不同的开发板而言,即使它们是基于同一种CPU而构建的,但如果他们的硬件资源或配置不一致的话,要想在一块开发板上运行的BootLoader程序也能在另一块板子上运行,还是需要作修改。
BootLoader 大多采用两阶段,分别完成什么工作?
BootLoader 的 stage1 通常包括以下步骤:
•硬件设备初始化
•为加载 BootLoader 的 stage2 准备 RAM 空间
•拷贝 BootLoader 的 stage2 到 RAM 空间中
•设置好堆栈
•跳转到 stage2 的 C 入口点。
== BootLoader 的 stage2 通常包括以下步骤:
•初始化本阶段要使用到的硬件设备;
•调用内核。
#include
#include
int main(void)
{
pid_t pid;
int count=0;
pid = fork();
count++;
printf( “count = %d\n", count );
return 0;
}
输出:
count = 1
count = 1
子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0。
#include
#include
int main()
{
printf("Using exit...\n");
printf("This is the content in buffer");
exit(0);
}
[root@(none) 1]# ./exit
Using exit...
//This is the content in buffer
由于 printf 函数使用的是缓冲 I/O 方式,该函数在遇到“\n”换行符时自动从缓冲区中将记录读出。
_exit()函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的各种数据结构;
exit()函数与_exit()函数最大的区别就在于 exit()函数在调用 exit 系统之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的“清理 I/O 缓冲”一项。
关于进程间通信
管道是 Linux 中进程间通信的一种方式。这里所说的管道主要指无名管道,它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。它是一个半双工的通信模式,具有固定的读端和写端。管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等.数据被一个进程读出后,将被从管道中删除,其它读进程将不能再读到这些数据。
假设进程mysignal的进程号为2550,怎样通过命令向该进程发送一SIGINT信号,写出整个编译运行过程:
#include
#include
#include
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
}
int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
/*注册信号处理函数*/
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
exit(0);
}
关于信号
发送信号的函数主要有 kill()、raise()、alarm()以及 pause() .
Kill既可以向自身发送信号,也可以向其他进程发送信号,与kill函数不同的是,
raise函数是向进程自身发送信号。
alarm 也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,它就向进程发送 SIGALARM 信号。要注意的是,一个进程只能有一个闹钟时间。当系统捕捉到某个信号时,可以忽略该信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式。
- 和进程相比,它是一种非常“节俭”的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。
- 程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
除了以上所说的优点外,多线程程序作为一种多任务、并发的工作方式,有如下优点:- 使多CPU系统更加有效。操作系统会保证当线程 数不大于CPU数目时,不同的线程运行于不同的 CPU上。
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
网络编程:
TCP/IP 协议模型从一开始就遵循简单明确的设计思路,
简化为 4 层:网络接口层、网络层、传输层、应用层。
UDP 提供不可靠的非连接型传输层服务,它允许在源和目的地之间传送数据,而不必在传送数据之前建立对话。
TCP是重要的传输层协议,目的是允许数据同网络上的其他节点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输。TCP 协议具有严格的内装差错检验算法确保数据的完整性。
UDP也是传输层协议,它是无连接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,它不提供输入包的顺序,如果出现丢失包或重份包的情况,也不会向发送方发出差错报文。由于它执行功能时具有较低的开销,因而执行速度比TCP快。
Linux中的网络编程通过Socket(套接字)接口实现,Socket是一种文件描述符。 流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向连 接的通讯流。它使用了TCP协议。
TCP 保证了数据传输的正确性和顺序性。数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP.
原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议的测试等。
tcp服务器和客户端的编程步骤
基于TCP-服务器
收发数据,用函数send()和recv(),或者 read()和write() 6.关闭网络连接
基于TCP-客户端
关闭网络连接
简述udp服务器和客户端的编程步骤:
基于UDP-服务器
- 创建一个socket,用函数socket()
- 绑定IP地址、端口等信息到socket上, 用函数bind()
- 循环接收数据,用函数recvfrom()
- 关闭网络连接
基于UDP-客户端
- 创建一个socket,用函数socket()
- 绑定IP地址、端口等信息到socket上, 用函数bind()
- 设置对方的IP地址和端口等属性
- 发送数据,用函数sendto()
- 关闭网络连接
循环服务器:服务器在同一个时刻只可以响应一个客户端的请求;
并发服务器:服务器在同一个时刻可以响应多个客户端的请求;
TCP循环服务器一次只能处理一个客户端的请求。只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求。因为UDP是非面向连接的,没有一个客户端可以老是占住服务端, 服务器对于每一个客户机的请求总是能够满足。
int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout); Maxfd: 文件描述符的范围,比待检的最大文件描述符大1;Readfds:被读监控的文件描述符集; Writefds:被写监控的文件描述符集; Exceptfds:被异常监控的文件描述符集; Timeout:定时器
设备驱动
Linux用户程序通过设备文件来使用驱动程序操作字符设备和块设备。
主设备号用来标示与设备文件相连的驱动程序;
次设备号被驱动程序用来辨别操作的是哪个设备。
查看设备名、设备号,cat /proc/devices
手工创建设备文件 mknod /dev/设备文件名 设备类型 主设备号 此设备号;