在上一篇文章中详细介绍了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)。

 #include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid; //pid_t 其实是int
pid = getpid();
printf("the current program's pid is %d\n",pid);
while();
return ;
}

运行程序,然后使用“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。

 #include <unistd.h>
#include <stdio.h>
int main()
{
pid_t ppid; //pid_t 其实是int
ppid = getppid();
printf("the current program's ppid is %d\n",ppid);
while();
return ;
}

同样,运行程序,然后使用“ps -u”命令查看对照如下:

我们注意到父进程为bash。事实上任何在命令行里运行的进程的父进程都是shell。

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

例子3: C地址空间测试代码如下:

 #include <stdio.h>
#include <stdlib.h>
int g_val = ;
void test()
{
int a = ;
int b = ;
printf("test stack1 address : 0x%x\n", &a);
printf("test stack2 address : 0x%x\n", &b);
}
void (*fp)();
int main()
{
int a = ;
int *heap = malloc(sizeof(int));
fp = test;
printf("code address : 0x%x\n", fp);
printf("data address : 0x%x\n", &g_val);
printf("heap address : 0x%x\n", heap);
printf("main stack0 address : 0x%x\n", &a);
fp();
return ;
}

结果如下:

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

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

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

  第一个参数int argc,表示命令行参数的个数。第二个参数char *argv[],是一个指向命令行参数的指针数组,每一个参数又都是以空字符(‘\0’) 结尾的字符串。第一个字符串,亦即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查看环境变量:

 #include <stdio.h>
int main()
{
extern char **environ;
int i=;
for(;environ[i]!=NULL;i++){
printf("%s\n",environ[i]);
}
return ;
}

常见环境变量

由于父进程在调用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:修改环境变量

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

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

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

  1. Linux下进程描述(1)—进程控制块

    进程概念介绍 进程是操作系统对运行程序的一种抽象. • 一个正在执行的程序: • 一个正在计算机上执行的程序实例: • 能分配给处理器并由处理器执行的实体: • 一个具有普以下特征的活动单元:一组指令 ...

  2. 《Linux内核分析》第六周笔记 进程的描述和进程的创建

    进程的描述和进程的创建 一.进程的描述 1.进程描述符task_struct数据结构(一) 操作系统的三大功能:进程管理(核心).内存管理.文件系统. 进程控制块PCB——task_struct(进程 ...

  3. LINUX内核分析第六周学习总结——进程的描述与创建

    LINUX内核分析第六周学习总结--进程的描述与创建 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc ...

  4. Linux下进程描述(1)—进程控制块【转】

    转自:http://www.cnblogs.com/33debug/p/6705391.html 进程概念介绍 进程是操作系统对运行程序的一种抽象. • 一个正在执行的程序: • 一个正在计算机上执行 ...

  5. 进程描述和控制(os 笔记二)

    进程描述和控制 ​ 计算机最初的主要任务之一就是高效的自动化我们的工作,完成用户交付的任务.而这种任务在计算机中的表示就是一个个的进程.从上一篇文章中描述的计算机的发展历史我们能发现,无论是单道批处理 ...

  6. Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)【转】

    Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息.它定义在include/linux/sched.h文件中. 谈到task_str ...

  7. LINUX内核分析第六周学习总结——进程的描述和进程的创建

    LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  8. linux进程学习-进程描述符的存储

    当进程被新建时,内核会给进程分配一个8K的空间作为进程的内核堆栈.同时我们知道task_struct结构体也会被创建,但有意思的是,内核不会给task_struct单独分别空间,而是直接将其扔到8k的 ...

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

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

随机推荐

  1. [LeetCode] Is Subsequence 题解

    前言 这道题的实现方法有很多,包括dp,贪心算法,二分搜索,普通实现等等. 题目 Given a string s and a string t, check if s is subsequence ...

  2. JSP +++SERVIET总复习

    一. JSP基础概念 软件架构 B/S架构:Browser/Server,浏览器-服务器 最大的优点就是:一次部署,处处访问. C/S架构:Client/Server,客户端-服务器 功能.事件丰富, ...

  3. vector 对象中存放指针类型数据

    <<C++ Primer>> 第四版Exercise Section 5.6 的5.1.6 有一道题是这样的:编写程序定义一个vector对象,其每个元素都是指向string类 ...

  4. 基于cxf开发restful风格的Web Service

    一.写在前面 webservice一些简单的其他用法和概念,就不在这里赘述了,相信大家都可以在网上查到,我也是一个新手,写这篇文章的目的一方面是想记录自己成长的历程,另一方面是因为学习这个的时候花了点 ...

  5. App对接支付宝移动支付功能

    前段时间看了下app对接支付宝移动支付的功能,并自己总结了下支付宝移动支付的实现流程 一.申请流程          前提是已有现成的应用. 1.  申请地址            https://b ...

  6. TCP/IP笔记(二)TCP/IP简介

    上回,主要介绍了下协议和OSI参考模型,并简单了解下网络构成要素,这回该说说TCP/IP了 互联网与TCP/IP的关系   互联网进行通信时,需要相应的网络协议,TCP/IP原本就是为使用互联网而开发 ...

  7. vue搭建开发环境

    windows下搭建vue开发环境 一.安装node.js 安装   vue项目通常通过webpack工具来构建,而webpack命令的执行是依赖node.js环境,所以首先要安装node.js. n ...

  8. Java标准注释配置

    eclipse中java文件头注释格式设置 windows->preferences->java->Code Templates->comments->Type-> ...

  9. (11)连个工具类之间的比较4.Collections与Arrays

    集合框架中的工具类:特点:该工具类中的方法都是静态的. Collections:常见方法: 1, 对list进行二分查找: 前提该集合一定要有序. int binarySearch(list,key) ...

  10. 2.WP8.1开发_在顶部显示标题和进度

    有时候加载页面的时候,需要在信号那一栏显示进度,或者把信号栏改成标题 1.确保显示状态栏.默认显示.如果不显示,可以在应用程序启动后手动用代码显示,代码如下: //取得状态栏 StatusBar ba ...