从程序到进程
Posted Fu_Lin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从程序到进程相关的知识,希望对你有一定的参考价值。
文章目录
- 前言
- 第一步: 编译。
- 第二步: 运行。
- 第三步: ps命令
- 第四步: 查看进程gemfield的 cmdline 切换到内核映像 proc目录下:
- 第五步: 查看进程gemfield的环境参数 切换到内核映像 proc目录下:
- 第六步: 查看进程gemfield所使用的文件 切换到内核映像 proc目录下:
- 第七步 查看进程gemfield所使用的io 切换到内核映像 proc目录下:
- 第八步:查看进程gemfield的内存映射 切换到内核映像 proc目录下:
- 第九步: 查看进程gemfield的状态 切换到内核映像 proc目录下:
- 第十步 查看进程 gemfield 的调度信息 切换到内核映像 proc目录下:
- 第十一步 粗略描述
- 第十二步 分析 **task_struct** 结构体。
- 第十三步 从ELF启航——fork
- 第十四步:exec() 函数家族
- 第十五步:do_execve()系统调用
- 第十六步 load_binary的工作 load_binary 函数的实施按照以下步骤
- 第十七步、回到用户态
前言
本文上接 详述 从代码如何到可执行文件 的过程和解耦 ,以此为基础,阅读本文前确保你熟悉了 详述 从代码如何到可执行文件 的过程和解耦 中提到的概念,本文中的示例程序仍是 上文中的 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使用的网络设备情况
步骤先放一放,那究竟什么是进程呢?
进程就是程序在计算机系统上运行时的一个实例,管理着计算机系统分配给它的各种资源,如:
- 程序的可运行机器码的一个在内存中的映像;
- 分配到的内存,包括可运行代码、特定于进程的数据(输入、输出)、堆、栈(用于保存运行时运输中途产生的数据);
- 分配给该进程的资源的操作系统描述子,诸如文件描述子(Unix 术语)或文件句柄(Windows)、数据源和数据终端;
- 安全特性,诸如进程拥有者和进程的权限集(可以容许的操作);
- 处理器状态(上下文),如寄存器内容等。当进程正在运行时,状态通常存储在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以上是关于从程序到进程的主要内容,如果未能解决你的问题,请参考以下文章