课程学习总结报告

linux操作系统上的一切操作都是进程对文件的操作

上面那句话的关键词是进程文件操作,下面我们对这两个主题进行总结

一、进程

CPU上要么在执行一个进程,要么在发生进程上下文切换或中断上下文切换。进程执行时,我们关注对文件的操作,这将会在下一节进行讨论;本节我们讨论上下文切换。

1.进程函数调用栈
  1. 每个用户态进程都有两个函数调用栈,一个用户态函数调用栈和一个内核态函数调用栈;
  2. 中断没有独立的函数调用栈,中断会占有当前被中断的进程的内核栈来作为自己的函数调用栈。
2.中断

中断发生有两种情形。一种是进程处于用户态时被中断,另一种是在内核态中发生的中断嵌套。
第一种情形,需要将用户进程的上下文按照结构体 pt_regs 的格式保存在用户的内核栈中。步骤如下:

  1. 硬件自动保存 ss, esp, eflags, cs 和 eip 这5个值;
  2. 保存硬件出错码,如果没有硬件出错码的话会保存数值0;
  3. 进入中断服务程序后,由 SAVE_ALL 指令保存 pt_regs 结构体中其余的值;
  4. 中断处理完后,SAVE_ALL 指令保存的值由 RESTORE_ALL指令恢复,硬件保存的值由硬件恢复。

第二种情形的处理与第一种情形基本类似,唯一的区别是硬件无需保存 ss 和 esp 的值。第一种情形中断前后发生了栈的切换,所以要保存着两个值;而第二种情形中断前后一直处于内核栈中,所以无需保存这两个值。

3.进程调度和切换

进程进行切换时,保存当前进程现场的工作同中断的第一种情形。

内核会维护一个进程队列和一个可运行进程队列,每次调度都会从可运行队列中选择。

可运行队列又分为 active 队列和 expired 队列。当 active 队列不为空时,expired 队列中的进程没有机会被调度;当 active 队列为空时,active 队列和 expired 队列会互换。

linux按进程优先级对进程进行调度,进程优先级会动态变化。但是进程又分为实时进程和普通进程,实时进程的优先级永远高于普通进程,而且实时进程和普通进程之间隔着一道墙,两者不会互相转化。

实时进程被切换出去时不会进入过期队列。普通进程分交互式进程和非交互式普通进程,非交互普通进程被切换出去时会进入 expired 队列,交互式进程切换出去时,一般会进入 active 队列,但是当 expired 队列中的最高优先级比当前进程高,或者 expired 队列中的最古老进程等待时间超出某个阈值时,当前交互式进程会进入 expired 队列。

进程切换的实际总结如下:

  1. 进程状态发生变化时
    处于运行态下的进程要等待某种资源
    运行态下的进程转入僵死态
    等待态的进程被唤醒后加入到可运行队列中
    运行态转入暂停态
    暂停态转入可运行态
  2. 当前进程时间片用完时
  3. 进程从系统调用返回到用户态时
  4. 中断处理后,进程返回到用户态时

二、文件操作

linux对文件的操作进行了统一抽象,向上提供open, read, write, close等统一接口,这些统一接口会去找与特定文件相关的,存储在file_opration中的相关操作函数完成动作。
下面我们从相关结构体定义开始来进行深入分析。

相关结构体:

  1. struct file_oprations: file_oprations结构体中都是一些函数指针。由于不同文件系统、不同设备文件(如块设备文件、字符设备文件)的读、写等操作都不同,所以每种类型的文件都要有相应的特定读写函数,这些函数的入口地址(函数指针变量)就存储在结构体file_oprations中。linux提供的统一接口最终调用的就是存储在file_oprations中的相应函数。
  2. struct inode: 内核用一个inode结构体变量来表示一个存储在磁盘上的文件。
  3. struct file: 一个file结构体变量表示一个在内存中打开的文件,系统会维护由所有file结构体变量串成的链表,称为系统打开文件表;进程会维护一个file*数组,称为进程打开文件表。一个文件只要在磁盘上,就会有一个inode变量与之对应;但是该文件只有被打开时,才会有一个file变量与之对应,而且当该文件关闭时,相应的file变量就会被释放。

从进程控制块 task_struct 开始,相应结构体变量的嵌套关系总结如下图,图中花括号右边的结构体变量嵌套在花括号左边的结构体变量中。

进程对文件的操作可以总结如下:

  1. 进程通过进程控制块中的结构体变量 fs 可以在文件系统中定位到文件的位置;
  2. 进程调用 open() 接口打开一个文件。打开文件会创建一个 file 结构体变量,并初始化 file 结构体中的 file_oprations 变量,也就是对文件的操作函数。创建完后会在数组 fd_array 中寻找下标最小的空槽,令其指向新创建的 file 变量,并返回这个槽的下标,也就是我们通常说的整型变量文件描述符(fd);
  3. 进程调用 read() 接口对文件进行读操作。read() 调用 sys_read(),sys_read()根据文件描述符参数 fd 在进程控制块的 fd_array 中找到表示打开文件的 file 变量,并调用 file 变量中的 file_oprations 变量里的相应读函数对文件进行读操作。写操作类似,不在赘述;
  4. 进程调用 close() 接口关闭文件,并释放 file 变量。

下面给出一些代码片段,实际上就是上面终结的那张图的代码形式。

struct task_struct {
	...
	/* Filesystem information: */
	struct fs_struct		*fs;

	/* Open file information: */
	struct files_struct		*files;
	...
};
/*
 * Open file table structure
 */
struct files_struct {
	...
	struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
struct file {
	...
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;
	...
} __randomize_layout
  __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iopoll)(struct kiocb *kiocb, bool spin);
	int (*iterate) (struct file *, struct dir_context *);
	int (*iterate_shared) (struct file *, struct dir_context *);
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
	ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
			loff_t, size_t, unsigned int);
	loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
				   struct file *file_out, loff_t pos_out,
				   loff_t len, unsigned int remap_flags);
	int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
原文地址:https://www.cnblogs.com/-zyq/p/13270103.html