文章目录
进程基础
1.进程相关概念
什么是程序?
- 编译好的二进制文件
什么是进程?
- 运行着的程序
- 站在程序员的角度:运行一系列指令的过程
- 站在操作系统角度:分配系统资源的基本单位
程序和进程的区别
- 程序占用磁盘,不占用系统资源
- 内存占用系统资源
- 一个程序对应多个进程,一个进程对应一个程序
- 程序没有生命周期,进程有生命周期
2、进程控制块PCB
进程控制块结构体声明在:/usr/include/linux/sched.h
进程和程序的区别?
(1)位置不一样:程序文件放在外部存储器,进程在内存中(正在运行的程序)
(2)程序文件:静态的,进程:动态的
3、进程的组织方式
①父子进程
(1)查看进程树,以树形图显示进程:pstree
(2)除了系统初始init进程以外,任何进程都必须有父进程
(3)如果一个进程的父进程死掉,那么这个进程就会被系统分配一个默认父进程给它
②进程运行
(1)./程序文件名 前台进程
(2)./程序文件名 & 后台进程
前台进程可以直接用ctrl+c关闭,后台进程不行,我们可以用kill <进程号>关闭该进程
当你的当前终端关闭,前台进程会关闭;后台进程不会关闭
Linux系统中每一个进程都有父进程,当这个进程的父进程死掉,子进程如果没有跟着父进程一起死掉,又没有被分配默认的父进程,就变成孤儿进程,但是linux不允许,它会将这个进程作为一个特殊的进程(类似孤儿院)的子进程。
③查看正在运行的进程
ps命令,默认显示当前终端的所有进程
选项:
-f 查看更多信息
-u 显示更多信息(和-f显示的信息不一样)
-a 显示所有用户的所有进程
-x 显示无终端的进程
-e 显示所有进程
ps -ef
④终止进程
kill <信号> <进程号>
kill -9 <进程号>
kill -l
⑤动态显示正在运行的进程
top
退出用q
4、进程的状态
- 就绪态
- 暂停态
- 睡眠态,睡眠进程既占用内存资源又占用CPU资源,睡眠的恢复是自动完成的,因为睡眠有一个睡眠时间,睡眠时间到则恢复到就绪态
- 挂起态,挂起进程不再占用CPU和内存资源
- 执行态
- 僵尸态
- 死亡态
5.进程相关函数
①创建一个新的进程
pid_t fork(void);
返回值:
- 失败 -1
- 成功,两次返回
- 父进程返回子进程的ID
- 子进程返回0
获得pid,进程id,获得当前进程
- pid_t getpid(void);
获得当前进程父进程的id
- pid_t getppid(void);
②父子进程的联系和区别
(1)相同的地方
A) 实际 UID 和 GID,以及有效 UID 和 GID。
B) 所有环境变量。
C) 进程组 ID 和会话 ID。
D) 当前工作路径。除非用 chdir()加以修改
E) 打开的文件。
F) 信号响应函数。
G) 整个内存空间,包括栈、堆、数据段、代码段、标准 IO 的缓冲区等等。
(2)不同的地方
A) 进程号 PID。PID 是身份证号码,哪怕亲如父子,也要区分开。
B) 记录锁。父进程对某文件加了把锁,子进程不会继承这把锁。
C) 挂起的信号。这些信号是所谓的“悬而未决”的信号,等待着进程的响应,子进程也不会继承这些信号。
循环创建n个子进程
如果在循环中子进程不中断循环,将出现问题,子进程会在循环中继续创建子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int n = 5;
int i =0;
pid_t pid = 0;
for(i = 0; i < 5 ; i ++)
{ //父进程循环结束
pid = fork();
if(pid == 0)
{
//son
printf("I am child ,pid=%d,ppid=%d\n",getpid(),getppid());
break;//子进程退出循环的接口
}else if(pid > 0){
//father
printf("I am father,pid=%d,ppid=%d\n",getpid(),getppid());
}
}
while(1)
{
sleep(1);
}
return 0;
}
③exec函数簇
功能:在进程中加载新的程序文件或者脚本,覆盖原有代码,重新运行
函数原型
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[ ]);
int execle(const char*path, const char *arg, .., char * const envp[ ]);
int execlp(const char *file, const char *arg,..);
int execvp(const char *file, char *const argv[ ]);
int execvpe(const char*file, char *const argv[ ],char *const envp[ ]);
参数
path即将被加载执行的ELF文件或脚本的路径
file即将被加载执行的ELF文件或脚本的名字
arg以列表方式罗列的ELF文件或脚本的参数
argv以数组方式组织的ELF文件或脚本的参数
envp用户自定义的环境变量数组
返回值:成功,不返回;失败,返回-1
备注
1,函数名带字母1意味着其参数以列表(list)的方式提供。
2,函数名带字母v意味着其参数以矢量(vector)数组的方式提供。
3,函数名带字母p意味着会利用环境变量PATH来找寻指定的执行文件。
4,函数名带字母e意味着用户提供自定义的环境变量。
被加载的文件的参数列表必须以自身名字为开始,以 NULL为结尾。
比如要加载执行当前目录下的一个叫做a.out的文件,需要一个参数"abcd",那么正确的调用应该是:
execI(./a.out", "a.out", "abcd",NULL);
或者;
const char *argv[3] = {"a.out", "abcd",NULL};
execv("./a.out", argv);
exec函数簇成功执行后,原有的程序代码都将被指定的文件或脚本覆盖,因此这些函数一旦成功后面的代码是无法执行的,他们也是无法返回的。
④程序退出
return
函数退出,当return放在main函数的时候,表示程序退出
exit()函数
#include <stdlib.h>
void exit(int status);
_exit()函数
#include <unistd.h>
void _exit(int status);
退出处理函数注册
#include <stdlib.h>
int atexit(void (*function)(void));
退出本进程
# include <unistd.h>
#include <stdlib.h>
原型
void _exit(int status);
void exit(int status);
参数
status子进程的退出值
返回值 不返回
备注
1,如果子进程正常退出,则status一般为0。
2,如果子进程异常退出,则statuc一般为非0。
3,exit()退出时,会自动冲洗(flush)标准IO总残留的数据到内核,如果进程注册了“退出处理函数”还会自动执行这些函数。而_exit()会直接退出。
⑤等待子进程退出/状态变化
wait、waitpid
功能
等待子进程
头文件
#include <sys/wait.h>
原型
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
参数
pid:
小于-1:等待组ID的绝对值为pid的进程组中的任一子进程
-1:等待任一子进程
0:等待调用者所在进程组中的任一子进程大于0:等待进程组ID为pid的子进程
stat_loc: 子进程退出状态
option:
WCONTINUED:报告任一从暂停态出来且从未报告过的子进程的状态
wNOHANG:非阻塞等待
wUNTRACED:报告任一当前处于暂停态且从未报告过的子进程的状态成功:
返回值:
wait()
失败: -1
成功:退出的子进程PID
waitpid()
成功:状态发生改变的子进程PID(如果 WNOHANG被设置,且由 pid指定的进程存在但状态尚未发生改变,则返回0)。
失败: -1
备注:
如果不需要获取子讲程的很出状态,stat loc可以设置为NULL
相关宏的含义
WIFEXITED(status)如果子进程正常退出,则该宏为真。
WEXITSTATUS(status)如果子进程正常退出,则该宏将获取子进程的退出值。
WIFSIGNALED(status)如果子进程被信号杀死,则该宏为真。
WTERMSIG(status)如果子进程被信号杀死,则该宏将获取导致他死亡的信号值。
WCOREDUMP(status)如果子进程被信号杀死且生成核心转储文件(core dump) ,则该宏为真。
WIFSTOPPED(status)如果子进程的被信号暂停,且 option中 WUNTRACED已经被设置时,则该宏为真。
wSTOPSIG(status)如果 WIFSTOPPED(status)为真,则该宏将获取导致子进程暂停的信号值。
WIFCONTINUED(status)如果子进程被信号SIGCONT重新置为就绪态,该宏为真。
实现父子进程同步互斥机制用那两个函数?
什么是同步?什么是互斥?
父进程没有用延时或者wait/waitpid等待子进程,父子进程同时运行
父进程使用延时或者wait/waitpid一直等到子进程结束,父进程才会运行
⑥孤儿进程与僵尸进程
孤儿进程:父亲死了,子进程被init进程领养。
僵尸进程:子进程死了,父进程没有回收子进程的资源(PCB)
如何回收僵尸进程:杀死父亲,init领养,负责回收。
⑦子进程回收
test
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
int i,wstatus;
char *funcdir[3] = {"./f1","./f2","./f3"};
char *func[3] = {"f1","f2","f3"};
pid_t _pid[3],w;
for(i=0;i<3;i++)
{
_pid[i] = fork();
if(_pid[i] == 0)
{
execl(funcdir[i],func[i],NULL);
}
if(_pid[i] < 0)
{
perror("fork()");
exit(0);
}
}
for (int i = 0; i < 3; ++i)
{
do {
w = waitpid(_pid[i],&wstatus,WUNTRACED | WCONTINUED);
if (w == -1)
{
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(wstatus)) {
printf("[%d] exited, status=%d\n", w,WEXITSTATUS(wstatus));
} else if (WIFSIGNALED(wstatus)) {
printf("[%d] killed by signal %d\n",w, WTERMSIG(wstatus));
} else if (WIFSTOPPED(wstatus)) {
printf("[%d] stopped by signal %d\n",w, WSTOPSIG(wstatus));
} else if (WIFCONTINUED(wstatus)) {
printf("[%d] continued\n",w);
}
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
// exit(EXIT_SUCCESS);
}
printf("父进程结束\n");
return 0;
}
atexit
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int *p;
void func()
{
printf("%s_________\n",__FUNCTION__);
printf("程序退出处理函数\n");
free(p);
return;
}
int main(int argc, char const *argv[])
{
atexit(func); //exit前执行退出处理函数
printf("%s_________\n",__FUNCTION__);
printf("%d_____________malloc______\n",__LINE__);
p= malloc(4);
exit(0);
}