⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux.txt

📁 一份精简的linux内核源代码
💻 TXT
📖 第 1 页 / 共 3 页
字号:
当一个进程打开一个字符设备文件时(open.c,163),其实质是将该设备赋予此进程(将该设备的tty_struct中的进程组设为当前进程),以后此进程可操作该设备(如某进程打开了显示器键盘设备文件,就等于进程获得了显示器键盘的使用权,之后进程即可通过直接读写显示器键盘设备文件来进行显示即输入操作),具体(open.c,167)。

一个需要进行控制台操作的进程的文件表内的第1,第2项为终端设备文件(显示器键盘,即传说中的标准输入输出句柄)。当要进行输入或显示操作时,直接读写终端设备文件即可。首先调用sys_write文件写系统调用,而sys_write则根据文件类型而调用rw_char从而调用tty_write显示数据(见19)。

一个tty_struct对应一个显示器、一个键盘、一个字符设备文件及一个进程组,当用户按键时键盘中断将键值存入tty_struct的读队列(这和进程无关),当某一进程欲读入字符时才会到tty_struct的读队列中去区;当某一进程要显示信息时将信息存入tty_struct的写队列,并调用终端显示con_write将数据显示在屏幕上(实质是将欲显示的数据写入显存中)。
读:
tty_read:读取辅助队列中的字符并存到用户数据缓冲区中并返回实际的读取数,当队列为空或不满一行或数据态少时进程都将挂起,整个操作有一个时间上限,超过上限时无论是否读取了指定数目的字符都将返回(若有信号需处理也将立即返回)(由进程调用)。
copy_to_cooked:每当键盘收到一个字符时就会将字符存入read_q中并调用copy_to_cooked,该函数将read_q中的字符转换为规范格式并存入辅助队列中(若回现标志置位则同事将字符存入write_q队列中并显示之),结束时唤醒等待辅助队列的进程(注:此处纯属中断处理和具体进程无关)(注:每当在键盘上按下一个按键就会执行一遍上述过程)
写:
tty_write:将用户缓冲区中的数据写入write_q中,并调用con_write将数据显示在屏幕上,若写队列满则等待(并不挂起,仅调度其它进程运行)
con_write:将write_q中的数据经过转换显示到屏幕上(实质是将欲显示的数据写入显存中)

16.串行终端
有4种情况都会引起串口中断:1,modem状态发生变化;2,线路发生变化;3,收到中断输入的字符;4,发送保持寄存器被打开(该寄存器只要为空就会发出中断)。串口中断处理程序首先取中断表示寄存器中的值以判断中断源,对于前两种情况只需读取对应状态寄存器的值即可复位该中断,对于第三种情况对缓冲器执行读操作即可复位该中断(具体中断程序类似控制台,仅仅将读入字符存入read_q中并调用copy_to_cooked进行格式转换),对于第四种情况,中断处理程序会从write_q中取出一个字符并发送置发送保持寄存器中,随后终端会自动从中读出该字符并显示之,而此时为空的保持寄存器又会发出中断,直到write_q为空时程序才会关闭发送保持寄存器并唤醒等待该队列的进程。

17.缓冲
若要读入块设备上的一个块:调用bread,而bread将调用getblk新分配一块缓冲块。若新分配的缓冲块即为所要读入的块则直接返回该块;否则调用读设备块操作ll_rw_block从设备上读入指定的块并存入新分配的缓冲块中。在从设备上读块的时候,进程将挂起待完成读操作后再唤醒(以指定的缓冲块是否上锁为进程挂起和唤醒的信号,因为ll_rw_block在进行读操作时首先将会锁定缓冲块,待操作完成后再解锁缓冲块)。
ll_rw_block:将读写块设备操作制作成一个请求项并挂入相应设备的请求队列中。若请求队列为空则触发设备的驱动程序执行。
getblk:(分配一个新的缓冲块),首先从hash表中去搜索,看待读入的块是否在缓冲区中,若在则直接返回,否则进入一般的缓冲块申请流程。
一般的缓冲块申请流程:找空闲块(b_count=0),若没有则将进程挂起(挂在空闲缓冲区等待队列中,当执行完某一设备块读写操作时会唤醒该队列);若找到空闲块(若有多个则取权重最小的)但该块被锁定,则挂起进程(挂在该缓冲块的等待对流中,并且唤醒后若该块又被别的进程改过将重新执行getblk);若找到空闲块但该块为脏则挂起当前进程(挂在该缓冲块的等待对流中,并且唤醒后若该块又被别的进程改过将重新执行getblk)并执行写盘操作;当通过上述检测后,最后再判断一次该块是否已在缓冲块中(有可能在执行上述检测过程中,该块被其它进程读入缓冲区);至此才找到了一块不葬,空闲,未被锁定的块,然后设置该块并返回。
引用计数为0的块也可能是脏的或被锁定的。

18.i节点
若要操作文件首先需把文件的i节点读入内存。i节点操作(p450)
从设备上读入i节点置内存i节点表的流程:首先搜索内存i节点表,若其中不含有欲读入的i节点,则从设备上读入指定的i节点;若其中含有欲读入的i节点则需判断其是否是另一文件系统的安装点,若不是则返回该i节点,若是则将欲读入的i节点重设为该文件系统的根i节点。
当某一i节点的引用次数为0时(内存i节点结构中)该i节点表项可另做它用,不过此时若其标志i_dirty为1则需要先将其写入设备。
一般调用iget都会将所指定的i节点的引用计数加1,故使用完时。需调用iput将其释放。

19.文件系统
一个文件系统由其超级块与之对应;一个设备由一个设备文件i节点与之对应(其中存放着设备号)。文件系统无名,一个设备对应一个文件系统。
加载一个文件系统:由设备名获得设备文件i节点从而获得设备号,由安装到的目录名获得安装到目录的i节点,读入超级块置内存超级块数组中,读入位图置缓冲块中,将文件系统的安装到目录设为上面刚才获得的i节点(即超级块的i_mount=inode)
根据路径名搜索指定文件:即找到路径名末端文件的i节点。在当前进程的root字段中存放着搜多的起始i节点,于是直接在此i节点中搜索子目录项,随后由子目录项获得对应的i节点,重复此过程直至找到指定的i节点为止。
在指定目录i节点中搜索子目录的一般情况:首先读入一块该i节点的数据块(i_zone[],其中存放着目录项)置缓冲区,然后在其中匹配子目录名,若没找到,则释放该缓冲块并再读入一块。若找到了子目录项则根据设备号及目录项中的i节点号读入该i节点。
特殊情况:在某一目录下搜索子目录的i节点,若该子目录为文件系统安装点则直接返回该文件系统的根i节点;在某一文件系统的根目录下搜索".."目录项,则直接返回该文件系统安装到i节点的".."目录项。
文件路径中某一文件系统的根i节点与文件系统安装到的i节点可视为重合,如:
设备1根目录\设备1目录\设备2安装到目录(设备2根目录)\设备2目录
只有超级用户才有新建文件的权限。
i节点中的i_nlink对于目录i节点而言为其中目录项的个数。
一个文件可能同时有几个目录指向它。

新建的文件只有一个i节点,其数据块是空的;新建一个目录,其数据块中将含有".",".."两项。
删除一个目录时,不允许删除自己或某个文件系统的根目录或非空的目录,具体步骤是直接从父目录中删除对应的目录项并置i_nlink为0,待执行iput时实际删除;删除一个文件时,直接从父目录中删除对应的目录项,但仅仅将i_nlink-1(因为可能有多个目录指向一个文件)待i_nlink为0时才会实际删除。

文件读写:若要写入整块数据则可直接申请一缓冲块;若写入的数据不满一块考虑到原数据则必须从设备上读入该块。
管道读写:p503
创建管道:包括新建一个管道i节点、令当前进程的文件表中的两项指向该i节点(一项设为读,一项设为写),将进程文件表中那两项的索引值赋予fildes(返回值)。

sys_read(p513):读当前进程文件表中指定文件的数据,若该文件为管道文件则调有read_pipe;若文件为字符设备文件(代表显示器键盘等)则调用rw_char;若文件为块设备文件则调用block_read,若为普通的目录或文件则调用file_read。(以上读写函数都在fs目录下)
rw_char:根据不同设备号来调用相应的字符设备读写函数(如终端读写tty_read,tty_write,见15)
使用文件前需打开文件:获得指定路径名的i节点,在内存文件表中申请一项使该项指向该i节点,使当前进程的文件表中的一项指向刚才申请到的文件表项,并返回该项的索引(句柄)。
申请内存文件表项时的依据是引用计数为0,出现内存文件表项引用计数>1是由于创建子进程时增加了文件引用计数。

20.用户权限
一个文件的权限由其i节点中的i_mode及uid,guid三个字段来处理。
i_mode的6-8位代表其宿主用户对该文件的操作权限(即进程的uid=i节点的uid,说明当前进程为该文件的宿主用户)
i_mode的3-5位代表其组用户对该文件的操作权限(即进程的guid=i节点的guid,说明当前进程与该文件的宿主用户同组)
i_mode的0-2位代表其它用户对该文件的操作权限(即同时不满足上述两情况)
注:超级用户拥有所有文件的所有访问权限

21.exective
ls -l/home/john/
当用户在提示符下键入上述命令时,shell进程会创建一个新进程并在其中执行/bin/ls命令,在加载/bin/ls执行文件时,三个命令行参数ls、-l、/home/john/将被新进程继承下来(被保存于进程用户堆栈的顶端,当程序需要明确用到环境变量时这些环境变量也会被存放到用户堆栈的顶端,去掉这些空间再往左将是一个用来指向上述参数及环境变量的指针表,再往左即为进程的用户太堆栈顶,p524认真看)(以上为新程序在进程逻辑空间中的初始化)
execve(文件名,命令行参数,环境变量):某一进程调用execve时,首先根据文件名获得其i节点,然后读入该文件的第一个数据块(其中存放着文件执行头结构),判断该头结构:
(A)若是可执行文件(linux0.11只能执行可执行文件,即无重定位信息)则将命令行参数及环境变量参数存入新申请的内存页(空,可执行文件命令行参数,可执行文件环境变量);
(B)若是脚本文件(脚本文件以#!开头后接解释程序名,如:#! erc\sh,脚本文件需由解释程序来执行)则放回脚本文件i节点,并读入解释文件的i节点,随后将命令行参数及环境变量参数存入新申请的内存页(空,解释程序名,解释程序参数,脚本名,脚本参数,脚本环境变量)

然后重设进程PCB,具体如下:
executable字段设为执行文件的i节点
复位所有信号处理句柄
根据close_on_exec位图关闭指定文件
释放原来的页表及页,
根据执行文件的执行头结构重设ldt
将内存页中128k的环境变量和命令行参数放入进程逻辑空间末端(即此时进程的逻辑空间的分布为:0 空 命令行参数 环境变量 64m)
重设进程用户态堆栈顶(64m-环境变量和命令行参数的空间)
使进程返回用户态时返回到新的执行文件开始处

pcb中的executable字段为进程执行文件的i节点
a.out文件格式(p539)
某进程在内存中的分布图(p554)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -