• 课程学习总结报告


    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;
    
  • 相关阅读:
    【iCore3 双核心板_FPGA】实验二十六:SDRAM读写测试实验
    【iCore3 双核心板_FPGA】实验二十五:NIOS II之UART串口通信实验
    【iCore3 双核心板_FPGA】实验二十四:Niosii——SDRAM读写实验
    【iCore3 双核心板】iCore3封装库及使用说明V1.0
    【iCore3 双核心板_FPGA】实验二十三:使用JTAG UART终端打印信息
    【iCore3 双核心板_FPGA】实验二十二:Niosii——固化程序到 EPCS 里
    【7集iCore3基础视频】7-5 iTool2驱动安装
    【iCore3 双核心板_FPGA】实验二十:基于FIFO的ARM+FPGA数据存取实验
    【iCore3 双核心板_FPGA】实验二十一:Niosii——基于内部RAM建立第一个软核
    【7集iCore3基础视频】7-4 iCore3连接示意图
  • 原文地址:https://www.cnblogs.com/-zyq/p/13270103.html
Copyright © 2020-2023  润新知