c/c++:进程(PCB进程控制块、进程状态、exec族函数、进程控制)

目录

一、进程概述

1、PCB(Processing Control Block)

2、进程状态

二、进程创建

2.1 进程ID

2.2 进程的创建

2.3 父子进程

三、exec族函数

四、进程控制

4.1 结束进程

4.2 孤儿进程

4.2 僵尸进程

4.3 进程回收

wait

waitpid


 

一、进程概述

1、PCB(Processing Control Block)

Linux内核的进程控制块是task_struct结构体。理解该结构体中包含的如下信息:


进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
进程的状态,有就绪、运行、挂起、停止等状态。
进程切换时需要保存和恢复的一些CPU寄存器。
描述虚拟地址空间的信息。
描述控制终端的信息。
当前工作目录(Current Working Directory)。
umask掩码。
文件描述符表,包含很多指向file结构体的指针。
和信号相关的信息。
用户id和组id。
会话(Session)和进程组。
进程可以使用的资源上限(Resource Limit)。

 

2、进程状态

进程有五种状态, 分别是: 创建态, 就绪态, 运行态, 阻塞态, 退出态。

创建态  == 初始态
阻塞态  == 挂起态

创建态: 初始化系统资源
就绪态: 不能运行, 因为没有cpu资源, 和别的进程抢cpu
运行态: 进程抢到了cpu, 将cpu时间使用完之后, 会再次失去cpu -> 就绪态
阻塞态: 运行的进程(有cpu资源), 受到了某些条件的干扰, 失去了cpu -> sleep(10), 睡醒了之后 -> 就绪态

 

二、进程创建

 

2.1 进程ID

  • 查看进程
 ps aux / ajx
  	a: 显示当前终端下的所有的程序, 包括所有的用户
  	u: 显示用户信息
  	x: 打印和tty终端先关的信息
  	j: 显示更多的用户信息
  • 杀死进程
 $ kill -l
   1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
   6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
  11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
  16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
  21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
  26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
  31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
  38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
  43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
  48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
  53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
  58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
  63) SIGRTMAX-1  64) SIGRTMAX
  
  # 杀死进程
  kill -SIGKILL 进程ID
  kill -9 进程ID
  • 获取进程ID
  #include <sys/types.h>
  #include <unistd.h>
  
  pid_t getpid(void);	// 当前进程
  pid_t getppid(void); // 当前进程的父进程

 

2.2 进程的创建

#include <unistd.h>
  // 在当前进程中创建一个子进程
  pid_t fork(void);
  返回值: 得到两个数
  	- >0 : 代表是父进程的返回值
  	- == 0: 子进程的返回值

如何区分父子进程?

通过fork的返回值, fork调用成功之后, 会得到两个进程
     每个进程都会返回一个值
      - 父进程: > 0
      - 子进程: == 0

 

2.3 父子进程

  • - 区别

  - fork的返回值不同
    - 父进程: >0
    - 子进程: == 0

  •   - pcb中一些数据

    - 当前进程的PID
    - 当前进程的PPID
    - 信号集

  • - 共同点

  - 某个状态下: 子进程刚被创建出来, 还没有执行任何操作
    - 用户区数据
    - 文件描述符表

  • - 父子进程对变量是不是共享的?

  - 刚开始时候一样, 如果数据被修改之后, 不共享
  - 读时共享(子进程被创建, 两个进程没有做任何写操作), 写时复制

 

三、exec族函数

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。

exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括[代码段],[数据段]和[堆栈]等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

#include <unistd.h>

// 不是linux系统函数
int execl(const char *path, const char *arg, ...);  // 使用最多的
	参数: 
		- path: 可执行程序的路径, 建议写绝对路径
		- arg: 第二个参数, 随便写, 一般为了看起来舒服, 写成和参数1相同的值
		- 从第三个参数: 可执行程序执行过程中需要的真正的参数
		- 最后一个参数: NULL(哨兵,记住必须是空即可)
   举例: /home/itcast/test/a.out
   execl("/home/itcast/test/a.out", "a.out", "hello", "123", NULL);
   execl("/bin/ps", "ps", "a", "u", "x", NULL);

int execlp(const char *file, const char *arg, ...); // 使用最多的
	参数:
		- file: 可执行程序的名字, 在执行这个程序之前, 子动搜索系统环境变量PATH
		- arg: 第二个参数, 随便写, 一般为了看起来舒服, 写成和参数1相同的值
		- 从第三个参数: 可执行程序执行过程中需要的真正的参数
		- 最后一个参数: NULL(哨兵)

int execle(const char *path, const char *arg, ..., char *const envp[]);
	- path: 文件名
	- char *const envp[], 从这个参数指定的路径中所属第一个参数对应的文件名
	char* envp[] = {"/home/robin", "/a/b", "home/zhangsan/test", NULL};

int execv(const char *path, char *const argv[]);
	- path: 例如: /bin/ps
	- argv: 参数列表
	
	char* args[] = {"xxx", "aux", NULL};
int execvp(const char *file, char *const argv[]);
// linux系统函数
int execve(const char *path, char *const argv[], char *const envp[]);

exec族函数的结尾单词所代表的含义:

 l(list)   参数地址列表,以空指针结尾
p(path) 按 PATH 环境变量指定的目录搜索可执行文件
e(environment) 存有环境变量字符串地址的指针数组的地址
v(vector)  存有各参数地址的指针数组的地址

 

四、进程控制

4.1 结束进程

每个进程结束之后, 都会自己释放自己地址空间中的 用户区数据, 内核区的pcb没有办法自己释放掉, 需要父进程去释放.

// exit -> c库函数
#include <stdlib.h>
void exit(int status);      // status为返回值(退出值),一般写-1;
exit(-1)

// _exit() -> linux系统函数
#include <unistd.h>
void _exit(int status);

 

4.2 孤儿进程

爹生孩子, 爹英年早逝, 只剩下孩子, 这个进程就是孤儿进程. 
孤儿会被领养, 被pid=1的进程, pid=1的进程, 变成了孤儿进程父亲

 

4.2 僵尸进程

爹生孩子,  爹还活着, 但是孩子死了, 这时候子进程的pcb需要回收, 这个爹不负责任, 不回收子进程的pcb, 这个进程变成了僵尸进程.

僵尸进程: 没有用户区数据, 只有一个pcb, 不能称之为一个存活的进程
僵尸进程不能被 kill -9 杀死

 

4.3 进程回收

  • wait() 函数用于使父进程,直到一个子进程结束或该进程接收到一个指定的信号为止。
  • 如果该父进程没有子进程或它的子进程已经结束,则wait()就不阻塞,会立即返回。
  • wait调用一次只能回收一个子进程
  • waitpid()的作用和wait()一样,但它并不一定要等待一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能,也能支持作用控制。
  • 实际上,wait()函数只是waitpid()函数的一个特例,在Linux内部实现wait()函数时直接调用的就是waitpid()函数。

 

wait

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
	- 参数: status记录了子进程退出时候的状态, 正常->退出值(exit(10))/非正常退出 -> 被信号干掉了
	- 返回值: 
		>0: 被回收的子进程的进程ID
		==-1: 调用失败


// status参数的使用
int s;
int ret = wait(&s);
if(WIFEXITED(s))	                // 判断子进程是不是正常退出
{   
    printf("退出的状态码: %d\n", WEXITSTATUS(s));	// 打印子进程退出的状态码
}   
if(WIFSIGNALED(s))	                // 判断子进程是不是被信号干掉了
{   
    printf("被这个信号干掉了: %d\n", WTERMSIG(s));	// 打印子进程是被那个信号杀死的
} 

上面使用的几个宏记一下,WIFEXITED、WEXITSTATUS、WIFSIGNALED、WTERMSIG

参数status解释:

如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其中最常见的两个:

1、WIFEXITED(status):这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

2、WEXITSTATUS(status):当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7)退出,WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出,也就是说,WIFEXITED返回0,这个值就毫无意义了。

 

waitpid

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
    参数:
	  - pid:
		>0: 某个子进程的pid, 比较常用
		=0: 回收当前进程组的子进程
		-1: 回收所有的子进程 == wait(NULL), 用的最多的
                <0: 某个进程组的组ID, 回收指定进程组的子进程
         - options: 设置函数阻塞或者非阻塞
         	- 0: 阻塞
         	- WNOHANG: 非阻塞,若由pid指定的子进程没有结束,则waitpid()不阻塞而立即返回。此时返回0
     返回值:
		- >0: 回收的子进程的ID
		- =0: options=WNOHANG, 还有子进程活着
		- -1: 错误, 已经没有子进程了

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章