在上一篇文章中详细介绍了task_struct结构体内的常见成员,然后我们就来看一下具体内容。每个进程都把它的信息放在 task_struct 这个数据结构中,task_struct 包含了这些内容:
标示符 : 描述本进程的唯一标示符,用来区别其他进程。
状态 : 任务状态,退出代码,退出信号等。
优先级 : 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/ O状态信息:包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
进程标识符初探

进程id(PID)。每个进程都有非负的整形表示唯一的进程ID。好比如我们的身份证一样,每个人的身份证号是唯一的.因为进程ID标示符总是唯一的,常将其用来做其他标示符的一部分以保证其唯一性,进程ID(PID)是无法在用户层修改的。调用getpid()函数可以获得当前进程的PID,此函数没有参数,如果执行成功返回当前进程的PID,失败返回-1,出错原因存储于errno。

函数定义:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

例子1:打印自己的进程ID(PID)。

 1 #include <unistd.h>
2 #include <stdio.h>
3 int main()
4 {
5 pid_t pid; //pid_t 其实是int
6 pid = getpid();
7 printf("the current program's pid is %d\n",pid);
8 while(1);
9 return 0;
10 }

运行程序,然后使用“ps -u”( ps u 以用户为主的格式来显示程序状况。)命令查看对照如下:Linux系统中,PID为0 的进程通常是调度进程,常常被称为交换进程,也是第一个系统进程。第一个用户进程是init进程,其PID为1。

父进程id(PPID)。当前进程的父进程id。任何进程(除init进程)都是由另一个进程创建,该进程称为被创建进程的父进程,被创建的进程称为子进程,父进程ID无法在用户层修改。父进程的进程ID即为子进程的父进程ID(PPID)。用户可以通过调用getppid()函数来获得当前进程的父进程ID(PPID)。此函数没有参数,如果执行成功返回当前进程的父进程ID(PPID),失败返回-1,出错原因存储于errno。
函数定义:
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);

例子2:打印自己的父进程PPID。

 1 #include <unistd.h>
2 #include <stdio.h>
3 int main()
4 {
5 pid_t ppid; //pid_t 其实是int
6 ppid = getppid();
7 printf("the current program's ppid is %d\n",ppid);
8 while(1);
9 return 0;
10 }

同样,运行程序,然后使用“ps -u”命令查看对照如下:我们注意到父进程为bash。事实上任何在命令行里运行的进程的父进程都是shell。

进程位置
1. 进程内存映像
Linux下C程序生成主要由四个步骤组成: 预编译、编译、汇编、链接。编译器gcc经过 预编译、编译、汇编3个步骤将源程序文件转换成目标文件。 如果程序有多个目标文件或程序中使用了库函数,则编译器还需要将所有目标文件及所需的库文件链接起来,最后生成可执行程序。当程序执行时,操作系统将可执行程序复制到内存中,程序转为为进程通常 需要以下步骤:
   内核将程序读入内存,为程序分配内存空间;
   内核为该进程保存PID及相应的状态信息,把进程放到运行队列中等待执行。程序转化为进程后就可被操作系统的调度程序执行了。 进程的内存映像是指内核在内存中如何存放可执行程序文件。在将程序转化为进程的过程中,操作系统将可执行程序从硬盘复制到内存中, 其布局如下:例子3 C地址空间测试代码如下:

 1 #include <stdio.h>
2 #include <stdlib.h>
3 int g_val = 100;
4 void test()
5 {
6 int a = 10;
7 int b = 10;
8 printf("test stack1 address : 0x%x\n", &a);
9 printf("test stack2 address : 0x%x\n", &b);
10 }
11 void (*fp)();
12 int main()
13 {
14 int a = 10;
15 int *heap = malloc(sizeof(int));
16 fp = test;
17 printf("code address : 0x%x\n", fp);
18 printf("data address : 0x%x\n", &g_val);
19 printf("heap address : 0x%x\n", heap);
20 printf("main stack0 address : 0x%x\n", &a);
21 fp();
22 return 0;
23 }

结果如下:

由打印的地址可以应证上面的说法。

2. 进程映像的位置依赖于使用的内存管理方案。
3. 可执行程序与进程内存映像的不同之处在于:
a. 可执行程序位于磁盘中而内存映像位于内存;
b. 可执行程序没有堆栈,因为程序被加载到内在中才会分配堆栈;
c. 可执行程序虽然也有未初始化数据段但它并不被存储在位于硬盘中的可执行文件中;
d. 可执行程序是静态的、不变的,而内在映像随着程序的执行是在动态变化的,比如数据段随着程序的执行要存储新的变量值,栈在函数调用时也是不断在变化中。
再谈环境变量
1.从命令行参数说起

1 int main(int argc,char* argv[],char* env[])

第一个参数int argc,表示命令行参数的个数。第二个参数char *argv[],是一个指向命令行参数的指针数组,每一参数又都是以空字符(null) 结尾的字符串。第一个字符串,亦即argv[0]指向的,(通常)是该程序的名称。argv中的指针列表以NULL指针结尾(即argv[argc]为NULL)。argv[0]包含了调用程序的名称,可以利用这一特性玩个实用的小技巧。首先为同一程序创建多个链接(即名称不同),然后让该程序查看argv[0],并根据调用程序的名称来执行不同任务。gzip(1)、gunzip(1)和zcat(1)命令是该技术应用的一个例子,这些命令链接的都是同一可执行文件。(使用该技术,必须小心处理如下情况:用户通过链接调用程序,但链接名又在该程序的意料之外。) 每个C语言程序都必须有一个称为main()的函数,作为程序启动的起点。当执行程序时,命令行参数(command-line argument)(由shell逐一解析)通过两个入参提供给main()函数。新建一个文件myenv.c输入如下代码

编译链接后它的输出如下:图解如下:argv* []指针数组,存放指向命令行内输入参数的指针,argc则是指针的个数,末尾永远存有一个NULL,它们描述的是命令行的输入内容,这也是之所以它们才叫做命令行参数。

介绍环境变量

其实命令行参数后面还有一个描述环境变量的参数char* env[],那么同之前的命令行参数类似char* env[]是一个存放char*类型的指针的数组,它的每一个指针指向一个环境变量。如图:它们在内存中的布局如下命令行参数与环境变量保存于栈/堆的上方。例子4查看环境变量:

 1 #include <stdio.h>
2 int main()
3 {
4 extern char **environ;
5 int i=0;
6 for(;environ[i]!=NULL;i++){
7 printf("%s\n",environ[i]);
8 }
9 return 0;
10 }

常见环境变量

由于父进程在调用fork创建进程时会把自己的环境变量表也复制给子进程,所以a.out打印的环境变量和Shell进程的环境变量是相同的。 按照惯例,环境变量字符串都name=value 这样的形式,大多数name由大写字母加下划线组成,一般把name的部分叫做环境变量,value的部分则是环境变量的值。环境变量定义了进程的运行环境,一些比较重要的环境变量的含义如下:
PATH
可执行文件的搜索路径。 ls命令也是一个程序,执行它不需要提供完整的路径名/bin/ls,然而通常我们执行当前目录下的程序a.out却需要提供完整的路径名./a.out,这是因为PATH 环境变量的值里面包含了ls命令所在的目录/bin,却不包含a.out所在的目录。 PATH环境变量的值可以包含多个目录,由: 号隔开。在Shell中用echo命令可以查看这个环境变量的值:
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
SHELL
当前Shell,它的值通常是/bin/bash。
TERM
当前终端类型,在图形界面终端下它的值通常是xterm,终端类型决定了一些程序的输出显示方式,比如图形界面终端可以显示汉字,而字符终端却般不行。
LANG

语言和locale,决定了字符编码以及时间、货币等信息的显示格式。
HOME
当前用户主目录的路径,很多程序需要在主目录下保存配置文件,使得每个用户在运行该程序时都有自己的一套配置。用environ指针可以查看所有环境变量字符串,但是不够方便,如果给出name要在环境变量表中查 找它对应的value,可以用getenv函数。getenv的返回值是指向value的指针,若未找到则为NULL。 修改环境变量可以用以下函数putenv和setenv函数若成功则返回为0,若出错则返回非0。setenv将环境变量name的值设置为value。如果已存在环境变量name,那么 若rewrite为0,则覆盖原来的定义; 若rewrite 为0,则不覆盖原来的定义,也不返回错误。 unsetenv删除name的定义。即使name没有定义也不返回错误。
例子5:修改环境变量

1 #include <stdlib.h>
2 char *getenv(const char *name);
3 #include <stdlib.h>
4 int setenv(const char *name, const char *value, int rewrite);
5 void unsetenv(const char *name);

父进程在创建子进程时会复制一份环境变量给子进程,即,可被子进程继承,但此后两者的环境变量互不影响。进程运行起来之后修改环境变量只能影响自己。

Linux-进程描述(2)之进程标识符进程位置与环境变量的更多相关文章

  1. UNIX高级环境编程(8)进程环境(Process Environment)- 进程的启动和退出、内存布局、环境变量列表

    在学习进程控制相关知识之前,我们需要了解一个单进程的运行环境. 本章我们将了解一下的内容: 程序运行时,main函数是如何被调用的: 命令行参数是如何被传入到程序中的: 一个典型的内存布局是怎样的: ...

  2. Linux中shell基础、重定向、管道符、环境变量

    1.什么是shell Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口(命令解释器).它接收用户输入的命令并把它送入内核去执行.起着协调用户与系统的一致性和在用户与系统之间进行交互的 ...

  3. Linux编程 13 (系统环境变量位置, 环境变量持久化)

    一.系统环境变量位置 在上章中,知道了如何修改系统环境变量,如PATH变量,以及创建自己的全局环境变量和局部环境变量.这篇学习怎么让环境变量的作用持久化.在此之前,先了解下系统环境变量文件会在哪些位置 ...

  4. linux系统下将php和mysql命令加入到环境变量中的方法

    在Linux CentOS系统上安装完php和MySQL后,为了使用方便,需要将php和mysql命令加到系统命令中,如果在没有添加到环境变量之前,执行 “php -v”命令查看当前php版本信息时时 ...

  5. window 远程在Linux(centOS7.0)上安装JDK以及配置环境变量

    本人是在windows 7 上安装了虚拟机,虚拟机安装的是linux(centOS7.0)系统现在在Windows 上安装SecureCRT 远程虚拟机的linux系统,安装JDK以及配置环境变量. ...

  6. Linux学习总结(十)-文件复制及查看, 环境变量

    一 文件复制及移动 1.命令 cp --------copy 的意思格式 cp 选项 源文件 目标文件a: 对于文件我们直接cp 文件 目标文件假定我们在普通用户家目录下/home/lv新建两个普通文 ...

  7. 记一次Linux bash 命令行卡顿排查之警惕LD_PRELOAD环境变量

    现象: 通过屏幕或者ssh登录Linux操作系统(本例:Ubuntu)后,执行ls 需要数秒才返回 strace -c ls 查看实际命令调用耗时并不长 对比和正常执行的主机命令执行时,加载的库文件差 ...

  8. linux进程学习-进程描述符,控制块

    从数据结构的角度,进程用task_struct结构来描述,称为“进程描述符 (Process Descriptor)”或者“进程控制块(Process Control Block, PCB)”,其包含 ...

  9. 进程描述符(PCB)

    进程描述符(PCB) 概述 CPU作为计算机的核心部件,我们当然希望它能一直工作,充分提高它的使用效率.对于上层软件来说,我们不可能直接去操控CPU(我们没这能力也没必要),因为操作系统是夹在计算机硬 ...

随机推荐

  1. Linux配置LNMP环境(二)配置PHP

    前言:本教程安装的PHP版本php-5.6.30(官方最后更新日期2017-01-19),教程编写日期2017-07-02.本教程中的下载地址是在写教程的时候从官方复制的,时间过长可能会有变化. 安装 ...

  2. Vysor破解助手for Linux/macOS/Windows

    Vysor更新到1.7.8后,之前的破解工具又失效了,但破解的方法依然可用.在更新破解工具的过程中,Vysor又出了1.7.9版本,主要是对Android O做了处理.更新后的破解工具支持1.6.6~ ...

  3. Excel多表合并的宏

    Sub 合并当前目录下所有工作簿的全部工作表() Dim MyPath, MyName, AWbName Dim Wb As Workbook, WbN As String Dim G As Long ...

  4. 选择、冒泡排序,二分查找法以及一些for循环的灵活运用

    import java.util.Arrays;//冒泡排序 public class Test { public static void main(String[] args) { int[] ar ...

  5. 【Storm】Storm实战之频繁二项集挖掘

    一.前言 针对大叔据实时处理的入门,除了使用WordCount示例之外,还需要相对更深入点的示例来理解Storm,因此,本篇博文利用Storm实现了频繁项集挖掘的案例,以方便更好的入门Storm. 二 ...

  6. Javascript百学不厌-递归

    虽然偶尔也用过,但是从来没具体来整理过 普通递归: function fac(n) { ) ; ); } fac() 这是个阶乘.但是占用内存,因为: fac(5) (5*fac(4)) (5*(4* ...

  7. MyEclipse安装SVN插件

    MyEclipse安装svn插件有两种方式,一种是从MyEclipse里面下载,但是下载速度巨慢:第二种是将插件先下载好,再进行配置,这种方式会快一些,本文讲的是第二种方式. 1.下载SVN插件sub ...

  8. jenkins - MultiJob使用

    我们如果使用jenkins需要由串行,并行,传递参数和等待执行的功能的话,那我们会用到jenkins里面的两个东西:MultiJob和pipeline 这里我介绍下MultiJob的使用 实例任务的拓 ...

  9. 【highchart】经典问题

    摘要 记录遇到的一些问题和解决方案 时差 数据容量 多表联动 1. 时差 问题描述 highcharts 默认是标准 UTC 时间,而国内默认是东八区时间,所以会有8个小时的时差 解决方法 使用hig ...

  10. MySQL优化 - 所需了解的基础知识

    时隔一年半,期间一直想写但却觉得没有实质性的内容可记录,本文为 [高性能MySQL] 的学习日志整理分享(感兴趣建议读原书). 优化应贯穿整个产品开发周期中,开发过程中考虑一些性能问题与影响,总比出问 ...