在上一篇文章中详细介绍了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. VB6之调整任务栏按钮的位置

    好无聊,睡前一更~ XP的任务栏没办法像win7那样随意拖动交换顺序,偶觉不爽,遂写程序搞之.这个不算什么新东西,参考了很多别人写的东东. 程序启动后,会在右下角托盘区显示钢铁侠的图标.右键击之,可选 ...

  2. Wireshark网络端点和会话

    如果想让网络进行正常通信,你必须至少拥有两台设备进行数据流交互.端点(endpoint)就是指网络上能够发送和接受数据的一台设备.举例来说,在TCP/IP的通信中就有两个断电:接收和发送数据系统的IP ...

  3. Java 9 揭秘(11. Java Shell)

    Tips 做一个终身学习的人. 在本章节中,主要介绍以下内容: 什么是Java shell JShell工具和JShell API是什么 如何配置JShell工具 如何使用JShell工具对Java代 ...

  4. 浅谈javascript中的call与apply方法

    call方法与apply方法都是为了改变函数体内部this的指向. call方法与apply方法,这二者的作用完全一样,只是接受参数的方式不太一样. apply()方法: Function.apply ...

  5. 华为OJ之放砝码

    题目描述: 现有一组砝码,重量互不相等,分别为m1.m2--mn:他们可取的最大数量分别为x1.x2--xn.现在要用这些砝码去称物体的重量,问能称出多少中不同的重量. 注: 称重重量包括0: 要对输 ...

  6. 树上差分 (瞎bb) [树上差分][LCA]

    做noip2015的运输计划写了好久好久写不出来   QwQ 于是先来瞎bb一下树上差分    混积分 树上差分有2个常用的功能: (1)记录从点i到i的父亲这条路径走过几次 (2)将每条路径(s,t ...

  7. 文件的默认权限UMASK

    当进入Linux系统后新创建的文件或者文件夹总是会有一个默认的权限,那么这个权限是如何设置的呢? umask就是设置文件与目录的默认权限 1. 查看umask 直接查看, [root@centos6 ...

  8. 实施软件测试风险分析&回归用例刷选

    [一两年前收集整理的资料,感觉不错,放到博客上来] 作为软件测试计划的一部分,软件测试风险的分析与控制是其中重要的环节.如果前期风险分析与控制比较充分,那么会使软件的测试成功性大大增加,且可将由风险异 ...

  9. 项目DEMO下载

    1.mybatis_generator自动生成代码demo github项目地址:https://github.com/JsonShare/mybatis_generator 2.设计模式解密系列示例 ...

  10. 极简的Android RecyclerView Adapter(使用DataBinding)

    阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...