进程
原文链接:http://www.orlion.ga/1015/
一、进程
每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,linux内核的进程控制块是task_struct结构体,其中有:
-
进程id。系统中每个进程有一个唯一的id,在C语言中用pid_t类型表示,是一个非负正是
-
进程的状态,有运行、挂起、停止、僵尸等状态
-
进程切换时需要保存和恢复的一些CPU寄存器
-
描述虚拟地址空间的信息
-
描述控制终端的信息
-
当前工作目录
-
umask掩码
-
文件描述符表,包含很多指向file结构体的指针
-
和信号相关的信息
-
用户id和组
-
控制终端、Session和进程组。
-
进程可以使用的资源上限
fork的作用是根据一个现有的进程(父进程)复制出一个新进程(子进程),系统中同时运行着多个进程,这些进程都是从最初只有一个进程开始一个一个复制出来的,在Shell下输入命令可以运行一个程序,是因为Shell进程在读取用户输入的命令之后会调用fork复制出一个新的Shell进程,然后新Shell进程调用exec执行新的程序。
一个程序可以多次加载到内存中成为同时运行的多个进程,例如可以同时开多个终端运行/bin/bash。一个进程在调用exec前后也可以分别执行两个不同的程序,例如在Shell下输入命令ls,首先fork创建子进程,这时子进程仍在执行/bin/bash程序然后子进程调用exec执行新的程序/bin/ls。如下图所示:
子进程的PCB是根据父进程复制而来的,所以其中的umask掩码也和父进程一样。同样道理子进程的当前工作目录也和父进程一样,所以我们用cd改变Shell进程的工作目录,然后ls列出那个目录下的文件,ls进程其实是在列自己的当前目录,而不是Shell进程的当前目录,只不过ls进程的目录和Shell的目录一样。但是子进程PCB中的进程id和父进程不同。
二、环境变量
exec系统调用执行新程序时会把命令行参数和环境变量表传递给main函数,它们在整个进程地址空间中的位置如下所示:
和命令行参数argv类似,环境变量表也是一组字符串,如下图所示:
libc定义的全局变量environ指向环境变量表,environ没有包含在头文件中,所以在使用时要用extern声明。例:
#include <stdio.h> int main(void) { extern char **environ; int i; for (i=0; environ[i] != NULL; i++) printf("%s\n", environ[i]); return 0; }
执行结果为:
由于父进程在调用fork创建子进程时会把自己的环境变量表也复制给子进程,所以打印的环境变量和Shell进程打印的环境变量是相同的。环境变量定义了进程的运行环境。
可以用char *getenv(const char *name)获取name在环境变量表中对应的value。
可以用int setenv(const char *name, const char *value, int rewrite);设置环境变量。
可以用void unsetenv(const char *name);删除name的定义。
在子进程中修改环境变量并不会改变父进程的环境变量。