进程的由来
程序进程查看进程之间的关系操作系统如何区分进程 创建一个新进程
fork函数fork函数特性fork函数要点总结 子进程的偷梁换柱
exec函数族要点总结 进程的退出
正常退出:exit() 和 _exit() 退出函数exit() 和 _exit() 两者的区别 等待子进程的终结
wait函数处理子进程退出状态值的宏 进程的生老病死
进程状态: 进程组、会话、终端
进程组进程组的诞生会话会话的诞生前台进程组后台进程组终端 守护进程
守护进程如何来写一个守护进程 PS命令详解
ps auxps axjf使用场景 僵尸进程和托孤进程
僵尸进程托孤进程 什么是进程间通信(ipc)
进程间通信Linux系统下的ipc 无名管道
pipe 函数特点使用步骤 有名管道
mkfifo函数特点使用步骤 信号简介
信号的基本概念怎么产生信号?信号的处理方式 常用信号分析signal_kill_raise 函数
signal函数kill 函数raise函数 信号集处理函数
屏蔽信号集未处理信号集 system-V 消息队列
system-V ipc特点消息队列用法ftok函数msgget函数msgsnd 函数msgrcv函数msgctl 函数 system-V 信号量
本质作用semget 函数semctl函数semop system-V 共享内存
作用共享内存用法shmget函数shmat函数shmdt 函数shmctl函数 进程的由来 程序
静态文件
进程运行着的实体
查看进程之间的关系pstree
操作系统如何区分进程PID:进程的身份证
创建一个新进程 fork函数头文件:
#include
函数原型:
pid_t fork(void);
返回值:
fork函数特性成功:0或其他正整数
失败:-1
执行fork函数之后,会返回两次在旧进程中返回,返回值为0在新进程返回时,返回值为进程的pid
所以叫做复制一个进程更加贴切
fork函数要点总结在执行fork函数之前,操作系统只有一个进程,fork函数之前的代码只会被执行一次。
在执行fork函数之后,操作系统有两个几乎一样的进程,fork函数之后的代码会被执行两次。
子进程的偷梁换柱 exec函数族常用后缀:
l: 代表以列表形式传参
v:代表以矢量数据形式传参
p:代表使用环境变量Path来寻找指定执行文件
e:代表用户提供自定义的环境变量
头文件:
#include
函数原型:
int execl(const char *path,const char *arg, …)
int execlp(const char *file, const char *arg, …)
int execv(const char *path,char *const argv[])
int execve(const char *path, char *const argv[], char *const envp[])
返回值
要点总结成功:不返回
失败:-1
l 后缀和 v 后缀必须两者选其一来使用p 后缀和 e 后缀是可选的,可用可不用组合后缀的相关函数还有很多,可自己进一步了解
exce函数有可能执行失败,需要预防
新程序的文件路劲出错传参或者是自定义环境变量时,后面没有加 NULL新程序没有执行权限 进程的退出 正常退出:
从main 函数 return调用 exit() 函数终止调用 _exit() 函数终止 exit() 和 _exit() 退出函数
头文件:
#include
#include
原型:
void _exit(int status);
void exit(int status);
返回值:
exit() 和 _exit() 两者的区别不返回
等待子进程的终结 wait函数_exit() 直接退出
exit() 将文本缓存处理完才退出
头文件
#include
函数原型
pid_t wait(int *status)
返回值
处理子进程退出状态值的宏成功:退出的子进程的pid
失败:-1
WIFEXITED(status):如果子进程正常退出,则宏为真WEXITSTATUS(status):如果子进程正常退出,则该宏获取子进程的退出值 进程的生老病死 进程状态:
TASK_RUNNING:就绪/运行状态TASK_INTERRUPTIBLE:可中断睡眠状态TASK_UNINTERRUPNBLE:不可中断睡眠状态TASK_TRACED:调试态TASK_STOPPED:暂停状态EXIT_ZOMBIE:僵死状态EXIT_DEAD:死亡态
进程组、会话、终端 进程组
作用:对相同类型的进程进行管理
进程组的诞生在 shell 里面直接执行一个应用程序,对于大部分进程来说,自己就是进程的首进程,进程组只有一个进程如果进程调用了 fork 函数,那么父子进程同属一个进程组,父进程为首进程在 shell 中通过管道执行连接起来的应用程序,两个程序同属一个进程组,第一个程序为进程组的首进程
进程组 id :pgid,由首进程 pid 决定
会话作用:管理进程组
会话的诞生调用 setsid 函数,新建一个会话,应用程序作为会话的第一个进程,称为会话首进程用户在终端正确登陆之后,启动 shell 时 linux 系统会创建一个新的会话,shell 进程作为会话首进程
会话 id:会话首进程 id,SID
前台进程组shell 进程启动时,默认是前台进程组的首进程。前台进程组的首进程会占用会话所关联的终端来运行,shell 启动其他应用程序时,其他程序成为首进程 后台进程组
后台进程中的程序是不会占用终端在 shell 进程里启动程序时,加上 & 符号可以指定程序运行在后台进程组里面
终端ctrl + z:可以将程序运行在后台进程组中
jobs:查看有哪些后台进程组
fg+job id:可以把后台进程组切换为前台进程组
物理终端
串口终端lcd 终端 伪终端
ssh远程连接产生的终端桌面系统启动的终端 虚拟终端
Linux 内核自带的,ctrl +alt+f0~f6可以打开7个虚拟终端 守护进程
会话用来管理的后台进程组
会话一般关联着一个终端
当终端被关闭了之后,会话中的所有进程都会被关掉
不受终端影响,就算终端退出,也可以继续在后台运行
如何来写一个守护进程步骤:
创建一个子进程,父进程直接退出方法:通过 fork() 函数创建一个新的会话,摆脱终端的影响
方法:通过 setsid() 函数改变守护进程的当前工作目录,改为 “/”
方法:通过 chrdir() 函数重设文件权限掩码
新建文件的权限受文件权限掩码影响
uamsk:022,000010010,只写
新建文件默认执行权限:666, 110110110
真正的文件执行权限:666&~umask
方法:通过 umask() 函数关闭不需要的文件描述符
0,1,2:标准输入、输出、出错
方法:通过 close() 函数
普通进程伪装成守护进程:
nohup
aux
axjf
a: 显示一个终端所有的进程u:显示进程的归属用户及内存使用情况x:显示没有关联控制终端的进程j:显示进程归属的进程 id,会话 id,父进程 idf:以 ascii 形式显示出进程的层次关系 ps aux
user:进程是哪个用户产生的pid:进程的身份证号码%cpu:表示进程占用了 cpu 计算能力的百分比%mem:表示进程占用了系统内存的百分比vsz:进程使用的虚拟内存大小rss:进程使用的物理内存大小tty:表示进程关联的终端stat:表示进程当前状态start:表示进程的启动时间time:记录进程的运行时间command:表示进程执行的具体程序 ps axjf
ppid:表示进程的父进程idpid:进程的身份证号码pgid:进程所在进程组的idsid:进程所在会话的idtty:表示进程关联的终端tpgid:值位-1,表示进程为守护进程stat:表示进程当前状态uid:启动进程的用户idtime:记录进程的运行时间command:表示进程的层次关系 使用场景
关注进程本身:ps aux
关注进程间的关系:ps axjf
进程的正常退出步骤:
子进程调用 exit() 函数退出父进程调用 wait() 函数为子进程处理其他事情 僵尸进程
子进程退出后,父进程没有调用 wait() 函数处理身后事,子进程变成僵尸进程
托孤进程父进程比子进程先退出,子进程变为孤儿进程,Linux系统会把子进程托孤给 1 号进程( init 进程)
什么是进程间通信(ipc) 进程间通信数据传输资源共享事件通知进程控制 Linux系统下的ipc
早期 unix 系统 ipc
管道信号fifo system-v ipc(贝尔实验室)
system-v 消息队列system-v 信号量system-v 共享内存 socket ipc(BSD)posix ipc(IEEE)
posix 消息队列posix 信号量posix 共享内存 无名管道 pipe 函数
头文件:
#include
函数原型:
int pipe(int pipefd[2]);
返回值:
特点成功:0
失败:-1
特殊文件(没有名字),无法使用 open 函数打开,但是可以使用 close 函数。只能通过子进程继承文件描述符的形式来使用write 和 read 操作可能会阻塞进程所有文件描述符被关闭之后,无名管道被销毁 使用步骤
父进程 pipe 无名管道fork 子进程close 无用端口write/read 读写端口close 读写端口 有名管道 mkfifo函数
头文件:
#include
#include
函数原型:
int mkfifo(const char *filename,mode_t mode)
返回值:
特点成功:0
失败:-1
有文件名,可以使用 open 函数打开任意进程间数据传输write 和 read 操作可能会阻塞进程write 具有“原子性” 使用步骤
第一个进程 mkfifo 有名管道open 有名管道,write/read 数据close 有名管道第二个进程 open 有名管道,read/ write 数据close 有名管道 信号简介 信号的基本概念
软件模拟中断,进程接受信号后,做出相应响应
怎么产生信号?硬件
执行非法指令访问非法内存驱动程序…… 软件
控制台:
ctrl+c:中断信号ctrl+c:退出信号ctrl+c:停止信号 kill 命令程序调用 kill() 函数 信号的处理方式
忽略:进程当信号从来没有发生过捕获:进程会调用相应的处理函数,进行相应的处理默认:使用系统默认处理方式来处理信号 常用信号分析 信号名 信号编号 产生原因 默认处理方式 SIGHUP1关闭终端终止SIGINT2ctrl+c终止SIGQUIT3ctrl+终止+转储SIGABRT6abort()终止+转储SIGPE8算术错误终止SIGKILL9kill -9 pid终止,不可捕获/忽略SIGUSR110自定义忽略SIGSEGV11段错误终止+转储SIGUSR212自定义忽略SIGALRM14alarm()终止SIGTERM15kill pid终止SIGCHLD17(子)状态变化忽略SIGTOP19ctrl+z暂停,不可捕获/忽略
pkill 命令:制定杀死某一类进程
signal_kill_raise 函数 signal函数头文件:
#include
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int aignum, sighandler_t handler);
参数:
signum:要设置的信号handler:
SIG_IGN:忽略SIG_DFL:默认void (*sighandler_t)(int):自定义
返回值:
kill 函数成功:上一次设置的handler
失败:SIG_ERR
头文件:
#include
#include
原型函数:
int kill(pid_t pid,int sig)
参数:
pid:进程idsig:要发送的信号
返回值:
raise函数成功:0
失败:-1
头文件:
#include
函数原型:
int raise(int sig);
参数:
sig:发送信号
函数返回值:
信号集处理函数 屏蔽信号集成功:0
失败:非0
屏蔽某些信号
手动自动 未处理信号集
信号如果被屏蔽,则记录在未处理信号集中
非实时信号(1-31),不排队,只留一个实时信号(34-64),排队,保留全部
信号集相关API
int sigemptyset(sigset_t *set);
将信号集合初始化为 0 int sigfillset(sigset_t *set);
将信号集合初始化为 1 int sigaddset(sigset_t *set, int signum);
将信号集合某一位设置为 1 int sigdelset(sigset_t *set, int signum);
将信号集合某一位设置为 0 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
使用设置好的信号集去修改信号屏蔽集参数 how:
SIG_BLOCK:屏蔽某个信号(屏蔽集 | set)SIG_UNBLOCK:打开某个信号(屏蔽集 & (~set))SIG_SETMASK:屏蔽集 = set 参数 oldset:保存旧的屏蔽集的值,NULL表示不保存 system-V 消息队列 system-V ipc特点
独立于进程没有文件名和文件描述符IPC对象有 key 和 ID 消息队列用法
定义一个唯一 key (ftok)构造消息对象(msgget)发送特定类型消息(msgsnd)接受特定类型消息(msgrcv)删除消息队列(msgctl) ftok函数
功能:获取一个 key
函数原型:
key_t ftok(const char *path, int proj_id)
参数:
path:一个合法路径proj_id:一个整数
返回值:
成功:合法键值
失败:-1
功能:获取消息队列ID
函数原型:
int msgget(key_t key, int msgflg)
参数:
key:消息队列的键值msgflg:
IPC_CREAT:如果消息队列不存在,则创建mode:访问权限
返回值:
成功:该消息队列的ID
失败:-1
功能:发送消息到消息队列
函数原型:
int msgsnd(int msgid, const void *msgp, size_t msgsz,int msgflg);
参数:
msqid:消息队列IDmsgp:消息缓存区
struct msgbuf
{
long mtype; //消息标识
char mtext[1]; //消息内容
} msgsz:消息正文的字节数msgflg:
IPC_NOWAIT:非阻塞发送0:阻塞发送
返回值:
成功:0
失败:-1
功能:从消息队列读取消息
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
参数:
msqid:消息队列 IDmsgp:消息缓存区msgsz:消息正文的字节数msgtyp:要接受消息的标识msgflg:
IPC_NOWAIT:非阻塞读取MSG_NOERROR:截断消息0:阻塞读取
返回值:
成功:0
失败:-1 msgctl 函数
功能:设置或获取消息队列的相关属性
函数原型:
int msgctl(int msgqid,int cmd,struct maqid_ds *buf)
msgqid:消息队列的IDcmd
IPC_STAT:获取消息队列的属性信息IPC_SET:设置消息队列的属性IPC_RMID:删除消息队列 buf:相关结构体缓冲区 system-V 信号量 本质
计数器
作用保护共享资源
互斥同步
信号量用法
定义一个唯一 key (fork)构造一个信号量(semget)初始化信号量(semctl SETVA)对信号量进行 P/V 操作(semop)删除信号量(semctl RMID) semget 函数
功能:获取信号量的ID
函数原型:
int semget(key_T key,int nsems,int semflg)
参数:
key:信号量键值nsems:信号量数量semflg:
IPC_CREATE:信号量不存在则创建mode:信号量的权限
返回值:
成功:信号量ID
失败:-1
功能:获取或设置信号量的相关属性
函数原型:
int semctl(int semid, int semnum, int cmd,union semun arg)
参数:
semid:信号量IDsemnum:信号量编号cmd:
IPC_STAT:获取信号量的属性信息IPC_SET:设置信号量的属性IPC_RMID:删除信号量IPC_SETVAL:设置信号量的值 arg:
union semun
{
int val;
struct semid_ds *buf;
}
返回值:
成功:由 cmd 类型决定
失败:-1
函数原型:
int semop(int semid,struct sembuf *sops,size_t nsops)
参数:
semid:信号量IDsops:信号量操作结构体数组
struct sembuf
{
short sem_num; //信号量编号
short sem_op; //信号量P/V操作
short sem_flg; //信号量行为,SEM_UNDO
}nsops:信号量数量
返回值:
成功:0
失败:-1
高效率传输大量数据
共享内存用法定义一个唯一key(fork)构造一个共享内存对象(shmget)共享内存映射(shmat)解除共享内存映射(shmdt)删除共享内存(shmctl RMID) shmget函数
功能:获取共享内存对象的ID
函数原型:
int shmget(key_t key, int size, int shmflg)
参数:
key:共享对象键值nsems:共享内存大小shmflg:
IPC_CREATE:共享内存不存在则创建mode:共享内存的权限
返回值:
成功:共享内存ID
失败:-1
功能:映射共享内存
函数原型:
int shmat(int shmid, const void *shmaddr, int shmflg)
参数:
shmid:共享内存IDshmaddr:映射地址,NULL为自动分配shmflg:
SHM_RDONLY:只读方式映射0:可读可写
返回值:
成功:共享内存首地址
失败:-1
功能:解除共享内存映射
函数原型:
int shmdt(const void *shmaddr)
参数:
shmaddr:映射地址
返回值:
成功:0
失败:-1
功能:获取或设置共享内存的相关属性
函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数:
shmid:共享内存IDcmd:
IPC_STAT:获取共享内存的属性信息IPC_SET:设置共享内存的属性IPC_RMID:删除共享内存 buf:属性缓冲区
返回值:
成功:由 cmd 类型决定
失败:-1