一.进程概念
1.进程与程序的差异? 未编译的程序 ----------可执行程序 程序: 一堆待执行的程序 gcc hello.c -o hello (程序是一个静态数据) 剧本
进程: 只有程序被加载到CPU中占用资源时,根据代码每行做出反应,才能形成一个真正动态的进程 (进程是一个动态的过程) 拍戏的过程
2.可执行程序 project --> ./project --> 开启一个进程
以下的几个区域的数据会直接从程序中拷贝到内存中
| |
---|
.init | 初始化段 | .data和.bss | 数据段 | .rodata | 常量区 |
3.当程序执行时,除了会分配空间之外,系统还会为你分配一个结构体目录,读取一个目录项时就会返回一个结构体指针,这个结构体用于描述这个目录项 (描述:类型,结构体大小,索引号,偏移量,文件名)
./project 开启一个进程
--> 返回一个结构体
struct task_struct{};
这个结构体用于描述这个进程 (描述:进程ID号,信号,文件,资源…)这个结构体在哪里?
/usr/src/linux-headers-3.5.0-23/include/linux/sched.h
4. 关于进程的命令
1) 查看整个Linux系统的命令 --> pstree
//所有的进程的祖先进程都是init进程 init─┬─NetworkManager───{NetworkManager} ├─accounts-daemon───{accounts-daemon} ├─acpid ├─atd ├─avahi-daemon───avahi-daemon ├─bluetoothd ├─colord───2*[{colord}] ├─console-kit-dae───64*[{console-kit-dae}] ├─cron ├─cupsd
2) 查看进程PID号 --> ps -ef (静态)
3)查看进程CPU占用率 --> top (动态)
CPU实时使用率
5. 进程的状态
就绪态 TASK_RUNNING 等待CPU资源 运行态 TASK_RUNNING 占用CPU资源 暂停态 TASK_STOPPED 收到暂停信号 睡眠态 TASK_INTERRUPTIBLE 响应信号 --> 浅度睡眠 pause() --> 直到收到一个信号为止 TASK_UNINTERRUPTIBLE 不响应信号 --> 深度睡眠 僵尸态 EXIT_ZOMBIE 进程退出时,就一定会变成僵尸态,占用CPU资源 死亡态 进程退出时,如果有进程帮自己回收资源,那么就会从僵尸态变成死亡态
所谓CPU资源: 指的就是 struct task_struct{};
二. 进程函数接口
1.如何创建新的进程? --> fork() --> man 2 fork
NAME fork - create a child process //在一个进程创建子进程,父子进程就会一起执行
//头文件 #include <unistd.h>
//函数原型 pid_t fork(void); --> 不需要传递任何参数
//返回值: pid_t 进程ID号类型
成功: 父进程返回子进程PID号 子进程返回0 失败: -1
====================================================
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello!\n");
fork();
printf("world!\n");
return 0;
}
========================================================
//情况1: 父进程先执行,子进程后执行,一旦父进程结束,就会执行return 0,就会出现命令行 gec@ubuntu:/mnt/hgfs/01/code$ ./fork hello! gec@ubuntu:/mnt/hgfs/01/code$ world!
//情况2: 父进程后执行,子进程先执行
gec@ubuntu:/mnt/hgfs/01/code$ ./fork hello! world! gec@ubuntu:/mnt/hgfs/01/code$
总结: 1)fork()之后,父子进程并发进行 2)父子进程是随机先后执行 3)PID号没有负数
2.查看自身的PID号/查看父进程的PID号
getpid() getppid()
getpid, getppid - get process identification //获取相应的ID号
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); --> 查看自身的ID号
pid_t getppid(void); --> 查看当前进程的父进程ID号
返回值:
成功: 相应的PID号
失败: These functions are always successful.
三. 创建前后,父子进程资源差异?
- 子进程继承父进程大部分资源,PID号资源不会继承。
- 父子进程之间的空间相互独立,互不影响
#include <stdio.h>
#include <unistd.h>
int main()
{
int a = 100;
pid_t x = fork();
if(x > 0)
{
a = 50;
printf("parent a = %d\n",a);
}
if(x == 0)
{
printf("child a = %d\n",a);
}
return 0;
}
四. 关于子进程中,资源回收问题。
为什么要进行资源回收? --> 释放CPU资源
进程状态 运行态: 占用CPU资源,运行程序中代码 僵尸态: 占用CPU资源,不运行程序中代码 死亡态: 不占用CPU资源
2.解决僵尸问题:
1) 当一个进程的父进程比自身先退出,系统就会指定init为继父,等待子进程退出,回收子进程的资源 2) 当父进程还在运行时,父进程主动回收子进程资源
两种情况: 1. 父进程需要持续一段时间,再进行回收,子进程很快就结束自己的任务退出了。子进程退出时,子进程会变成僵尸态,等待自己的父进程结束任务后,再帮子进程回收资源,子进程变成死亡态
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t x = fork();
if(x > 0)
{
int i;
printf("hello,I am parent!\n");
for(i=0;i<20;i++)
{
printf("hello!\n");
sleep(1);
}
int state;
wait(&state);
printf("state = %d\n",state);
}
if(x == 0)
{
sleep(2);
printf("I am child!\n");
}
return 0;
}
2.父进程在子进程退出之前就已经在监听子进程的状态,回收子进程资源,让子进程变成死亡态。子进程退出后,变成僵尸态,马上就会被父进程回收,进入死亡态。
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t x = fork();
if(x > 0)
{
int state;
printf("hello,I am parent!\n");
wait(&state);
printf("state = %d\n",state);
}
if(x == 0)
{
int i;
for(i=0;i<5;i++)
{
printf("hello!\n");
sleep(1);
}
exit(1);
}
return 0;
}
- 回收子进程资源的接口 – wait() / waitpid()
wait for process to change state
功能: 监听子进程的状态
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
status: 监听的子进程的退出状态的指针变量
返回值: 成功:退出的子进程的PID号 失败:-1
waitpid()是针对wait()函数的封装 --> man 2 waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
pid:
< -1 : 等待这个负数的绝对值的ID号的进程
-1 : 等待任意一个子进程 与wait()
0 : 等待进程组中任意一个进程
> 0 : 等待指定的子进程
status: 监听的子进程的退出状态的指针变量
options:
WNOHANG: 监听子进程的退出状态,但是是非阻塞,无论当时子进程有没有退出,这个函数会立即返回!
WUNTRACED: 监听子进程的暂停信号,阻塞
WCONTINUED:监听子进程的恢复信号,阻塞
0 :阻塞 (等价于wait())
The call wait(&status) is equivalent to: waitpid(-1, &status, 0);
4.进程的退出
#include <stdlib.h>
void exit(int status); ---> 清洗缓冲区后,再退出
#include <unistd.h>
void _exit(int status); ---> 不清理缓冲区,直接退出
五. exec族
NAME execl, execlp, execle, execv, execvp, execvpe - execute a file
SYNOPSIS
#include <unistd.h>
int execl(const char *path, const char *arg,...);
int execlp(const char *file, const char *arg,...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
path: 需要执行的那个文件的绝对路径 file: 文件名 arg: 执行程序时,需要的参数列表,以NULL作为结束标志 envp: 环境变量
只要进程被exec族替换掉,在exec函数之后的代码都不会执行
|