1.进程状态
就绪态:这个进程当前所有运行条件就绪,只要得到了CPU时间就能直接运行。
运行态:就绪态时得到了CPU就进入运行态开始运行。
僵尸态:进程已经结束但是父进程还没来得及回收。
等待态(浅度睡眠&深度睡眠):进程在等待某种条件,条件成熟后可进入就绪态。等待态下就算你给他CPU调度进程也无法执行。浅度睡眠等待时进程可以被(信号)唤醒,而深度睡眠等待时不能被唤醒只能等待的条件到了才能结束睡眠状态。
暂停态:暂停并不是进程的终止,只是被被人(信号)暂停了,还可以恢复的。
进程各种状态之间的转换图:
2.守护进程
(1)daemon,表示守护进程,简称为d(进程名后面带d的一般就是守护进程)
(2)长期运行(一般是开机运行直到关机时关闭)
(3)与控制台脱离(普通进程都和运行该进程的控制台相绑定,表现为如果终端被强制关闭了则这个终端中运行的所有进程都被会关闭)
3.编写简单守护进程(任何一个进程都可以将自己实现成守护进程)
create_daemon函数要素
(1)子进程等待父进程退出
(2)子进程使用setsid创建新的会话期,脱离控制台
(3)调用chdir将当前工作目录设置为/
(4)umask设置为0以取消任何文件权限屏蔽
(5)关闭所有文件描述符
(6)将0、1、2定位到/dev/null(类似于回收站)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void create_daemon(void);
int main(void)
{
create_daemon();
while (1)
{
printf("I am running.\n");
sleep(1);
}
return 0;
}
// 函数作用就是把调用该函数的进程变成一个守护进程
void create_daemon(void)
{
pid_t pid = 0;
pid = fork();
if (pid < 0)
{
perror("fork");
exit(-1);
}
if (pid > 0)
{
exit(0); // 父进程直接退出
}
// 执行到这里就是子进程
// setsid将当前进程设置为一个新的会话期session,目的就是让当前进程
// 脱离控制台。
pid = setsid();
if (pid < 0)
{
perror("setsid");
exit(-1);
}
// 将当前进程工作目录设置为根目录
chdir("/");
// umask设置为0确保将来进程有最大的文件操作权限
umask(0);
// 关闭所有文件描述符
// 先要获取当前系统中所允许打开的最大文件描述符数目
int cnt = sysconf(_SC_OPEN_MAX);
int i = 0;
for (i=0; i<cnt; i++)
{
close(i);
}
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
}
4.进程间通信
管道(无名管道)
(1)管道通信的原理:内核维护的一块内存,有读端和写端(管道是单向通信的)
(2)管道通信的方法:父进程创建管道后再fork子进程,子进程继承父进程的管道fd
(3)管道通信的限制:只能在父子进程间通信、半双工
(4)管道通信的函数:pipe、write、read、close
有名管道(fifo)
(1)有名管道的原理:实质也是内核维护的一块内存,表现形式为一个有名字的文件
(2)有名管道的使用方法:固定一个文件名,2个进程分别使用mkfifo创建fifo文件,然后分别open打开获取到fd,然后一个读一个写
(3)管道通信限制:半双工(注意不限父子进程,任意2个进程都可)
(4)管道通信的函数:mkfifo、open、write、read、close
消息队列
(1)本质上是一个队列,队列可以理解为(内核维护的一个)FIFO
(2)工作时A和B2个进程进行通信,A向队列中放入消息,B从队列中读出消息。
信号量
(1)实质就是个计数器(其实就是一个可以用来计数的变量,可以理解为int a)
(2)通过计数值来提供互斥和同步
共享内存
(1)大片内存直接映射
(2)类似于LCD显示时的显存用法
剩余的2类IPC
(1)信号
(2)Unix域套接字 : socket