从程序到进程

Posted Fu_Lin_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从程序到进程相关的知识,希望对你有一定的参考价值。

前言

本文上接 详述 从代码如何到可执行文件 的过程和解耦 ,以此为基础,阅读本文前确保你熟悉了 详述 从代码如何到可执行文件 的过程和解耦 中提到的概念,本文中的示例程序仍是 上文中的 gemfield.c。代码如下:

 int main(int argc,char *argv[])
 {
      int a = 0;
      char gemfield[32];
      printf("input gemfield’s blog: ");
      scanf("%s",gemfield);
      printf("gemfield’s blog is %s\\n",gemfield);
 }
~             

第一步: 编译。

 gcc gemfield.c -o gemfield

第二步: 运行。

./gemfield &

输出:[1] 5500  input gemfield’s blog: 

第三步: ps命令

ps -e|grep gemfield

输出: 5500 00:00:00 gemfield

表明进程id为 5500 的gemfield进程已经产生了。

第四步: 查看进程gemfield的 cmdline 切换到内核映像 proc目录下:

cd /proc/5500
cat cmdline

输出: ./gemfield

正是程序运行的参数

第五步: 查看进程gemfield的环境参数 切换到内核映像 proc目录下:

cd /proc/5500 
cat environ

输出: XDG_SESSION_ID=20468HOSTNAME=iZwz94wr80gpxpbjwp3i3tZTERM=xtermSHELL=/bin/bashHISTSIZE=100
0SSH_CLIENT=27.38.242.219 23432 
22SSH_TTY=/dev/pts/0USER=rootLS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=
01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30
;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;3
1:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.
t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.
lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;
31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.
ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=0
1;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:
*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.p
cx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=
01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35
:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl
=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35
:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;
36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:MAIL=/var/spool/mail/rootPATH=/usr/local/clang+ll
vm-9.0.0-x86_64-linux-sles11.3/bin:/usr/local/clang+llvm-9.0.0-x86_64-linux-
sles11.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/lib/jvm/java-1.8.0-
openjdk-1.8.0.252.b09-
2.el7_8.x86_64/bin:/root/bin:/root/bin:/usr/local/python3/bin:/opt/rh/devtoolset-
9/root/bin:/root/bin:/root/bin:/usr/local/python3/binPWD=/homeJAVA_HOME=/usr/lib/jvm/java
-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64LANG=en_US.UTF-
8HISTCONTROL=ignoredupsSHLVL=1HOME=/rootLOGNAME=rootCLASSPATH=.:/usr/lib/jvm/java-1.8.0-
openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar:/usr/lib/jvm/java-1.8.0-openjdk-
1.8.0.252.b09-2.el7_8.x86_64/lib/dt.jar:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-
2.el7_8.x86_64/lib/tools.jarSSH_CONNECTION=27.38.242.219 23432 172.18.226.139 
22LESSOPEN=||/usr/bin/lesspipe.sh %sXDG_RUNTIME_DIR=/run/user/0CMAKE_ROOT=/home/cmake-
3.15.4-Linux-x86_64_=./gemfieldOLDPWD=/root[root@iZ

第六步: 查看进程gemfield所使用的文件 切换到内核映像 proc目录下:

cd /proc/5500
ls fd

输出: 0 1 2 说明目前gemfield进程只使用了标准输入、输出、错误。

第七步 查看进程gemfield所使用的io 切换到内核映像 proc目录下:

cd /proc/5500
cat io 
输出:
rchar: 2012
wchar: 25
syscr: 8
syscw: 1
read_bytes: 0
write_bytes: 0
cancelled_write_bytes: 0

第八步:查看进程gemfield的内存映射 切换到内核映像 proc目录下:

cd /proc/5500 
cat maps 
输出
00400000-00401000 r-xp 00000000 fd:01 1060081                          /home/gemfield
00600000-00601000 r--p 00000000 fd:01 1060081                          /home/gemfield
00601000-00602000 rw-p 00001000 fd:01 1060081                          /home/gemfield
7f77c3ff2000-7f77c41b4000 r-xp 00000000 fd:01 658657           /usr/lib64/libc-2.17.so
7f77c41b4000-7f77c43b4000 ---p 001c2000 fd:01 658657           /usr/lib64/libc-2.17.so
7f77c43b4000-7f77c43b8000 r--p 001c2000 fd:01 658657           /usr/lib64/libc-2.17.so
7f77c43b8000-7f77c43ba000 rw-p 001c6000 fd:01 658657           /usr/lib64/libc-2.17.so
7f77c43ba000-7f77c43bf000 rw-p 00000000 00:00 0 
7f77c43bf000-7f77c43e1000 r-xp 00000000 fd:01 658372           /usr/lib64/ld-2.17.so
7f77c45d0000-7f77c45d3000 rw-p 00000000 00:00 0 
7f77c45dd000-7f77c45e0000 rw-p 00000000 00:00 0 
7f77c45e0000-7f77c45e1000 r--p 00021000 fd:01 658372           /usr/lib64/ld-2.17.so
7f77c45e1000-7f77c45e2000 rw-p 00022000 fd:01 658372           /usr/lib64/ld-2.17.so
7f77c45e2000-7f77c45e3000 rw-p 00000000 00:00 0 
7ffc60a37000-7ffc60a58000 rw-p 00000000 00:00 0                        [stack]
7ffc60b7b000-7ffc60b7d000 r-xp 00000000 00:00 0                        [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                [vsyscall]

第九步: 查看进程gemfield的状态 切换到内核映像 proc目录下:

 cd /proc/5500
 cat status 
 输出:
 Name:	gemfield
Umask:	0022
State:	T (stopped)
Tgid:	5500
Ngid:	0
Pid:	5500
PPid:	5286
TracerPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0
FDSize:	256
Groups:	0 
VmPeak:	    4256 kB
VmSize:	    4216 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	     356 kB
VmRSS:	     356 kB
RssAnon:	      76 kB
RssFile:	     280 kB
RssShmem:	       0 kB
VmData:	      56 kB
VmStk:	     132 kB
VmExe:	       4 kB
VmLib:	    1936 kB
VmPTE:	      28 kB
VmSwap:	       0 kB
Threads:	1
SigQ:	0/15076
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000000000
SigIgn:	0000000000000000
SigCgt:	0000000000000000
CapInh:	0000000000000000
CapPrm:	0000001fffffffff
CapEff:	0000001fffffffff
CapBnd:	0000001fffffffff
CapAmb:	0000000000000000
Seccomp:	0
Speculation_Store_Bypass:	vulnerable
Cpus_allowed:	1
Cpus_allowed_list:	0
Mems_allowed:	
00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000
,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000
0,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000
00,00000000,00000001
Mems_allowed_list:	0
voluntary_ctxt_switches:	1
nonvoluntary_ctxt_switches:	0

第十步 查看进程 gemfield 的调度信息 切换到内核映像 proc目录下:

cd /proc/5500
cat sched 

当查看这个文件时, 定义在 kernel/sched_debug.c 中的 proc_sched_show_task() 函数将会被调用。
输出

gemfield (5500, #threads: 1)
-------------------------------------------------------------------
se.exec_start                                :    8106596666.907056
se.vruntime                                  :     118833600.764166
se.sum_exec_runtime                          :             0.696281
se.nr_migrations                             :                    0
nr_switches                                  :                    1
nr_voluntary_switches                        :                    1
nr_involuntary_switches                      :                    0
se.load.weight                               :                 1024
policy                                       :                    0
prio                                         :                  120
clock-delta                                  :                   41
mm->numa_scan_seq                            :                    0
numa_migrations, 0
numa_faults_memory, 0, 0, 1, 0, -1
numa_faults_memory, 1, 0, 0, 0, -1

第十一步 粗略描述

我们还可以从 /proc/mount* 查看进程gemifeld使用的分区信息 /proc/net/* 查看进程gemfield使用的网络设备情况

步骤先放一放,那究竟什么是进程呢?

进程就是程序在计算机系统上运行时的一个实例,管理着计算机系统分配给它的各种资源,如:

  1. 程序的可运行机器码的一个在内存中的映像;
  2. 分配到的内存,包括可运行代码、特定于进程的数据(输入、输出)、堆、栈(用于保存运行时运输中途产生的数据);
  3. 分配给该进程的资源的操作系统描述子,诸如文件描述子(Unix 术语)或文件句柄(Windows)、数据源和数据终端;
  4. 安全特性,诸如进程拥有者和进程的权限集(可以容许的操作);
  5. 处理器状态(上下文),如寄存器内容等。当进程正在运行时,状态通常存储在cpu的寄存器,其他情况则在内存中。

先在清楚为什么本文要花费大片费来介绍/proc/pid下面的内容了吧,在建立了一个直观的印象后,我们深入到Linux的内核中来看看进程,在内核中,进程是由一个叫作 task_struct 的结构体来维护的。

第十二步 分析 task_struct 结构体。

让我们先行猜测下这个结构体应该有什么内容吧,从前十一步得知,它应该有下面的内容:
1、进程的id什么的;
2、进程的状态,在等待,运行,或死锁;
3、进程的家族,像吸血鬼家族一样,有吸血鬼祖先、后代、还有僵尸;
4、进程的内存映射;
5、时间片信息,以便cpu调度;
6、使用的文件描述符;
7、处理器寄存器的上下文。

那我们来实际看看这个结构体,在 /include/linux/sched.h 中有 task_struct 的定义:

struct task_struct 
{ 
volatile long state;//运行时状态,-1不可运行,0可运行,>0已停止  
unsigned long flags; //flage是当前的进程标志,有正在被创建、正准备退出、被fork出但是没有执行exec、由于其他进程发送相关信号而被杀死  
int sigpending; //进程上是否有待处理的信号 
mm_segment_t addr_limit;  
volatile long need_resched;//调度标志,表示该进程是否需要重新调度. 
int lock_depth; //锁深度 
long nice; //进程的基本时间片 
unsigned long policy;//调度策略有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER struct mm_struct *mm; //进程内存信息 
int processor; 
unsigned long cpus_runnable, cpus_allowed;//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新 
struct list_head run_list; //指向运行队列的指针 
unsigned long sleep_time; //进程的睡眠时间 
struct task_struct *next_task, *prev_task;//用于将系统中所有的进程连成一个双向循环链表, 其根是init_task 
struct mm_struct *active_mm; 
struct list_head local_pages;//指向本地页面  
unsigned int allocation_order, nr_local_pages; 
struct linux_binfmt *binfmt;//进程所运行的可执行文件的格式 
int exit_code, exit_signal; 
int pdeath_signal; //父进程终止是向子进程发送的信号 
unsigned long personality; 
int did_exec:1; 
pid_t pid; //进程号 
pid_t pgrp; //进程组标识,表示进程所属的进程组 
pid_t tty_old_pgrp; //进程控制终端所在的组标识 
pid_t session; //进程的会话标识 
pid_t tgid;//进程组号 
int leader; //表示进程是否为会话主管 
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr; 
struct list_head thread_group; //线程链表 
struct task_struct *pidhash_next; //用于将进程链入HASH表 
struct task_struct **pidhash_pprev; wait_queue_head_t wait_chldexit; //供wait4()使用
struct completion *vfork_done; //供vfork() 使用 
unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值 
unsigned long it_real_value, it_prof_value, it_virt_value; 
unsigned long it_real_incr, it_prof_incr, it_virt_value; 
struct timer_list real_timer; //指向实时定时器的指针 
struct tms times; //记录进程消耗的时间 
unsigned long start_time; //进程创建的时间 
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];  
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;//参考下面 
int swappable:1; //表示进程的虚拟地址空间是否允许换出 
uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; //记录进程在多少个用户组中 
gid_t groups[NGROUPS]; //记录进程所在的组 
kernel_cap_t cap_effective, cap_inheritable, cap_permitted; 
int keep_capabilities:1; 
struct user_struct *user; 
struct rlimit rlim[RLIM_NLIMITS]; //与进程相关的资源限制信息 
unsigned short used_math; //是否使用FPU 
char comm[16]; //进程正在运行的可执行文件名 
int link_count, total_link_count; 
struct tty_struct *tty;//NULL if no tty  
unsigned int locks; 
struct sem_undo *semundo; //进程在信号灯上的所有undo操作 
struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作 
struct thread_struct thread;//存放cpu寄存器的上下文,参考下面。 
struct fs_struct *fs; //文件系统信息 
struct files_struct *files;//打开文件信息 
spinlock_t sigmask_lock; //信号处理函数 
struct signal_struct *sig; //信号处理函数 
sigset_t blocked; //进程当前要阻塞的信号,每个信号对应一位 
struct sigpending pending; //进程上是否有待处理的信号 
unsigned long sas_ss_sp; 
size_t sas_ss_size; 
int (*notifier)(void *priv); 
void *notifier_data; 
sigset_t *notifier_mask; 
u32 parent_exec_id; 
u32 self_exec_id; 
spinlock_t alloc_lock; 
void *journal_info以上是关于从程序到进程的主要内容,如果未能解决你的问题,请参考以下文章

使用 NavController 从片段导航到另一个片段

线程学习知识点总结

使用导航从一个片段导航到另一个片段后,防止后按工作

如何将字符串数据从活动发送到片段?

从控制台应用程序启动 WPF 窗口

Android:如何在选项卡内从一个片段导航到另一个片段? [关闭]