1.进程的定义:
运行中的程序
操作系统资源调度的基本单位
资源 CPU时间片、文件描述符号 fd、内存、设备、等等...
2.进程的组成:
数据:全局、静态、只读变量(类比内存四区)
代码:进程相关的源代码,存在于全局变量区
堆栈:进程绑定的堆空间和栈空间
优先级、时钟、进程id 等等...
linux系统下每个进程在/proc目录下有进程id及其对应的目录存储进程的一些数据
3.进程模式
用户模式:应用程序运行模式
内核模式:程序调用系统调用,触发系统中断切到内核模式,内核模式完毕切回用户模式
4.进程状态
运行时状态、睡眠状态、挂起状态、等待状态、死亡状态、僵尸状态、等等...
用ps -aue、top、tree等命令可以查看进程状态
5.创建进程
1)手动运行
命令、双击执行、system函数(程序中执行命令)
2)调度进程
nice、renice 控制进程是否让步
kill 结束进程
crontab 安装进程
3)fork vfork 创建子进程
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
4)exec簇
6.父子进程
getpid 获取当前进程的id
getppid 获取当前进程的父进程(parent)id
创建进程的进程 父进程
被创建的进程 子进程
clone 克隆
子进程复制父进程的代码,会拷贝进程上下文(当前的运行状态,运行到什么时候,到哪行)
所以子进程也有fork函数,且子进程一开始就会执行fork函数
父进程执行fork后,父子进程同时运行
父进程的fork返回子进程id,子进程fork返回0
所以可以根据fork返回值区分父子进程代码
7.进程的结束
1)程序执行完毕自然结束(main函数的return)
2)程序自己结束自己(exit函数)
3)父进程结束不会自动结束其子进程
但是结束终端会结束终端上的子进程
4)其他进程可以结束进程(kill命令)
kill -9 [进程id]
8.僵尸进程
父进程先于子进程结束,子进程结束的时候它的一些资源未释放
持续占用系统资源,直到系统重启才会释放
要注意避免僵尸进程的产生(类比手动内存分配要注意内存泄漏)
最简单避免防止:父进程 晚于 子进程结束
子进程结束前会向父进程发送SIGCHILD信号,父进程可以使用wait函数等待这个信号
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
9.进程的组织形式
进程
进程组:多个 进程 组成,进程组的组长(session进程)
会话:多个 进程组 组成
10.守护进程
用来 观察、记录的
特征:
能持续运行,不能被其他进程轻易干掉
属于后台进程,不会被看到
一般用来用作记录日志文档
守护进程编程模型:
第一种:
1)创建新会话 setsid
2)改变当前工作目录 chdir
3)重设当前文件权限 umask
4)关闭文件 close
第二种:
1)重设当前文件权限 umask
2)创建子进程 fork
3)让父进程结束
4)创建新会话 setsid
5)忽略一些信号 主要忽略SIGCHLD、SIGHUP
6)改变当前工作目录 chdir
7)重定向文件描述符号 dup2 为了屏蔽终端干扰
进程间通信:
广义:进程间数据交换(传输)
借助:
文件
文件映射虚拟内存(可以在父子进程之间进行通信)
管道
信号
狭义:IPC
借助:
共享内存
消息队列
旗语(信号量)
父子进程间通信:
变量:父子进程都拥有同名变量,但是不共用
堆内存:父子进程都有堆内存,但是不共用
mmap:父子进程共享同一个mmap映射出来的内存
相关示例
#include <stdio.h>
#include <unistd.h> //linux操作系统标准头文件
#include <string.h> //memcpy
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> //umask
#include <time.h>
#include <sys/time.h>
#include <dirent.h> //遍历目录
#include <fcntl.h> //文件映射虚拟内存
#include <sys/mman.h>
#include <wait.h>
#include <signal.h> //SIGCHLD
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <bits/pthreadtypes.h>
#include <aio.h>
// exec进程
void _exec(int argc, char *argv[])
{
printf("exec启动\n");
execl(argv[1], "启动进程名%s", argv[1]);
}
// fork创建子进程
void _fork()
{
printf("当前进程id: %u\n", getpid());
sleep(3);
// 创建子进程
int pid = fork();
printf("被创建子进程id: %u\n", pid);
if (pid) // 父进程
{
while (1)
printf("我是父进程:%u %u\n", getpid(), getppid());
sleep(1);
}
else
{
while (1)
printf("---我是子进程: %u %u\n", getpid(), getppid());
sleep(1);
}
}
// kill结束进程
void _kill(int argc, char *argv[])
{
int pid = atoi(argv[1]);
kill(pid, 9);
printf("给%d进程发送信号9完毕\n", pid);
}
// 父进程等待子进程结束后再继续
void _wait()
{
printf("当前进程id: %u\n", getpid());
sleep(3);
// 创建子进程
int pid = fork();
printf("被创建子进程id: %u\n", pid);
if (pid) // 父进程
{
for (int i = 0; i < 3; i++)
{
printf("我是父进程:%u %u\n", getpid(), getppid());
sleep(1);
}
printf("父进程的事情干完了,正在等待子进程\n");
int status = 114514;
pid_t r = wait(&status);
printf("子进程结束,父进程继续 %d %d\n", r, status);
}
else
{
for (int i = 0; i < 5; i++)
{
printf("---我是子进程: %u %u\n", getpid(), getppid());
sleep(1);
}
printf("子进程结束\n");
return 123;
}
}
// 守护进程
void _daemon()
{
// 1)重设当前文件权限 umask
umask(0);
// 2)创建子进程 fork
int ret = fork();
if (ret < 0)
{
printf("创建进程失败%m\n");
exit(-1);
}
// 3)让父进程结束
if (ret > 0)
{
exit(0);
}
printf("pid:%d\n", getpid());
// 4)创建新会话 setsid
setsid();
// 5)忽略一些信号 主要忽略SIGCHLD、SIGHUP
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
// 6)改变当前工作目录 chdir
chdir("/");
// 7)重定向文件描述符号 dup2 为了屏蔽终端干扰
// /dev/null 是黑洞设备,什么东西往这里一丢就没了
int fd = open("/dev/null", O_RDWR);
dup2(fd, 0);
dup2(fd, 1);
// 模拟守护进程工作
while (1)
{
sleep(1); // 2852
}
}
int main(int argc, char *argv[])
{
// _exec(argc,argv);
// _fork();
// _kill(argc,argv);
// _wait();
// _daemon();
}