引言(操作系统如何管理)
在冯诺依曼体系结构中,计算机由输入设备、输出设备、运算器、控制器和存储器组成。我们使用计算机的时候,实际就是数据在这些硬件中传递的过程。
硬件的行为由驱动控制,驱动又由更上层的操作系统控制。操作系统又会向上层提供系统调用接口,使用户可以通过系统调用逐级向下控制硬件行为。
操作系统负责与硬件交互,管理所有的软硬件资源,并给用户程序提供一个良好的执行环境,所以操作系统在计算机中起着承上启下的重要作用:
毋庸置疑,因为有着许多程序的执行,我们的计算机才能运转起来,这其中正在执行的程序就被称为进程。而操作系统就是通过管理这些进程来管理软硬件资源的。
在管理这些进程时,其实只需要将这些进程的属性数据组织为一个结构体,再对这些结构体进行管理即可。而不需要管理这些进程对应的大量代码。从操作系统的角度来讲,进程也可以被定义为程序属性结构体和对应代码的集合。
基本概念
前面已经介绍过进程的定义了,对于操作系统,它是程序属性结构体和对应代码的集合:
描述与组织进程
进程的信息被描述在数据控制块PCB(process control block) 中的,即进程属性的集合;
在Linux中,描述进程的结构体为task_struct
,是PCB的一种。其中包含着进程的各种信息。其中大致包括:
- 标示符:描述本进程的唯一标示符,用来区别其他进程;
- 状态:任务状态,退出代码,退出信号等;
- 优先级:相对于其他进程的优先级;
- 程序计数器:程序中即将被执行的下一条指令的地址;
- 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针;
- 上下文数据:进程执行时处理器的寄存器中的数据;
- I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表;
- 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等;
在Linux的源码中,可以查到描述进程的task_struct
结构体(文末):
在Linux中,所有运行在系统中的进程都是使用链表来组织在内核中的。
查看进程
进程的信息可以使用ls
在 /proc
系统文件夹中查看:
也可以使用ps axj
查看全部进程以及一些进程的信息:
进程pid与ppid
pid
是进程的标识符,即进程的编号,在任何时候都是唯一的只有当一进程终止并回收后,该编号才会被重新使用。
ppid
是该进程父进程的pid
,子进程由父进程创建,在子进程结束后要向父进程发出信号。
getpid与getppid
getpid
可以获取该进程的pid
,getppid
可以获取该进程父进程的pid
:
我们可以通过一个死循环,其中不停的打印该进程的pid
与ppid
,来观察:
在进程运行时,我们也可以使用ps axj
在通过管道让grep
筛选后,来查找该进程的一些基本信息:
这里虽然查找到了我们的testproc
进程,但是也多出了一个grep --color=auto testproc
,这是因为grep
要能执行查找的操作,它本身也要是一个进程,自然就会被筛选到并打印出来。
想要不显示这条,只需要-v
过滤掉即可:
task_struct源码
struct task_struct {
/** * 进程状态。 */
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
/** * 进程的基本信息。 */
struct thread_info *thread_info;
atomic_t usage;
unsigned long flags; /* per process flags, defined below */
unsigned long ptrace;
int lock_depth; /* Lock depth */
/** * 进行的动态优先权和静态优先权 */
int prio, static_prio;
/** * 进程所在运行队列。每个优先级对应一个运行队列。 */
struct list_head run_list;
/** * 指向当前运行队列的prio_array_t */
prio_array_t *array;
/** * 进程的平均睡眠时间 */
unsigned long sleep_avg;
/** * timestamp-进程最近插入运行队列的时间。或涉及本进程的最近一次进程切换的时间 * last_ran-最近一次替换本进程的进程切换时间。 */
unsigned long long timestamp, last_ran;
/** * 进程被唤醒时所使用的代码。 * 0:进程处于TASK_RUNNING状态。 * 1:进程处于TASK_INTERRUPTIBLE或者TASK_STOPPED状态,而且正在被系统调用服务例程或内核线程唤醒。 * 2:进程处于TASK_INTERRUPTIBLE或者TASK_STOPPED状态,而且正在被ISR或者可延迟函数唤醒。 * -1:表示从UNINTERRUPTIBLE状态被唤醒 */
int activated;
/** * 进程的调度类型:sched_normal,sched_rr或者sched_fifo */
unsigned long policy;
/** * 能执行进程的CPU的位掩码 */
cpumask_t cpus_allowed;
/** * time_slice-在进程的时间片中,还剩余的时钟节拍数。 * first_time_slice-如果进程肯定不会用完其时间片,就把该标志设置为1. * xie.baoyou注:原文如此,应该是表示任务是否是第一次执行。这样,如果是第一次执行,并且在开始运行 * 的第一个时间片内就运行完毕,那么就将剩余的时间片还给父进程。主要是考虑到有进程 * 会大量的动态创建子进程时,而子进程会立即退出这种情况。如果不还给父进程时间片,会对这种进程不公平。 */
unsigned int time_slice, first_time_slice;
#ifdef CONFIG_SCHEDSTATS
struct sched_info sched_info;
#endif
/** * 通过此链表把所有进程链接到一个双向链表中。 */
struct list_head tasks;
/* * ptrace_list/ptrace_children forms the list of my children * that were stolen by a ptracer. */
/** * 链表的头。该链表包含所有被debugger程序跟踪的P的子进程。 */
struct list_head ptrace_children;
/** * 指向所跟踪进程其实际父进程链表的前一个下一个元素。 */
struct list_head ptrace_list;
/** * mm:指向内存区描述符的指针 */
struct mm_struct *mm, *active_mm;
/* task state */
struct linux_binfmt *binfmt;
long exit_state;
int exit_code, exit_signal;
int pdeath_signal; /* The signal sent when the parent dies */
/* ??? */
unsigned long personality;
/** * 进程发出execve系统调用的次数。 */
unsigned did_exec:1;
/** * 进程PID */
pid_t pid;
/** * 线程组领头线程的PID。 */
pid_t tgid;
/* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->parent->pid) */
/** * 指向创建进程的进程的描述符。 * 如果进程的父进程不再存在,就指向进程1的描述符。 * 因此,如果用户运行一个后台进程而且退出了shell,后台进程就会成为init的子进程。 */
struct task_struct *real_parent; /* real parent process (when being debugged) */
/** * 指向进程的当前父进程。这种进程的子进程终止时,必须向父进程发信号。 * 它的值通常与real_parent一致。 * 但偶尔也可以不同。例如:当另一个进程发出监控进程的ptrace系统调用请求时。 */
struct task_struct *parent; /* parent process */
/* * children/sibling forms the list of my children plus the * tasks I'm ptracing. */
/** * 链表头部。链表指向的所有元素都是进程创建的子进程。 */
struct list_head children; /* list of my children */
/** * 指向兄弟进程链表的下一个元素或前一个元素的指针。 */
struct list_head sibling; /* linkage in my parent's children list */
/** * P所在进程组的领头进程的描述符指针。 */
struct task_struct *group_leader; /* threadgroup leader */
/* PID/PID hash table linkage. */
/** * PID散列表。通过这四个表,可以方便的查找同一线程组的其他线程,同一会话的其他进程等等。 */
struct pid pids[PIDTYPE_MAX];
struct completion *vfork_done; /* for vfork() */
/** * 子进程在用户态的地址。这些用户态地址的值将被设置或者清除。 * 在do_fork时记录这些地址,稍后再设置或者清除它们的值。 */
int __user *set_child_tid; /* CLONE_CHILD_SETTID */
int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */
/** * 进程的实时优先级。 */
unsigned long rt_priority;
/** * 以下三对值用于用户态的定时器。当定时器到期时,会向用户态进程发送信号。 * 每一对值分别存放了两个信号之间以节拍为单位的间隔,及定时器的当前值。 */
unsigned long it_real_value, it_real_incr;
cputime_t it_virt_value, it_virt_incr;
cputime_t it_prof_value, it_prof_incr;
/** * 每个进程的动态定时器。用于实现ITIMER_REAL类型的间隔定时器。 * 由settimer系统调用初始化。 */
struct timer_list real_timer;
/** * 进程在用户态和内核态下经过的节拍数 */
cputime_t utime, stime;
unsigned long nvcsw, nivcsw; /* context switch counts */
struct timespec start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
unsigned long min_flt, maj_flt;
/* process credentials */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
struct group_info *group_info;
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
unsigned keep_capabilities:1;
struct user_struct *user;
#ifdef CONFIG_KEYS
struct key *session_keyring; /* keyring inherited over fork */
struct key *process_keyring; /* keyring private to this process (CLONE_THREAD) */
struct key *thread_keyring; /* keyring private to this thread */
#endif
int oomkilladj; /* OOM kill score adjustment (bit shift). */
char comm[TASK_COMM_LEN];
/* file system info */
/** * 文件系统在查找路径时使用,避免符号链接查找深度过深,导致死循环。 * link_count是__do_follow_link递归调用的层次。 * total_link_count调用__do_follow_link的总次数。 */
int link_count, total_link_count;
/* ipc stuff */
struct sysv_sem sysvsem;
/* CPU-specific state of this task */
struct thread_struct thread;
/* filesystem information */
/** * 与文件系统相关的信息。如当前目录。 */
struct fs_struct *fs;
/* open file information */
/** * 指向文件描述符的指针 */
struct files_struct *files;
/* namespace */
struct namespace *namespace;
/* signal handlers */
/** * 指向进程的信号描述符的指针 */
struct signal_struct *signal;
/** * 指向进程的信号处理程序描述符的指针 */
struct sighand_struct *sighand;
/** * blocked-被阻塞的信号的掩码 * real_blocked-被阻塞信号的临时掩码(由rt_sigtimedwait系统调用使用) */
sigset_t blocked, real_blocked;
/** * 存放私有挂起信号的数据结构 */
struct sigpending pending;
/** * 信号处理程序备用堆栈的地址 */
unsigned long sas_ss_sp;
/** * 信号处理程序备用堆栈的大小 */
size_t sas_ss_size;
/** * 指向一个函数的指针,设备驱动程序使用这个函数阻塞进程的某些信号 */
int (*notifier)(void *priv);
/** * 指向notifier函数可能使用的数据 */
void *notifier_data;
sigset_t *notifier_mask;
void *security;
struct audit_context *audit_context;
/* Thread group tracking */
u32 parent_exec_id;
u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
spinlock_t alloc_lock;
/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */
spinlock_t proc_lock;
/* context-switch lock */
spinlock_t switch_lock;
/* journalling filesystem info */
/** * 当前活动日志操作处理的地址。 */
void *journal_info;
/* VM state */
struct reclaim_state *reclaim_state;
struct dentry *proc_dentry;
struct backing_dev_info *backing_dev_info;
struct io_context *io_context;
unsigned long ptrace_message;
siginfo_t *last_siginfo; /* For ptrace use. */
/* * current io wait handle: wait queue entry to use for io waits * If this thread is processing aio, this points at the waitqueue * inside the currently handled kiocb. It may be NULL (i.e. default * to a stack based synchronous wait) if its doing sync IO. */
wait_queue_t *io_wait;
/* i/o counters(bytes read/written, #syscalls */
u64 rchar, wchar, syscr, syscw;
#if defined(CONFIG_BSD_PROCESS_ACCT)
u64 acct_rss_mem1; /* accumulated rss usage */
u64 acct_vm_mem1; /* accumulated virtual memory usage */
clock_t acct_stimexpd; /* clock_t-converted stime since last update */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *mempolicy;
short il_next;
#endif
};
总结
关于进程的一些基本概念就介绍完了
这篇文章只是进程的开始,后面会更详细的介绍进程的相关知识,欢迎持续关注哦
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出
如果本文对你有帮助,希望一键三连哦
希望与大家共同进步哦
文章评论