• Linux进程【1】进程概念(超详解哦)


    引言(操作系统如何管理)

    冯诺依曼体系结构中,计算机由输入设备、输出设备、运算器、控制器和存储器组成。我们使用计算机的时候,实际就是数据在这些硬件中传递的过程。
    硬件的行为由驱动控制,驱动又由更上层的操作系统控制。操作系统又会向上层提供系统调用接口,使用户可以通过系统调用逐级向下控制硬件行为。
    操作系统负责与硬件交互,管理所有的软硬件资源,并给用户程序提供一个良好的执行环境,所以操作系统在计算机中起着承上启下的重要作用:
    在这里插入图片描述
    毋庸置疑,因为有着许多程序的执行,我们的计算机才能运转起来,这其中正在执行的程序就被称为进程。而操作系统就是通过管理这些进程来管理软硬件资源的

    在管理这些进程时,其实只需要将这些进程的属性数据组织为一个结构体,再对这些结构体进行管理即可。而不需要管理这些进程对应的大量代码。从操作系统的角度来讲,进程也可以被定义为程序属性结构体和对应代码的集合

    基本概念

    前面已经介绍过进程的定义了,对于操作系统,它是程序属性结构体和对应代码的集合:

    描述与组织进程

    进程的信息被描述在数据控制块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可以获取该进程的pidgetppid可以获取该进程父进程的pid

    我们可以通过一个死循环,其中不停的打印该进程的pidppid,来观察:
    在这里插入图片描述
    在这里插入图片描述

    在进程运行时,我们也可以使用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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303

    总结

    关于进程的一些基本概念就介绍完了
    这篇文章只是进程的开始,后面会更详细的介绍进程的相关知识,欢迎持续关注哦

    如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

    如果本文对你有帮助,希望一键三连哦

    希望与大家共同进步哦

  • 相关阅读:
    读博准备路线图
    你一定要知道的四个程序员接外包的网站,悄悄把技术变现!
    Springboot龙龙汽车配件网站88000计算机毕业设计-课程设计-期末作业-毕设程序代做
    杰理之在所有模式下打开喊话增加 mic 自动 mute【篇】
    Docker镜像的导入导出
    新能源车补能;小鹏、蔚来各有千秋
    -星号梯形-
    Ansible自动化运维、ZABBIX监控
    戴尔科技集团通过多云数据保护和安全创新增强网络弹性
    游戏里的猎头
  • 原文地址:https://blog.csdn.net/weixin_73450183/article/details/132655857