Linux C编程详解:进程原理分析、文件描述符和文件记录表、文件句柄和文件原理

一、引言

文件操作是Linux C编程中其中的一项核心技术,实际上也相当重要,这里并不是说狭义上的那种文件操作,它也非常有助于理解和学习Linux系统。为什么这样说呢?因为在Unix/Linux的世界中,一切皆文件!这种简单的设计其实非常有利于编程设计,让你可以集中在数据结构和算法的设计上,例如网络Socket和外部设备都作为文件处理,省去了很多繁琐的概念。

Linux一切皆文件

本文主要根据Linux文件实质原理展开讨论,其中也涉及到Linux的进程(Process)原理分析,Linux系统的进程对象持有一个文件记录表,该文件记录表保存一个该进程可处理的文件句柄数组,那么什么是文件描述符呢?它其实就是一个索引,就是文件句柄数组的下标索引,引用维基百科:

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIXLinux这样的操作系统。

维基百科

这里讨论的都是广泛意义上的文件,你不需要强加想象将什么当做文件,下面会有具体的讲解。

二、进程管理与原理分析

什么是Linux进程?Linux进程是系统执行的一个程序以及该程序所管理的资源,在内存中执行一个简单的helloworld应用程序,这是一个进程,通常我们会说一个应用程序就是一个进程,严格来说并不正确,因为一个应用程序可以有多个子进程。

linux进程管理

那么如何才能理解Linux的进程概念呢?从编程设计的角度来看,它应该被设计为一个结构体,C语言基本就是指针和结构体了,而结构体是最基本的数据结构单元。

我们一起来分析一下Linux内核源码,这里用的是linux2.6.0,立即吃瓜:https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/

Linux进程的源码文件为/include/linux/sched.h,取感兴趣的数据成员,进程结构体对象为:

struct task_struct {
	// 进程可运行状态,-1不可执行,0可执行,>0可终止
	volatile long state;	
// 线程信息,在这里你可以到线程是从属于进程的
	struct thread_info *thread_info;
	// 进程描述符使用计数,表示该进程是否在被使用
atomic_t usage;
// 内核对每个进程的状态标识,如PF_FORKNOEXEC表示进程刚创建但未执行
	unsigned long flags;
	
// 进程的调度策略,SCHED_FIFO和SCHED_RR为实时进程,SCHED_OTHER未分时进程
	unsigned long policy;

	// 进程标识符,代表一个进程
	pid_t pid;
	// 进程组标识符,代表该进程所属进程组别
pid_t __pgrp;	
	
	// 以下表示进程间的从属关系
	struct task_struct *real_parent; /* real parent process (when being debugged) */
	struct task_struct *parent;	/* parent process */
	struct list_head children;	/* list of my children */
	struct list_head sibling;	/* linkage in my parent's children list */
	struct task_struct *group_leader;	/* threadgroup leader */

// 进程的CPU状态信息
	struct thread_struct thread;
// 文件系统信息
	struct fs_struct *fs;
// 该进程打开的文件信息
	struct files_struct *files;
// namespace
	struct namespace *namespace;
};

以上代码,取了进程的一些基本信息,如进程ID,然后你会发现线程是属于进程的,但是Linux的进程和线程有什么区别呢?首先是从属关系,然后进程和线程的本质区别是,进程占据单独的内存空间,包括全局静态区、文字常量区、堆区、栈区和代码区,而线程是单独占据一个栈区,所以多线程是处理多个栈区包括主线程栈区。

接着还有进程之间的关系,子进程和父进程等,这里就表明了一个应用程序是可以有多个进程的。

最后红色加粗的struct
files_struct *files这个结构体变量表示该进程所打开的文件信息,该文件在/include/linux/file.h中:

// 打开文件表结构
struct files_struct {
	// 使用该表的进程数
        atomic_t count;
        spinlock_t file_lock;     /* Protects all the below members.  Nests inside tsk->alloc_lock */
        int max_fds;
        int max_fdset;
        int next_fd;
        // 当前文件描述符数组的指针,下面fd_array的指针
        struct file ** fd;     
        fd_set *close_on_exec;
        fd_set *open_fds;
        fd_set close_on_exec_init;
        fd_set open_fds_init;
        // 一个文件结构体指针数组,表示文件描述符数组,保存打开的文件
        struct file * fd_array[NR_OPEN_DEFAULT];
};

每个进程都有这个文件描述符表,我们操作的文件对象都在fd_array中,文件描述符就是这个数组的索引,发现这样称呼也不甚准确,如果称索引为文件描述符,那么文件对象称指针会好点,不过看懂了就不用纠结了。你可以发现函数read和write,或者socket中的send和recv等,这些都是使用文件描述符来进行操作的,如果不明白其中的原理,OOP编程入门的开发者可能会想:为什么传个int就可以进行网络传输了?

三、Linux文件原理分析

这里要注意,该文件描述符数组在进程创建的时候默按顺序认初始化3个文件句柄:stdin,stdout,stderr,用于标准输入,标准输出,标准错误输出。

Linux进程启动默认创建的文件句柄

到这里你可以知道,对文件操作的基本元素有:文件句柄/指针,和文件描述符(索引),fdopen()函数可以将文件描述符转为文件指针,你可以想到的实现就是,根据索引从数组中获取到一个文件指针。

接着,我们需要扩展文件的概念,在linux中一切皆文件,这会很大程度上影响我们自己的编程想法,那么文件是什么样子的呢?看内核/include/linux/fs.h文件结构体的代码:

struct file {
	struct list_head	f_list;
	// 目录结构
struct dentry		*f_dentry;
	struct vfsmount         *f_vfsmnt;
	// 文件操作,文件相关的函数操作,使用一个结构体封装函数指针
	struct file_operations	*f_op;
	atomic_t		f_count;
	// 文件标志
	unsigned int 		f_flags;
	mode_t			f_mode;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	unsigned int		f_uid, f_gid;
	int			f_error;
	struct file_ra_state	f_ra;

	unsigned long		f_version;
	void			*f_security;

	/* needed for tty driver, and maybe others */
	void			*private_data;

	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;
	spinlock_t		f_ep_lock;
};

struct file和FILE是一样的,都代表一个文件,说了那么多,到底在Linux中具体哪些都是文件呢?

  • 普通文件File,如一个txt文本,一个视频文件,一个字节码文件;
  • 终端I/O,如字符终端;
  • 管道Pipe,如普通管道和FIFO;
  • 网络通信资源,套接字socket;
  • 设备Device,网络设备,硬盘块设备,块设备。

以上都是我们常见的Linux文件,进一步我们可以将文件看作一种资源池,我们主要是对资源池中的数据进行读取,但是我们不说文件都是二进制表示所以说一切都是文件,基本是废话,二进制太广泛了,主要还是对数据结构和算法的理解,上面说的都是数据结构,实际相应的头文件中定义了相关操作。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?