1、进程控制块=进程描述符(PCB)
进程状态(4/5种):就绪(初始化),运行,挂起(=等待), 停止
PBC成员:
1、进程id
2、进程状态
3、进程切换需要保存和恢复的CPU寄存器
4、描述虚拟地址空间信息
5、描述控制终端的信息
6、当前工作目录
7、umask掩码
8、文件描述符
9、信号相关的信息
10、用户和用户组id
11、会话(session)和进程组
12、资源使用上限(命令ulimit -a, 查看资源上限值)
2、环境变量
2.1 环境变量
环境变量,是指在操作系统中用来指定操作系统运行环境的一些参数。通常具备以下特征:
① 字符串(本质) ② 有统一的格式:名=值[:值] ③ 值用来描述进程环境信息。
2.1、环境变量
在libc库中使用外部名变量:environ获取当前系统全部的系统变量。
2.2、获取和设置环境变量
获取环境变量接口名称:
#include <stdlib.h>
/*
param:
name: 表示环境变量名称
*/
char *getenv(const char *name);
设置和取消环境变量
#include <stdlib.h>
/*
param:
name: 环境变量名
value: 环境变量
rewrite:0:不覆盖,非0:覆盖
*/
int setenv(const char* name, const char *value, int rewrite);
/*
param:
name:环境变量名
*/
void unsetenv(const char *name);
例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *path = getenv("PATH"); //获取环境变量
printf("PATH:%s\n", path);
setenv("PATH", "hello", 0); //0:不覆盖之前,非0:覆盖
printf("PATH:%s\n", getenv("PATH"));
unsetenv("PATH"); //删除环境变量
return 0;
}
进程控制
创建进程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int var = 34;
int main(void)
{
pid_t pid;
pid = fork();
if (pid < 0 ) { //出错
perror("fork");
exit(1);
} else if (pid > 0) { //父进程
sleep(2);
var = 55;
printf("I'm parent pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
} else if (pid == 0) { //子进程
var = 100;
printf("child pid = %d, parentID=%d, var = %d\n", getpid(), getppid(), var);
}
printf("var = %d\n", var);
return 0;
}
4、如何创建N个进程
简单想想写下如下代码:
for(int i = 0; i < N; i++){
pid_ t pid = fork();
if(pid > 0){
}else if(pid == 0){
//break;
}else{
}
}
但这样事实果真如此吗?上述情况创建进程会创建2^N-1个进程,因为在子进程中还会去创建孙进程,如此子子孙孙创建得不是N个进程。避免已创建的子进程也去创建进程,我们应该在else if(pid == 0)上就break来跳出当前的for循环。
5、如何使用GDB调试多线程:
set follow-fork-mode child //切换子进程
set follow-fork-mode father //gdb下默认进入父进程,切换回子进程
在gdb下上面两个命令实现父进程和子进程的切换
6、防止孤儿进程和僵死进程
孤儿进程:父进程优先于子进程结束,则子进程成为孤儿进程,孤儿进程由init进程管理。
僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放在内核中。
1. 子进程都是通过父进程来回收的。如果父进程先退出子进程还在运行,就会产生孤儿进程。
2. 僵尸进程是不能用kill 命令清除掉的,因为kill 命令只是用来终止进程的,而僵尸进程已经终止了。
子进程回收函数:
#include <sys/types.h>
#include <sys/wait.h>
/*
param:
status:传出参数
return:
成功:pid, 失败:-1
功能:1、阻塞等待子进程 2、回收子进程资源 3、获取进程结束状态
WIFEXITED(status)=true/false;
WEXITSTATUS(status)获取子进程退出状态
WIFSIGNALED(status)=true/false
WTERMSIG(status)获取终止子进程的信号编号
*/
pid_t wait(int status);
/*
param:
pid:
pid > 0 指定进程回收
pid =-1 回收任意子进程
pid = 0 回收本组任意子进程
pid <-1 回收该进程组的任意子进程
status: 参考wait()
options: 0:阻塞回收
WNOHANG:非阻塞回收(需要轮询查)
return:
成功:pid 失败 -1 返回0:options=WNOHANG,此时子进程未结束
*/
pid_t waitpid(pid_t pid, int *status, int options);
特点:
1. 如果子进程还在运行会阻塞等待。
2. 带子进程的终止信息立即返回。
3. 出错(return -1)立即返回。
区别:
- 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
- wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。