进程---基础知识+命令+函数(fork+getpid+exit+wait+exec)
目录
一、进程的定义
二、pcb
三、PID
四、进程和程序的区别
1.程序
2.进程
五、进程分类
六、进程的状态
七、进程的作用
1.并行
2.并发
八、进程的调度,进程上下文切换
九、查询进程相关命令
1.ps aux
1.1功能
1.2相关状态
2.top
2.1功能
3.kill和killall
3.1功能
3.2用法
十、函数
1.fork();
1.2特性
1.3功能
1.4返回值
1.5代码实现
2.getpid
2.1函数原型
2.2功能
2.3参数
2.4返回值
3.getppid parent
3.1函数原型
3.2功能
3.3参数
3.4返回值
十一、父子进程的关系与区别
1.关系
2.区别
十二、进程的终止
1.主动退出
2.异常终止
3.代码实现
3.1exit()
3.2_exit()
十三、僵尸进程和孤儿进程
1.孤儿进程
2.僵尸进程
十四、回调函数
atexit
1.函数原型
2.功能
3.参数
4.返回值
5.注
6.代码示例
十五、进程空间的回收
1.wait
1.1函数原型
1.2功能
1.3参数
1.4返回值
1.5宏值
1.6代码示例
2.waitpid
2.1函数原型
2.2功能
2.3参数
2.4返回值
2.5代码示例
十六、进程替换函数族---exec函数族(execute)
1.定义
2.特性
3.分类
3.1execl
3.2execlp
3.3execv
3.4execvp
一、进程的定义
进程是一个程序执行的过程,会去分配内存资源,cpu的调度
二、pcb
是一个结构体,process control block print circuit board
vim -t
task_struct
三、PID
进程标识符
当前工作路径 chdir
umask 0002
进程打开的文件列表 文件IO中有提到
信号相关设置 处理异步io,
用户id,组id
进程资源的上限
ulimit -a,显示资源上限。
四、进程和程序的区别
1.程序
静态,存储在硬盘中代码,数据的集合
2.进程
动态,程序执行的过程,包括进程的创建、调度、消亡
.c ----> a.out-----> process(pid)
1)程序是永存,进程是暂时的
2)进程有程序状态的变化,程序没有
3)进程可以并发,程序无并发
4)进程与进程会存在竞争计算机的资源
5)一个程序可以运行多次,变成多个进程
一个进程可以运行一个或多个程序
内存的分布
0-3G,是进程的空间,3G-4G是内核的空间,虚拟地址
虚拟地址 * 物理内存和虚拟内存的地址 映射表 1page=4k mmu
五、进程分类
1.交互式进程
2.批处理进程 shell脚本
3.守护进程
六、进程的状态
3个状态:就绪→执行态→阻塞(等待,睡眠)基本操作系统
linux中的状态,运行态,睡眠态,僵尸,暂停态。
七、进程的作用
1.并行
进程在多个cpu上运行
2.并发
进程在一个cpu上运行
八、进程的调度,进程上下文切换
内核主要功能之一就是完成进程调度, 硬件,bios,io,文件系统,驱动
调度算法, other,idle
rr,fifo
rt_thread ucos
先来先服务
短任务优先
优先级, 时间片
宏观并行
微观串行
九、查询进程相关命令
1.ps aux
1.1功能
查看进程相关信息
1.2相关状态
1)就绪态、运行态 R
2)睡眠态、等待态
可唤醒等待态 S
不可唤醒等待态 D
3)停止态 T
4)僵尸态 Z
5)结束态
2.top
2.1功能
根据CPU占用率查看进程相关信息
3.kill和killall
3.1功能
发送一个信号
3.2用法
1)kill -2 PID
发送信号+PID对应的进程,默认接收者关闭
2)kill -9 PID
发送信号+PID对应的进程,强制接收者关闭
3)killall -9 进程名
发送信号 进程名对应的所有进程
killall a.out
十、函数
1.fork();
1.1函数原型
pid_t fork(); 叉子
1.2特性
一次调用,会返回两次。
子进程先运行和是父进程先进程,顺序不确定。
变量不共享。
子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。
1.3功能
通过该函数可以从当前进程中克隆一个同名新进程。
克隆的进程称为子进程,原有的进程称为 父进程。
子进程是父进程的完全拷贝。
子进程的执行过程是从fork函数之后执行。
子进程与父进程具有相同的代码逻辑。
1.4返回值
int 类型的数字
1)在父进程中:成功 返回值是子进程的pid号 >0
失败 返回-1;
2)在子进程中:成功 返回值 0
失败 无
1.5代码实现
#include <stdio.h>
// #include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char **argv)
{pid_t ret = fork();if (ret > 0) //父进程 ---ret是子进程的id{while (1){printf("发送视频...\n");sleep(1);}}else if (0 == ret) //子进程{while (1){printf("接受控制\n");sleep(1);}}else{perror("fork error\n");return 1;}return 0;// system("pause");return 0;
}
变量不共享代码示例
#include <stdio.h>
// #include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>//变量不共享int a = 5;
int main(int argc, char **argv)
{pid_t ret = fork();if (ret > 0){sleep(5);printf("father a is %d\n", a);}else if (0 == ret){a += 5;printf("child a is %d\n", a);sleep(1);}else{perror("fork error\n");return 1;}//父进程和子进程都会走一遍---打印两边printf("a is %d\n", a);return 0;// system("pause");return 0;
}
2.getpid
2.1函数原型
pid_t getpid(void);
2.2功能
获得调用该函数进程的pid
2.3参数
缺省
2.4返回值
进程的pid
2.5代码实现
#include <stdio.h>
// #include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char **argv)
{pid_t ret = fork();if (ret > 0){int i = 5;while (i){printf("父进程 发送视频...pid:%d ppid:%d\n", getpid(), getppid());sleep(1);i--;}}else if (0 == ret){int i = 5;while (i){printf("子进程 接受控制...pid:%d ppid:%d\n", getpid(), getppid());sleep(1);i--;}}else{perror("fork error\n");return 1;}printf("pid:%d ppid:%d\n", getpid(), getppid());return 0;// system("pause");return 0;
}
3.getppid parent
3.1函数原型
pid_t getppid(void);
3.2功能
获得调用该函数进程的父进程pid号
3.3参数
缺省
3.4返回值
返回父进程id号
fork()&&fork()||fork();
十一、父子进程的关系与区别
1.关系
子进程是父进程的副本。子进程获得父进程数据段,堆,栈,正文段共享。
在fork之后,一般情况那个会先运行,是不确定的。如果非要确定那个要先运行,需要IPC机制
2.区别
1)fork的返回值 >0 ==0
2)pid不同
十二、进程的终止
1.主动退出
1)main 中return
2)exit(), c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。已经清理函数(atexit)。
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。
4) 主线程退出
5)主线程调用pthread_exit
2.异常终止
6)abort()
7)signal kill pid
8)最后一个线程被pthread_cancle
3.代码实现
3.1exit()
#include <stdio.h>
#include <stdlib.h>int main(int argc, char **argv)
{printf("hello");exit(EXIT_SUCCESS);printf("---------end----------\n");// system("pause");return 0;
}
3.2_exit()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char **argv)
{printf("hello");_exit(EXIT_SUCCESS);printf("------------------\n");// system("pause");return 0;
}
十三、僵尸进程和孤儿进程
1.孤儿进程
父进程提前终止,但是子进程还在运行中,父进程无法对子进程进行监管,子进程就成为了孤儿进程。
2.僵尸进程
子进程运行已经结束,但是父进程没有对其资源回收,导致子进程不能彻底退出,在僵尸状态,进程表中仍然会保留僵尸进程的PCB,占用系统资源。
十四、回调函数
atexit
1.函数原型
int atexit(void (*function)(void));
2.功能
注册进程退出前执行的函数
3.参数
function:函数指针
指向void返回值void参数的函数指针
4.返回值
成功返回0
失败返回非0
5.注
当程序调用exit或者由main函数执行return时,所有用atexit注册的退出函数,将会由注册时顺序倒序被调用
6.代码示例
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
char *p;
int fd;
void clean()
{printf("clean---p:%s fd:%d\n", p, fd);free(p);close(fd);
}
int main(int argc, char **argv)
{atexit(clean);p = malloc(10);fd = open("1.txt", O_RDONLY);printf("in procing.....\n");strcpy(p, "hello");char buf[10] = {0};read(fd, buf, 10);printf("before return \n");// system("pause");return 0;
}
十五、进程空间的回收
exit(20);
1.wait
1.1函数原型
pid_t wait(int *status);
1.2功能
2.1该函数可以阻塞等待任意子进程退出,并回收该进程的状态。
2.2一般用于父进程回收子进程状态。
2.3子进程若是没有消亡,父进程则会等待子进程消亡才会回收
1.3参数
status 进程退出时候的状态
如果不关心其退出状态一般用NULL表示
如果要回收进程退出状态,则用WEXITSTATUS回收。
1.4返回值
成功回收的子进程pid
失败 -1;
1.5宏值
5.1WIFEXITED(status) 是不是正常结束
WEXITSTATUS(status) 使用这个宏去那返回值
5.2WIFSIGNALED(status) 是不是收到了信号而终止的
WTERMSIG(status)如果是信号终止的,那么是几号信号。
1.6代码示例
#include <stdio.h>
// #include <stdlib.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main(int argc, char **argv)
{pid_t pid = fork();if (pid > 0){printf("father pid:%d\n", getpid());wait(NULL);printf("recycle end\n");}else if (0 == pid){sleep(3);printf("child pid:%d\n", getpid());exit(1);}else{perror("fork error");return 1;}// system("pause");return 0;
}
2.waitpid
2.1函数原型
pid_t waitpid(pid_t pid, int *status, int options);
2.2功能
< -1 回收指定进程组内的任意子进程
-1 回收任意子进程,组内外
0 回收和当前调用waitpid一个组的所有子进程,组内
> 0 回收指定ID的子进程
2.3参数
status 子进程退出时候的状态,
如果不关注退出状态用NULL;
options 选项:
0 表示回收过程会阻塞等待
WNOHANG 表示非阻塞模式回收资源。
2.4返回值
返回值:成功 返回接收资源的子进程pid
失败 -1
2.5代码示例
十六、进程替换函数族---exec函数族(execute)
1.定义
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。
2.特性
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。
调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
3.分类
3.1execl
3.1.1函数原型
int execl(const char *path, const char *arg,...
/* (char *) NULL */);
3.1.2代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char **argv)
{//实现终端命令ls -a -l//实现./a.out -a -lexecl("/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);printf("看见就错了\n");// system("pause");return 0;
}
3.2execlp
3.2.1函数原型
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
3.2.2代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char **argv)
{//实现终端命令ls -a -l//实现./a.out -a -l// execl("/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);//从firefox打开www.baidu.comexeclp("/usr/bin/firefox", "firefox", "www.baidu.com", NULL);printf("看见就错了\n");// system("pause");return 0;
}
3.3execv
3.3.1函数原型
int execv(const char *path, char *const argv[ ]);
3.3.2代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, const char *argv[])
{// ls -a -l// ./a.out -a -l execute argument list// execl("/bin/ls","ls","-a","-l","--color=auto",NULL); list argument// execlp("ls","ls","-a","-l","--color=auto",NULL); echo $PATHchar *const args[] = {"ls", "-a", "-l", "--color=auto", NULL};execv("/bin/ls", args); // vector 数组printf("看见就错了\n");// system("pause");return 0;
}
3.4execvp
3.4.1函数原型
int execvp(const char *file, char *const argv[ ]);
3.4.2代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, const char *argv[])
{// ls -a -l// ./a.out -a -l execute argument list// execl("/bin/ls","ls","-a","-l","--color=auto",NULL); list argument// execlp("ls","ls","-a","-l","--color=auto",NULL); echo $PATHchar *const args[] = {"ls", "-a", "-l", "--color=auto", NULL};// execv("/bin/ls",args);//vector 数组execvp(args[0], args);printf("看见就错了\n");// system("pause");return 0;
}