《嵌入式程序设计》第五周学习总结
##教材学习总结 一.标准I/O编程 标准I/O提供流缓冲的目的是尽可能减少使用read()和write()等系统调用的数量。 二.标准I/O3种类型的缓冲存储 (1)全缓冲:在这种情况下,当填满标准 I/O 缓存后才进行实际I/O 操作。存放在磁盘上的文件通常是由标准I/O 库实施全缓冲的。在一个流上执行第一次I/O 操作时,通常调用malloc()就是使用全缓冲。 (2)行缓冲:在这种情况下,当在输入和输出中遇到行结束符时,标准 I/O 库执行I/O 操作。这允许我们一次输出一个字符(如fputc()函数),但只有写了一行之后才进行实际I/O操作。标准输入和标准输出就是使用行缓冲的典型例子 (3)不带缓冲:标准 I/O 库不对字符进行缓冲。如果用标准I/O 函数写若干字符到不带缓冲的流中,则相当于用系统调用write()函数将这些字符全写到被打开的文件上。标准出错stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个行结束符。 三.基本操作 (1)打开文件 ①函数说明 打开文件有三个标准函数,分别为:fopen(),fdopen()和freopen()。它们可以以不同的模式打开。但都返回一个指向FILE的指针,该指针指向对应的I/O流。此后,对文件的读写都是通过这个FILE指针来进行。其中fopen()可以指定打开文件的路径和模式。 ②函数格式定义 所需头文件:```#include(2)就绪态(Ready)。当进程已分配到除处理机以外所有必要的资源时,它将处于准备执行状态,一旦获CPU,便立即执行。在一个系统中,可以有多个进程同时处于就绪状态。按时间片的大小,可构成多个就绪队列。
(3)阻塞态(Blocked)。一个进程正等待输入输出或等待某一事件发生而暂时停止执行,即使把处理机分配给该进程也无法执行。称此刻进程所处的状态为阻塞状态(等待状态)。引起进程阻塞的事件有多种,例如,请求I/O,申请缓冲区,等待某个信号或消息,系统中同时处于阻塞状态的进程可以有多个,根据阻塞的原因(比如,阻塞到哪个资源信号量上),可分成多个阻塞队列。
五.引起创建进程的事件:
1.用户登录:
为终端用户建立一进程
2.作业调度:(不是进程调度)
为被调度的作业建立进程
3.提供服务:
如要打印时建立打印进程
4.应用请求:
由应用程序建立多个进程
六.进程创建的方式
① 由系统模块统一创建。例如,在批处理系统中,由操作系统的作业调度程序为用户创建,相应的进程以完成用户作业的要求功能。
② 由父进程创建。例如,在层次结构的系统中,父进程创建子进程以完成并行工作。子进程还可以创建新的子进程,从而整个系统可以形成一个树型结构的家族。
七.进程创建原语
其步骤如下:
① 从系统的PCB表中找出一个空闲的PCB,并指定唯一的标识符PID。
② 为新进程分配资源,包括必要的内存空间、存放程序和数据的工作区等。
③ 根据调用者提供的参数,将新进程PCB初始化。这些参数包括新的进程名、进程优先级、进程状态等。
④ 将新进程加到就绪队列中。
八.进程创建图示
课后作业总结
一.fork()函数
1.fork()函数说明
fork()函数用于从已存在的进程中创建一个新进程,新进程称为子进程,而原进程称为父进程。
使用fork()函数得到的子进程是父进程的一个复制品,从父进程处继承了整个进程的地址空间,而子进程所独有的只有它的进程号、资源使用和计时器等,使用fork()函数,执行速度不是很快。
2.fork()函数语法
①所需头文件
#include<sys/types.h> // 提供类型 pid_t的定义
#include<unistd.h>
②函数原型
pid_t fork(void)
③函数返回值
0:子进程
子进程ID(大于0的整数):父进程
-1:出错
3.函数使用注意点
fork()函数使用一次就创建一个进程,所以若把fork()函数放在了if else判断语句中则要小心,不能多次使用fork()函数
二.exec()函数
1.exec函数族说明
exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。
2.exec函数族语法
①所需头文件
#include<unistd.h>
②函数原型
int execl(const char *path, const char *arg,...)
int execv(const char *path, char *const argv[])
int execle(const char *path, const char *arg,..., char *const envp[])
int execve(const char *path, char *const argv[], char *const envp[])
int execlp(const char *file, const char *arg,...)
int execvp(const char *file, char *const argv[])
③函数返回值
-1:出错
3.exec函数族使用注意点。
在使用exec函数族时,一定要加上错误判断语句。exec很容易执行失败,其中最常见的有
①找不到文件或路径,此时errno被设置为ENOENT;
②数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT;
③没有对应可执行文件的运行权限,此时errno被设置为EACCES;
三.exit()和_exit()函数
1.exit()和_exit()函数说明
exit()和_exit()函数都是用来终止进程的。当程序执行到exit()或_exit()时,进程会无条件地停止剩下的所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。
_exit()函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的各种数据结构:
exit()函数则在这些基础上做了一些包装,在执行退出之前加了若干道工序。
2.exit()和_exit()区别
exit()函数在调用exit系统之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。
3.exit()和_exit()函数族语法
①所需头文件
exit:#include<stdib.h>
_exit:#include<unistd.h>
②函数原型
exit:void exit(int status)
_exit:void _exit(int status)
③函数传入值
status是一个整形的参数,可以利用这个参数传递进程结束时的状态。一般来说,0表示正常结束;其他的数值表示出现了错误,进程非正常结束。
在实际编程时,可以用wait()系统调用接受子进程的返回值,从而针对不同的情况进行不同的处理
四.wait()和waitpid()函数
1.wait()和waitpid()函数说明
wait()函数是用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。如果该父进程没有子进程或者他的子进程已经结束,则wait()就会立即返回。
waitpid()作用和wait()一样,但它并不一定要等待第一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能,也能支持作业控制
2.wait()函数族语法
①所需头文件
#include<sys/types.h>
#include<sys/wait.h>
②函数原型
pid_t wait (int *status)
③函数传入值
这里的status是一个整形指针,是该子进程退出时的状态,status若不为空,则通过它可以获得子进程的结束状态。另外,子进程的结束状态可由Linux中一些特定的宏来测定
④函数返回值
成功:已结束运行的子进程的进程号
失败:-1
3.waitpid()函数语法
①所需头文件
#include<sys/types.h>
#include<sys/wait.h>
②函数原型
pid_t waitpid (pid_t pid , int *status , int options)
③函数传入值
Pid :
pid>0:只等待进程ID等于pid的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有结束, waitpid()就会一直等下去
pid=-1:等待任何一个子进程退出,此时和wait()作用一样
pid=0:等待其组ID等于调用进程的组ID的任一子进程
pid<-1:等待其组ID等于pid的绝对值的任一子进程
status:
这里的status是一个整形指针,是该子进程退出时的状态,status若不为空,则通过它可以获得子进程的结束状态。另外,子进程的结束状态可由Linux中一些特定的宏来测定
options:
WNOHANG:若由pid指定的子进程不立即可用,则waitpid()不阻塞,此时返回值为0
WUNTRACED:若实现某支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态
0:
同wait(),阻塞父进程,等待子进程退出
④函数返回值
正常:已经结束运行的子进程的进程号
使用选项WNOHANG且没有子进程退出:0
调用出错:-1
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 15 | 20/20 | |
第一周 | 200/200 | 2/2 | 1/1 | vi的使用 |
第二周 | 1000/1200 | 3/7 | 7/8 | gcc,gdb,makefile |
第三周 | 500/1700 | 1/8 | 4/12 | U-Boot |
第四周 | 1500/3200 | 1/9 | 6/18 | Linux的内核 |
第五周 | 1200/4400 | 1/10 | 3/21 | 标准I/O的编程,linux下进程相关的基本系统调用 |
参考资料
- 《嵌入式Linux应用程序开发标准教程》