struct files_struct和struct fdtable

本文详细解析了Linux系统中文件描述符的管理机制,重点介绍了structfiles_struct和structfdtable的初始化过程及其在进程间的关系。并通过代码示例阐述了如何进行文件描述符的扩充。
struct files_struct和struct fdtable的初始化
我们先来列出struct files_struct和struct fdtable的定义,为了讨论方面,下面的定义中略去了很少一部分的锁成员,下面的代码均摘自linux 2.6.24。
struct files_struct在<include/linux/fdtable.h>中定义如下:
struct files_struct {
    atomic_t count;
    struct fdtable *fdt;
    struct fdtable  fdtab;
   
    int next_fd;
    struct embedded_fd_set close_on_exec_init;
    struct embedded_fd_set open_fds_init;
    struct file * fd_array[NR_OPEN_DEFAULT];
};


struct fdtable定义如下:
struct fdtable {
    unsigned int max_fds;
    struct file ** fd; /* 当前fd_array */
    fd_set *close_on_exec;
    fd_set *open_fds;
    struct rcu_head rcu;
    struct files_struct *free_files;
    struct fdtable *next;
};
在struct files_struct中包括一个struct fdtable变量实例和一个struct fdtable类型指针,而struct files_struct中的成员变量close_on_exec,open_fds,fd又分别指向struct files_struct中的成员close_on_exec_init,open_fds_init和fd_array。即一个结构中嵌套了另一个结构,被嵌套结构中的成员又反过来引用了属主的成员,这样看起来有些重复和让人迷惑。
在files_struct结构的初始化时,能更清晰的看出这种重复。如内核第一个进程(即进程init)的files_struct静态初始化:
struct files_struct init_files = {
    .count      = ATOMIC_INIT(1),
    .fdt        = &init_files.fdtab,
    .fdtab      = {
        .max_fds        = NR_OPEN_DEFAULT,
        .fd               = &init_files.fd_array[0],
        .close_on_exec  = (fd_set *)&init_files.close_on_exec_init,
        .open_fds       = (fd_set *)&init_files.open_fds_init,
        .rcu            = RCU_HEAD_INIT,
    },
    .file_lock  = __SPIN_LOCK_UNLOCKED(init_task.file_lock),
};

下图展示了上述代码初始化后各结构成员之间的关系:

可以看出,files_struct的成员fdt在这里初始化为其内嵌的struct fdtable变量实例(成员fdtab)地址,内嵌的struct fdtable实例变量的fd,close_on_exec,open_fds又指向属主的相应变量。那么这个fdtab存在的必要意义是什么呢(在此处看不出来,即使去除了该变量,也足以表达files_struct)?
我们知道,linux系统中,一个进程打开的文件数是有初步限制的,即其文件描述符数初始时有最大化定量,即一个进程一般只能打开NR_OPEN_DEFAULT个文件,该值在32位机上为32个,在64位机上为64个。上面的files_struct这种初始化正是体现了进程初步所能打开文件的内核结构描述。这里需要说明的是,这不仅仅限于类似上面的静态初始化,当init进程fork一个子进程时,也是如此(此时是files_struct是动态分配的)。
见下面的代码:
static struct files_struct *alloc_files(void)
{
    struct files_struct *newf;
    struct fdtable *fdt;
    newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
    if (!newf)
        goto out;
    atomic_set(&newf->count, 1);
    spin_lock_init(&newf->file_lock);
    newf->next_fd = 0;
    fdt = &newf->fdtab;
    fdt->max_fds = NR_OPEN_DEFAULT;
    fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;
    fdt->open_fds = (fd_set *)&newf->open_fds_init;
    fdt->fd = &newf->fd_array[0];
    INIT_RCU_HEAD(&fdt->rcu);
    fdt->next = NULL;
    rcu_assign_pointer(newf->fdt, fdt);
out:
    return newf;
}

函数alloc_files的被调用流程是:
do_fork------>copy_process
    |------>dup_task_struct------>alloc_task_struct
    |------>copy_files------>dup_fd------>alloc_files
即先调用alloc_task_struct分配一个task_struct结构实例,然后使用alloc_files来分配并初始化files_struct变量实例。
上面的 files_struct初始化和上面例子中的静态初始化并无本质差别:
语句fdt = &newf->fdtab取出newf的struct fdtable实例变量fdtab的指针,然后通过rcu_assign_pointer函数将其赋值给newf->fdt,那么newf->fdt还是指向其自身中的struct fdtable实例变量,fdt的成员close_on_exec、open_fds和fd也是如此。

struct files_struct扩充
当进程打开的文件数超过NR_OPEN_DEFAULT时,就要在对上面的已初始化的struct files_struct(更准确的说是其所包含的数据对象)进行扩充。
上面对于struct files_struct和struct fdtable这种互相引用及其冲突讨论了很多,其实内核中之所以还使用了struct fdtable,根本目的是为了应对这种扩充。
当进行struct files_struct扩充时,会分配一个新的struct fdtable,为了叙述方便,下面该变量用指针用nfdt来表示。另外还分配了满足扩充要求的fd数组(即struct file数组),以及与fd相对的bitmap描述close_on_exec,open_fds的存储空间。
然后将新分配的close_on_exec,open_fds,fd空间指针赋值给nfdt->close_on_exec,nfdt->open_fds和nfdt->fd。注意,这里的close_on_exec,open_fds和上面初始化时close_on_exec_init,open_fds_init的差别:
close_on_exec,open_fds的最大值按bit来说为__FDSET_LONGS,实际值为1024位,即文件描述符的最大数为1024个。但它们也是按需分配,并和file数组的大小一致,分配的实际值同时赋值给nfdt->max_fds。
分配并初始化新的struct fdtable变量后,原先指向fdtab的struct files_struct指针成员fdt,会调整为指向新分配的struct fdtable变量。这时,struct files_struct实例变量中就包含两个struct fdtable存储区:一个是其自身的,一个新分配的,用fdt指向。
执行完上面的操作后,其关系如下图:

注意上图中同颜色的存储区,代表的是两者内容是一致的。即执行完上述的操作后,还要将的结构存储区的内容拷贝到新的存储区,这包括files_struct自身所包含的close_on_exec,open_fds,fd到新分配的close_on_exec,open_fds,fd的拷贝。
执行完上述拷贝之后,就要释放旧的struct fdtable,但这里并不执行执行该项释放操作(下面的讨论中会说明何时才进行释放操作)。

struct files_struct扩充使用内核源码中的expand_files来实现,expand_files会调用expand_fdtable:
static int expand_fdtable(struct files_struct *files, int nr)
{
    struct fdtable *new_fdt, *cur_fdt;
    new_fdt = alloc_fdtable(nr);      //分配了一个fdtable
    cur_fdt = files_fdtable(files);   //files->fdt
    if (nr >= cur_fdt->max_fds) {
        /* Continue as planned */
        copy_fdtable(new_fdt, cur_fdt);   //拷贝了其中的3个变量:fd,open_fds,close_on_exec
        rcu_assign_pointer(files->fdt, new_fdt);  //将新分配的fdtable赋值给files的fdt
          if (cur_fdt->max_fds > NR_OPEN_DEFAULT)  //注意它第一次初始化为NR_OPEN_DEFAULT
            free_fdtable(cur_fdt);
     }
    return 1;
}

上面代码中包括:
先使用函数alloc_fdtable分配一个struct fdtable变量,以及close_on_exec,open_fds,file数组
因为我们这里讨论的是扩充,所以接下来的if判断为真,那么执行旧的数据区到新分配数据区的拷贝,即函数copy_fdtable,拷贝操作上面已经进行过讨论,在此不再赘述。这里重点说明旧的数据区的释放:
if (cur_fdt->max_fds > NR_OPEN_DEFAULT)  
    free_fdtable(cur_fdt);
此处的cur_fdt是旧的struct fdtable变量指针,它的文件描述符数初始化为NR_OPEN_DEFAULT,显然if判断为假,并不执行free_fdtable来进行旧的struct fdtable释放。那么何时才进行释放呢?
我们下面来分析free_fdtable函数,该函数会引起free_fdtable_rcu的调用,简化的free_fdtable_rcu函数代码如下:
void free_fdtable_rcu(struct rcu_head *rcu)
{
    struct fdtable *fdt = container_of(rcu, struct fdtable, rcu);
    struct fdtable_defer *fddef;
 
    if (fdt->max_fds <= NR_OPEN_DEFAULT) {
          kmem_cache_free(files_cachep,
                container_of(fdt, struct files_struct, fdtab)); //这里释放的是files_struct本身
        return;
    }
    //上面是最初的分配,下面的是扩展分配(本次是再次扩展),注意扩展分配时fd,open_fds都是动态分配的,
    //所以可以用free释放掉。
    //另外,这里并没有显式的释放close_on_exec指向的内存
    if (fdt->max_fds <= (PAGE_SIZE / sizeof(struct file *))) {
        kfree(fdt->fd);
        kfree(fdt->open_fds);
        kfree(fdt);
    } else {
            .....
    }
}
上面的代码中,开头的if (fdt->max_fds <= NR_OPEN_DEFAULT)判断语句为真,此时是struct files_struct没有进行扩充,而需要释放files_struct本身时,对free_fdtable_rcu的调用。这时,会执行kmem_cache_free,以container_of(fdt, struct files_struct, fdtab)为需要释放存储空间的指针参数,来完成释放。

下一个if语句块就是扩充后struct files_struct的释放,注意此时暗含的条件是fdt->max_fds大于NR_OPEN_DEFAULT,即进行扩充后的释放,但我们前面讨论的expand_fdtable不会执行到free_fdtable_rcu(第一次扩充),而此处的fdt->max_fds大于NR_OPEN_DEFAULT表示的是二次扩充,即在我们前面讨论的扩充基础上的再次扩充,此时就需要释放上面讨论的新分配的struct fdtable,以及open_fds和file array。
这里并没有看到释放close_on_exec,其实在函数alloc_fdtable中分配open_fds和close_on_exec时,是将二者统一分配的,所以上面的kfree(fdt->open_fds)语句用来释放这个统一分配的存储区,close_on_exec的分配空间一同释放。
最后列出空间分配函数alloc_fdtable的代码:
static struct fdtable * alloc_fdtable(unsigned int nr)
{
    struct fdtable *fdt;
    char *data;

    data = alloc_fdmem(nr * sizeof(struct file *));  //这里可以看出fdt->max_fds是最大的文件描述
                                                      //符, 即分配了多少个file结构体
    fdt->fd = (struct file **)data;

    //下面乘2的意思是open_fds + close_on_exec
    data
= alloc_fdmem(max_t(unsigned int,
                 2 * nr / BITS_PER_BYTE, L1_CACHE_BYTES)); 
    fdt->open_fds = (fd_set *)data;
    data += nr / BITS_PER_BYTE;
    fdt->close_on_exec = (fd_set *)data;
    INIT_RCU_HEAD(&fdt->rcu);
    fdt->next = NULL;
    return fdt;
     ...
}






struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* * For reasons of header soup (see current_thread_info()), this * must be the first element of task_struct. */ struct thread_info thread_info; #endif /* -1 unrunnable, 0 runnable, >0 stopped: */ volatile long state; /* * This begins the randomizable portion of task_struct. Only * scheduling-critical items should be added above here. */ randomized_struct_fields_start void *stack; atomic_t usage; /* Per task flags (PF_*), defined further below: */ unsigned int flags; unsigned int ptrace; #ifdef CONFIG_SMP struct llist_node wake_entry; int on_cpu; #ifdef CONFIG_THREAD_INFO_IN_TASK /* Current CPU: */ unsigned int cpu; #endif unsigned int wakee_flips; unsigned long wakee_flip_decay_ts; struct task_struct *last_wakee; /* * recent_used_cpu is initially set as the last CPU used by a task * that wakes affine another task. Waker/wakee relationships can * push tasks around a CPU where each wakeup moves to the next one. * Tracking a recently used CPU allows a quick search for a recently * used CPU that may be idle. */ int recent_used_cpu; int wake_cpu; #endif int on_rq; int prio; int static_prio; int normal_prio; unsigned int rt_priority; const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; #ifdef CONFIG_CGROUP_SCHED struct task_group *sched_task_group; #endif struct sched_dl_entity dl; #ifdef CONFIG_PREEMPT_NOTIFIERS /* List of struct preempt_notifier: */ struct hlist_head preempt_notifiers; #endif #ifdef CONFIG_BLK_DEV_IO_TRACE unsigned int btrace_seq; #endif unsigned int policy; int nr_cpus_allowed; cpumask_t cpus_allowed; #ifdef CONFIG_PREEMPT_RCU int rcu_read_lock_nesting; union rcu_special rcu_read_unlock_special; struct list_head rcu_node_entry; struct rcu_node *rcu_blocked_node; #endif /* #ifdef CONFIG_PREEMPT_RCU */ #ifdef CONFIG_TASKS_RCU unsigned long rcu_tasks_nvcsw; u8 rcu_tasks_holdout; u8 rcu_tasks_idx; int rcu_tasks_idle_cpu; struct list_head rcu_tasks_holdout_list; #endif /* #ifdef CONFIG_TASKS_RCU */ struct sched_info sched_info; struct list_head tasks; #ifdef CONFIG_SMP struct plist_node pushable_tasks; struct rb_node pushable_dl_tasks; #endif struct mm_struct *mm; struct mm_struct *active_mm; /* Per-thread vma caching: */ struct vmacache vmacache; int exit_state; int exit_code; int exit_signal; /* The signal sent when the parent dies: */ int pdeath_signal; /* JOBCTL_*, siglock protected: */ unsigned long jobctl; /* Used for emulating ABI behavior of previous Linux versions: */ unsigned int personality; /* Scheduler bits, serialized by scheduler locks: */ unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; unsigned sched_migrated:1; unsigned sched_remote_wakeup:1; /* Force alignment to the next boundary: */ unsigned :0; /* Unserialized, strictly 'current' */ /* Bit to tell LSMs we're in execve(): */ unsigned in_execve:1; unsigned in_iowait:1; #ifndef TIF_RESTORE_SIGMASK unsigned restore_sigmask:1; #endif #ifdef CONFIG_MEMCG unsigned in_user_fault:1; #ifdef CONFIG_MEMCG_KMEM unsigned memcg_kmem_skip_account:1; #endif #endif #ifdef CONFIG_COMPAT_BRK unsigned brk_randomized:1; #endif #ifdef CONFIG_CGROUPS /* disallow userland-initiated cgroup migration */ unsigned no_cgroup_migration:1; /* task is frozen/stopped (used by the cgroup freezer) */ unsigned frozen:1; #endif #ifdef CONFIG_BLK_CGROUP /* to be used once the psi infrastructure lands upstream. */ unsigned use_memdelay:1; #endif unsigned long atomic_flags; /* Flags requiring atomic access. */ struct restart_block restart_block; pid_t pid; pid_t tgid; #ifdef CONFIG_SECURITY_KYSEC_AUTHENTICATION void *kysec_fop; #endif #ifdef CONFIG_STACKPROTECTOR /* Canary value for the -fstack-protector GCC feature: */ unsigned long stack_canary; #endif /* * Pointers to the (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->real_parent->pid) */ /* Real parent process: */ struct task_struct __rcu *real_parent; /* Recipient of SIGCHLD, wait4() reports: */ struct task_struct __rcu *parent; /* * Children/sibling form the list of natural children: */ struct list_head children; struct list_head sibling; struct task_struct *group_leader; /* * 'ptraced' is the list of tasks this task is using ptrace() on. * * This includes both natural children and PTRACE_ATTACH targets. * 'ptrace_entry' is this task's link on the p->parent->ptraced list. */ struct list_head ptraced; struct list_head ptrace_entry; /* PID/PID hash table linkage. */ struct pid *thread_pid; struct hlist_node pid_links[PIDTYPE_MAX]; struct list_head thread_group; struct list_head thread_node; struct completion *vfork_done; /* CLONE_CHILD_SETTID: */ int __user *set_child_tid; /* CLONE_CHILD_CLEARTID: */ int __user *clear_child_tid; u64 utime; u64 stime; #ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME u64 utimescaled; u64 stimescaled; #endif u64 gtime; struct prev_cputime prev_cputime; #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN struct vtime vtime; #endif #ifdef CONFIG_NO_HZ_FULL atomic_t tick_dep_mask; #endif /* Context switch counts: */ unsigned long nvcsw; unsigned long nivcsw; /* Monotonic time in nsecs: */ u64 start_time; /* Boot based time in nsecs: */ u64 real_start_time; /* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */ unsigned long min_flt; unsigned long maj_flt; #ifdef CONFIG_POSIX_TIMERS struct task_cputime cputime_expires; struct list_head cpu_timers[3]; #endif /* Process credentials: */ /* Tracer's credentials at attach: */ const struct cred __rcu *ptracer_cred; /* Objective and real subjective task credentials (COW): */ const struct cred __rcu *real_cred; /* Effective (overridable) subjective task credentials (COW): */ const struct cred __rcu *cred; /* * executable name, excluding path. * * - normally initialized setup_new_exec() * - access it with [gs]et_task_comm() * - lock it with task_lock() */ char comm[TASK_COMM_LEN]; struct nameidata *nameidata; #ifdef CONFIG_SYSVIPC struct sysv_sem sysvsem; struct sysv_shm sysvshm; #endif #ifdef CONFIG_DETECT_HUNG_TASK unsigned long last_switch_count; unsigned long last_switch_time; #endif /* Filesystem information: */ struct fs_struct *fs; /* Open file information: */ struct files_struct *files; #ifdef CONFIG_IO_URING struct io_uring_task *io_uring; #endif /* Namespaces: */ struct nsproxy *nsproxy; /* Signal handlers: */ struct signal_struct *signal; struct sighand_struct *sighand; sigset_t blocked; sigset_t real_blocked; /* Restored if set_restore_sigmask() was used: */ sigset_t saved_sigmask; struct sigpending pending; unsigned long sas_ss_sp; size_t sas_ss_size; unsigned int sas_ss_flags; struct callback_head *task_works; struct audit_context *audit_context; #ifdef CONFIG_AUDITSYSCALL kuid_t loginuid; unsigned int sessionid; #endif struct seccomp seccomp; /* Thread group tracking: */ u64 parent_exec_id; u64 self_exec_id; /* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */ spinlock_t alloc_lock; /* Protection of the PI data structures: */ raw_spinlock_t pi_lock; struct wake_q_node wake_q; #ifdef CONFIG_RT_MUTEXES /* PI waiters blocked on a rt_mutex held by this task: */ struct rb_root_cached pi_waiters; /* Updated under owner's pi_lock and rq lock */ struct task_struct *pi_top_task; /* Deadlock detection and priority inheritance handling: */ struct rt_mutex_waiter *pi_blocked_on; #endif #ifdef CONFIG_DEBUG_MUTEXES /* Mutex deadlock detection: */ struct mutex_waiter *blocked_on; #endif #ifdef CONFIG_TRACE_IRQFLAGS unsigned int irq_events; unsigned long hardirq_enable_ip; unsigned long hardirq_disable_ip; unsigned int hardirq_enable_event; unsigned int hardirq_disable_event; int hardirqs_enabled; int hardirq_context; u64 hardirq_chain_key; unsigned long softirq_disable_ip; unsigned long softirq_enable_ip; unsigned int softirq_disable_event; unsigned int softirq_enable_event; int softirqs_enabled; int softirq_context; #endif #ifdef CONFIG_LOCKDEP # define MAX_LOCK_DEPTH 48UL u64 curr_chain_key; int lockdep_depth; unsigned int lockdep_recursion; struct held_lock held_locks[MAX_LOCK_DEPTH]; #endif #ifdef CONFIG_UBSAN unsigned int in_ubsan; #endif /* Journalling filesystem info: */ void *journal_info; /* Stacked block device info: */ struct bio_list *bio_list; #ifdef CONFIG_BLOCK /* Stack plugging: */ struct blk_plug *plug; #endif /* VM state: */ struct reclaim_state *reclaim_state; struct backing_dev_info *backing_dev_info; struct io_context *io_context; /* Ptrace state: */ unsigned long ptrace_message; siginfo_t *last_siginfo; struct task_io_accounting ioac; #ifdef CONFIG_TASK_XACCT /* Accumulated RSS usage: */ u64 acct_rss_mem1; /* Accumulated virtual memory usage: */ u64 acct_vm_mem1; /* stime + utime since last update: */ u64 acct_timexpd; #endif #ifdef CONFIG_CPUSETS /* Protected by ->alloc_lock: */ nodemask_t mems_allowed; /* Seqence number to catch updates: */ seqcount_t mems_allowed_seq; int cpuset_mem_spread_rotor; int cpuset_slab_spread_rotor; #endif #ifdef CONFIG_CGROUPS /* Control Group info protected by css_set_lock: */ struct css_set __rcu *cgroups; /* cg_list protected by css_set_lock and tsk->alloc_lock: */ struct list_head cg_list; #endif #if defined(CONFIG_RESCTRL) || defined(CONFIG_INTEL_RDT) u32 closid; u32 rmid; #endif #ifdef CONFIG_FUTEX struct robust_list_head __user *robust_list; #ifdef CONFIG_COMPAT struct compat_robust_list_head __user *compat_robust_list; #endif struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; struct mutex futex_exit_mutex; unsigned int futex_state; #endif #ifdef CONFIG_PERF_EVENTS struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts]; struct mutex perf_event_mutex; struct list_head perf_event_list; #endif #ifdef CONFIG_DEBUG_PREEMPT unsigned long preempt_disable_ip; #endif #ifdef CONFIG_NUMA /* Protected by alloc_lock: */ struct mempolicy *mempolicy; short il_prev; short pref_node_fork; #endif #ifdef CONFIG_NUMA_BALANCING int numa_scan_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; int numa_preferred_nid; unsigned long numa_migrate_retry; /* Migration stamp: */ u64 node_stamp; u64 last_task_numa_placement; u64 last_sum_exec_runtime; struct callback_head numa_work; /* * This pointer is only modified for current in syscall and * pagefault context (and for tasks being destroyed), so it can be read * from any of the following contexts: * - RCU read-side critical section * - current->numa_group from everywhere * - task's runqueue locked, task not running */ struct numa_group __rcu *numa_group; /* * numa_faults is an array split into four regions: * faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer * in this precise order. * * faults_memory: Exponential decaying average of faults on a per-node * basis. Scheduling placement decisions are made based on these * counts. The values remain static for the duration of a PTE scan. * faults_cpu: Track the nodes the process was running on when a NUMA * hinting fault was incurred. * faults_memory_buffer and faults_cpu_buffer: Record faults per node * during the current scan window. When the scan completes, the counts * in faults_memory and faults_cpu decay and these values are copied. */ unsigned long *numa_faults; unsigned long total_numa_faults; /* * numa_faults_locality tracks if faults recorded during the last * scan window were remote/local or failed to migrate. The task scan * period is adapted based on the locality of the faults with different * weights depending on whether they were shared or private faults */ unsigned long numa_faults_locality[3]; unsigned long numa_pages_migrated; #endif /* CONFIG_NUMA_BALANCING */ #ifdef CONFIG_RSEQ struct rseq __user *rseq; u32 rseq_len; u32 rseq_sig; /* * RmW on rseq_event_mask must be performed atomically * with respect to preemption. */ unsigned long rseq_event_mask; #endif struct tlbflush_unmap_batch tlb_ubc; #ifndef __GENKSYMS__ union { refcount_t rcu_users; struct rcu_head rcu; }; #else struct rcu_head rcu; #endif /* Cache last used pipe for splice(): */ struct pipe_inode_info *splice_pipe; struct page_frag task_frag; #ifdef CONFIG_TASK_DELAY_ACCT struct task_delay_info *delays; #endif #ifdef CONFIG_FAULT_INJECTION int make_it_fail; unsigned int fail_nth; #endif /* * When (nr_dirtied >= nr_dirtied_pause), it's time to call * balance_dirty_pages() for a dirty throttling pause: */ int nr_dirtied; int nr_dirtied_pause; /* Start of a write-and-pause period: */ unsigned long dirty_paused_when; #ifdef CONFIG_LATENCYTOP int latency_record_count; struct latency_record latency_record[LT_SAVECOUNT]; #endif /* * Time slack values; these are used to round up poll() and * select() etc timeout values. These are in nanoseconds. */ u64 timer_slack_ns; u64 default_timer_slack_ns; #ifdef CONFIG_KASAN unsigned int kasan_depth; #endif #ifdef CONFIG_KCSAN struct kcsan_ctx kcsan_ctx; #endif #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* Index of current stored address in ret_stack: */ int curr_ret_stack; int curr_ret_depth; /* Stack of return addresses for return function tracing: */ struct ftrace_ret_stack *ret_stack; /* Timestamp for last schedule: */ unsigned long long ftrace_timestamp; /* * Number of functions that haven't been traced * because of depth overrun: */ atomic_t trace_overrun; /* Pause tracing: */ atomic_t tracing_graph_pause; #endif #ifdef CONFIG_TRACING /* State flags for use by tracers: */ unsigned long trace; /* Bitmask and counter of trace recursion: */ unsigned long trace_recursion; #endif /* CONFIG_TRACING */ #ifdef CONFIG_KCOV /* Coverage collection mode enabled for this task (0 if disabled): */ unsigned int kcov_mode; /* Size of the kcov_area: */ unsigned int kcov_size; /* Buffer for coverage collection: */ void *kcov_area; /* KCOV descriptor wired with this task or NULL: */ struct kcov *kcov; #endif #ifdef CONFIG_MEMCG struct mem_cgroup *memcg_in_oom; gfp_t memcg_oom_gfp_mask; int memcg_oom_order; /* Number of pages to reclaim on returning to userland: */ unsigned int memcg_nr_pages_over_high; /* Used by memcontrol for targeted memcg charge: */ struct mem_cgroup *active_memcg; #endif #ifdef CONFIG_BLK_CGROUP struct request_queue *throttle_queue; #endif #ifdef CONFIG_UPROBES struct uprobe_task *utask; #endif #if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE) unsigned int sequential_io; unsigned int sequential_io_avg; #endif #ifdef CONFIG_DEBUG_ATOMIC_SLEEP unsigned long task_state_change; #endif int pagefault_disabled; #ifdef CONFIG_MMU struct task_struct *oom_reaper_list; #endif #ifdef CONFIG_VMAP_STACK struct vm_struct *stack_vm_area; #endif #ifdef CONFIG_THREAD_INFO_IN_TASK /* A live task holds one reference: */ atomic_t stack_refcount; #endif #ifdef CONFIG_LIVEPATCH int patch_state; #endif #ifdef CONFIG_SECURITY /* Used by LSM modules for access restriction: */ void *security; #endif #ifdef CONFIG_BPF_SYSCALL /* Used for BPF run context */ struct bpf_run_ctx *bpf_ctx; #endif /* * New fields for task_struct should be added above here, so that * they are included in the randomized portion of task_struct. */ randomized_struct_fields_end unsigned int task_iowait_count; unsigned int task_io_quirk; unsigned long iowait_flip_decay_ts; unsigned long task_io_record_ts; struct cpumask preferred_io_cpumask; #ifdef CONFIG_QOS_SCHED_SMT_EXPELLER long qos_level; #endif unsigned long interactive_app; unsigned long task_section_flag; cpumask_t *prefer_cpus; const cpumask_t *select_cpus; #ifdef CONFIG_QOS_SCHED_SMART_GRID struct sched_grid_qos *grid_qos; #endif KABI_RESERVE(1) KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4) #ifdef CONFIG_PID_RESERVE KY_KABI_USE(5, fork_pid_t fork_pid_union) #else KABI_RESERVE(5) #endif KY_KABI_USE(6, unsigned long last_pipe_waketime); KABI_RESERVE(7) KABI_RESERVE(8) KY_KABI_REPLACE_SPLIT(long rh_reserved[64], int ky_kabi_dummy, /* 0 */ /* Use this one first */ ) /* CPU-specific state of this task: */ struct thread_struct thread; /* * WARNING: on x86, 'thread_struct' contains a variable-sized * structure. It *MUST* be at the end of 'task_struct'. * * Do not put anything below here! */ };
08-15
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值