学习Linux系统编程-Day(5)

1.shell中的每个程序都会在一个新进程内被发起。几乎所有主流的shell都会提供任务控制的特性,也就是支持用户同时执行和操纵多条任务和管道。且管道内(一条命令行)所有进程都会被放置到一个新的进程组中,组中的每个进程都有一个相同的进程组ID,此ID为进程组组长(process group leader)的进程ID。内核可以对组中所有进程执行同样的操作

2.会话的概念建立在进程组之上,正如进程组中保管着多个进程,一个会话包含多个进程组(任务),会话中的所有进程都有着相同的会话ID。创建会话的进程叫做会话首进程,它的进程ID就被作为会话ID。会话最终要与一个控制终端相关联,控制终端建立于会话首进程打开终端设备时,打开控制终端时会话首进程也将成为终端的控制进程。关闭控制终端之后,控制进程(会话首进程)会收到SIGHUP信号而结束。

3.进程涉及到两种类型的时间,即真实时间进程时间,前者指从一精确的时间节点出发开始测得的时间(经常取日历时间或者进程启动时间),后者则指进程从启动开始占用的CPU时间总量。后者还可以继续分为内核时间用户时间,内核时间指完成此进程的代码在内核态下(往往是系统调用)执行所用的时间,用户时间则指代码在用户态下(常规的程序代码)执行的时间。

4.Linux也提供了对/proc文件系统的支持,这是一个虚拟文件系统,提供了指向内核数据结构的接口,在此下可以看到和改变系统属性,也可以根据进程ID来查看进程信息。比如,就可以用cat /proc/cpuinfo来查看CPU信息:
在这里插入图片描述
5.系统调用是受控的内核入口, 系统调用有以下三点重要的特点:
(1)系统调用需要从用户态切换到核心态
(2)系统调用是一个明确的功能集合,每一个系统调用在系统内部都有一个唯一的数字来表示,这个数字对程序员是透明的
(3)每个系统调用都伴随着一套参数,对内核与用户空间的数据交换加以规范。
对于系统调用,一定要检查调用是否成功

6.对于Linux系统编程,我们往往是在C语言应用程序中调用由C封装好的外壳(wrapper)函数来进行系统功能调用。下面是一个典型系统调用过程(x86-32):
(1)在应用程序代码中调用C语言函数库的外壳函数
(2)函数调用中传递给外壳函数的参数暂存到堆栈,随后被外壳函数复制到寄存器
(3)外壳函数将系统调用编号复制到一个特殊的寄存器(%eax)中。
(4)外壳函数执行一条中断类型号为0x80的中断指令,使CPU从用户态转为内核态,并开始执行0x80指令所指向的中断服务程序(现在特殊的sysenter陷入指令效率更高)。
(5)在0x80的响应过程中(执行0x80中断服务程序中),内核将调用system_call()例程来处理中断,系统调用的真正执行就在此期间发生
(6)系统调用例程执行并将反映执行结果的数值返回给外壳函数,外壳函数根据此设置errno的值,再返回一个经过处理的整形值到应用程序来表明系统调用是否成功。
根据上述过程可以看到,系统调用虽然方便,但是过程还是很繁琐的,开销也不容忽视。

7.对6中(5)的system_call()例程工作内容进行详细说明,这也是真正完成系统调用的过程:
(1)在内核栈中保存当前寄存器的所有值
(2)检查系统调用编号的有效性
(3)根据编号对内核数据结构sys_call_table进行索引来找到对应的系统调用例程,期间还要进行参数有效性,地址有效性的检查。检查无误后开始执行系统调用函数,调用结果返回值返回到system_call()系统调用。
(4)恢复寄存器的值,并将返回值置于内核栈中
(5)返回外壳函数,CPU重新从内核态转为用户态

猜你喜欢

转载自blog.csdn.net/zzy980511/article/details/115069832