今天开始学习关于进程的相关知识,参考的教材依然是周立功的《嵌入式Linux开发教程(上册)》,内容相当于第12章。因为没多少机会写代码来体会,这部分内容可能会比较抽象,难以理解。总之一个一个来吧。
初识进程
1. 进程是一个已经开始执行,但是还没终止的程序实例。
2. 进程是一个动态的实体,它是如何从静态的程序转换而来呢?
首先要创建一个新的进程,然后在新进程中装载程序文件,并执行文件的main函数
PS: 关于这个main函数比较有意思,通常它被作为程序的起点。main函数有三种原型定义:
int main()
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char *env[]);
来关注一下最后一种形式吧,因为他最全面嘛,搞懂了它就万无一失了。三个参数:
第一个是整形,代表命令行参数的数目
第二个是字符型指针数组,大小不定。即它是若干个指向字符型变量的指针所构成的数组。如果把命令的各个参数都当成字符串,那么这里的每个元素都指向各个字符串的首地址。注意,argv[0]这个元素指向的是程序的名称。
第三个也是字符型指针数组,大小不定。因为程序执行中,可能会有各种环境变量发生作用,这个数组中各个元素分别指向各环境变量。还有个疑问(环境变量都是字符串?)
3. 进程的状态
比较多,我也记不清,总之来一张图吧
使用命令 ps -aux 后,观察STAT列,第一个字符就代表该进程当前状态
4. 每个进程在创建时,都被内核分配一个ID,常被称为PID。PID是一个整型数据,当进程退出时,这个数值可以被回收再利用。使用getpid()函数可以获取当前进程的PID。
比如像这样写:
printf("current process pid is %d
", getpid());
PS:我查了下,getpid这个函数的头文件是unist.h
5. UID和GID
Linux中每个用户有一个用户ID,称为UID;每个用户所在的组有一个组ID,称为GID。为什么要扯这个,他与进程有什么关系?
这决定了进程的权限。在Linux中对不同的用户和组进行了非常明确的权限划分,不同的用户和组所具有的权限是不同的。当执行一个程序时,该程序会获取当前用户的UID和GID,并且以此来作为进程的权限。
6. 父进程和子进程
进程可以创建新的进程,这两者一个是父一个是子。因此父进程和子进程都是相对概念。
Linux中,所有的进程如果都套用这个父子进程的概念,可以将它们组织成一个树状结构。这棵大树的根基是init进程。也就是说所有的进程都是通过它衍生出来的。
输入 pstree命令可以以树状结构显示所有进程,不过我在root权限下这么做了,发现根基并非init,而是systemd。上网查了下,init作为根基和systemd作为根基,可以说是两种启动方式。概念上是并列、平级的。用了一个,另一个就不需要用了。init是比较老的,而systemd比较新。init应该是类似串行的执行方式,而systemd是并行的执行。具体的暂时没空去研究,以后再议吧。
7. 关于环境变量
上面提到了进程运行中需要用到环境变量,那么如何获取这些环境变量呢?方法有三
其一,就像上面讲的那样,使用main函数的第三个参数 char *env[],注意数组的最后一个元素是NULL。像书上那样写个代码测试下吧。
运行后,把一大溜的环境变量都打印了出来。
从打印的结果来反推,这是用puts一行行打印出了环境变量。因而这每一行都被视作字符串,它们的首地址传给了main函数的参数env。
其二,利用全局变量environ,该变量是一个指向字符型指针的指针,即 char **environ
教材上利用下面的代码,也能实现和刚才一样的效果:
关于这个environ全局变量,我认为以下猜测应该是基本靠谱的:
其三,利用函数getenv函数获取环境变量。
该函数的定义:
char *getenv(const char *name);
示例:
char *env;
env = getenv("HOME");
8. 标准IO
每个进程创建时,系统都会自动打开3个文件,标准输入、标准输出和标准错误输出。既然是文件就会有句柄,它们分别是0、1、2。
教材上指出:
但是我感觉,既然你已经知道了这三个文件的句柄,如果需要用到的话。直接写数字就好了呀,为什么要整这么麻烦,用宏去替代呢~是不是也会偶发像上次使用open函数,指定生成文件的权限时一样的问题:在系统版本升级或者变迁的时候,因为底层实现代码的改变,导致应用层只认这种助记符一样的宏。