进程命令 PS
Ps axu(打印所有进程信息)
程序
存放在磁盘上的指令和数据的有序集合(静态的)
进程
执行一个程序所分配的资源的总称
进程是程序的一次执行过程
动态的,包括创建、调度、执行和死亡
进程类型
交互进程:在shell下启动,可以在前台运行,也可以在后台运行
批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行
守护进程:和终端无关,一直在后台运行
进程状态
运行态:进程正在运行,或者准备运行
等待态:进程在等待一个事件的发生或某种系统资源(可中断、不可中断)
停止态:进程被终止,收到信号后可继续运行
死亡态:已终止的进程,但pcb没有被释放
查看进程信息
PS 查看系统进程快照
Top 查看进程动态信息
/proc 查看进程详细信息
Nice 按用户指定的优先级运行进程
Renice 改变正在运行进程的优先级
Jobs 查看后台进程
bg 将挂起的后台进程在后台运行
Fg把后台运行的进程放到前台运行
进程创建-fork
#include<unistd.h>
Pid_t fork(void)
创建新的进程,失败时返回-1
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值区分父进程和子进程
父子进程
子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束
- 子进程成为孤儿进程,被init进程收养
- 子进程变成后台进程
若子进程先结束
父进程如果没有及时回收,子进程变成僵尸进程。
子进程从何处开始运行?
子进程从fork后开始运行,不是从main开始
父子进程谁先执行?
父子进程谁先运行依赖于操作系统调度策略,不确定
父进程能否多次调用fork?子进程呢?
都可以
进程结束-exit/_exit
#include<stdlib.h>
#include<unistd.h>
Void exit(int status);
Void _exit(int status);
结束当前的进程并将status返回
Exit结束进程时会刷新(流)缓冲区
进程回收
子进程结束时由父进程回收
孤儿进程由init进程回收
若没有及时回收会出现僵尸进程
进程回收-wait
#include<unistd.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,哪个先结束就先回收
Status指定保存子进程返回值和结束方式的地址
Status为NULL 表示直接释放子进程PCB,不接受返回值
子进程通过exit/_exit/return返回某个值(0-255)
父进程调用wait(&status)回收
WIFEXITED(status)判断子进程是否正常结束
WEXITSTATUS(status)获取子进程返回值
WIFSIGNALED(status)判断子进程是否被信号结束
WIERMSIG(status)获取结束子进程的信号类型
进程回收-waitpid
#include <unistd.h>
Pid_t waitpid(pid_t pid,int *status,int option);
成功时返回回收的子进程的pid或0;失败时返回EOF
Pid可用于指定回收哪个子进程或任意子进程
Status指定用于保存子进程返回值和结束方式的地址
Option指定回收方式,0或WNOHANG
进程-exec函数族
进程调用exec函数族执行某个程序
进程当前内容被指定的程序替换
实现让父子进程执行不同的程序
- 父进程创建子进程
- 子进程调用exec函数族
- 父进程不受影响
#include<unistd.h>
Int execl(const char *path,const char *arg,...);
Int execlp(const char *file,const char *arg,...);
成功时执行指定的程序;失败时返回EOF
Path执行的程序名称,包含路径
Arg...传递给执行的程序的参数列表
File 执行的程序的名称,在path中查找
#include<unistd.h>
Int execv(const char *path,char *const argv[])
Int execvp(const char *file,char *const argv[]);
成功时执行指定的程序;失败时返回EOF
Arg...封装成指针数组的形式
#include<stdlib.h>
Int system(const char *command);
成功时返回命令command的返回值;失败时返回EOF
当前进程等待command执行结束后才继续执行。
守护进程
守护进程(Daemon)是Linux三种进程类型之一
通常在系统启动时运行,系统关闭时关闭
Linux系统中大量使用,很多服务程序以守护进程形式运行
守护进程特点:
始终在后台运行
独立于任何终端
周期性的执行某种任务或等待处理特定事件
守护进程-会话、控制终端
Linux以会话(session)、进程组的方式管理进程
每个进程属于一个进程组
会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话
终端关闭时,所有相关进程都被结束
创建守护进程(一)
创建子进程,父进程退出
if(fork()>0){
Exit(0);
}
子进程变成孤儿进程,被init进程收养
子进程在后台运行
创建守护进程(二)
子进程创建新会话
If(setsid()<0){
Exit(-1);
}
子进程成为新的会话组长
子进程脱离原先的终端
创建守护进程(三)
更改当前工作目录
Chdir(“/”);
Chdir(“/tmp”);
守护进程一直在后台运行,其工作目录不能被卸载
重新设定当前工作目录cwd
创建守护进程(四)
重设文件权限掩码
If(umask(0)<0){
Exit(-1);
}
文件权限掩码设置为0;
只影响当前进程
创建守护进程(五)
关闭打开的文件描述符
Int i;
For(i=0,i<gettablesize();i++){
Close(i);
}
关闭所有从父进程继承的打开方式
已脱离终端,stdin/stdout/stderr无法再使用
进程
进程有独立的地址空间,Linux为每个进程创建task_struct,每个进程都参与内核调度,互不影响。
线程
进程在切换时系统开销大,很多操作系统引入了轻量级进程LWP,同一进程中的线程共享相同地址空间,Linux不区分进程、线程。
线程特点
通常线程指的是共享相同地址空间的多个任务
使用多线程的好处
大大提高了任务切换的效率,避免了额外的TLB & cache的刷新
线程共享资源
一个进程中的多个线程共享以下资源:
可执行的指令、静态数据、进程中打开的文件描述符、当前工作目录,用户ID、用户组ID
每个线程私有的资源包括:
线程ID(TID)、PC(程序计数器)和相关寄存器、堆栈、错误号(errno)、优先级、执行状态和属性。
Linux线程库
Pthread线程库提供了如下基本操作:
创建线程、回收线程、结束线程
同步和互斥机制
信号量、互斥锁
线程创建-pthread_create
#include<pthread.h>
Int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*routine)(void *),void *arg);
成功返回0,失败时返回错误码
Thread线程对象
Attr 线程属性,NULL 代表默认属性
Routine线程执行的函数
arg 传递给routine的参数 ,参数是void * ,注意传递参数格式
Pthread_t pthread_self(void) 查看自己的TID。
线程回收-pthread_join
#include <pthread.h>
Int pthread_join(pthread_t thread,void **retval);
成功返回0,失败时返回错误码
Thread要回收的线程对象
调用线程阻塞直到thread结束
*retval接受线程thread的返回值
Ps -eLf|grep cthread //查看线程
Ps -ef|grep pthread //查看进程
线程结束-pthread_exit
#include <pthread.h>
Void pthread_exit(void *retval);
结束当前线程
Retval可被其他线程通过pthread_join获取
线程私有资源被释放
线程间通信
线程共享同一进程的地址空间
优点:线程间通信很容易,通过全局变量交换数据
缺点:多个线程访问共享数据时需要同步或互斥机制
线程通信—同步
同步(synchronization)指的时多个任务按照约定的先后次序相互配合完成一件事情
由信号量来决定线程是继续运行还是阻塞等待
信号量(灯)
信号量代表某一类资源,其值代表系统中该资源的数量
信号量是一个受保护的变量,只能通过三种操作来访问
初始化
P操作(申请资源)
V操作(释放资源)
信号量—P/V操作
P(S)含义如下:
if(信号量的值大于0){申请资源的任务继续运行;信号量的值减一:}
Else{申请资源的任务受阻}
V(S)含义如下:
信号量的值加一;
If(有任务在等待资源){唤醒等待的任务,让其继续运行}
线程通信-互斥
临界资源
一次只允许一个任务(进程,线程)访问的共享资源
临界区
访问临界区的代码
互斥机制
Mutex互斥锁
任务访问临界资源前申请锁,访问完后释放锁。
无名管道特点
无名管道具有如下特点:
只能用于具有血缘关系的进程之间的通信
单工的通信模式,具有固定的读端和写端
无名管道创建时会返回两个文件描述符,分别用于读写管道
有名管道特点:
对应管道文件,可用于任意进程之间进行通信
打开管道时可指定读写方式
通过文件IO操作,内容存放在内存中。
信号机制
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件
Linux对早期的unix信号机制进行了扩展
进程对信号有不同的响应方式
缺省方式
忽略信号
捕捉信号
信号相关命令kill/killall
Kill[-signal]pid
默认发送SIGTERM
-sig可指定信号
Pid 指定发送对象
Killall[-u user|prog]
prog 指定进程名
User 指定用户名
SIGHUP(该信号在用户终端关闭时产生,通常是发给和该终端关联的会话内的所有进程)
SIGINT(该信号在用户键入INTR字符(Ctrl-C)时产生,内核发送此信号送到当前终端的所有前台进程)
SIGQUIT(该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来产生)
SIGILL(该信号在一个进程企图执行一条非法指令时产生)
SIGSEV(该信号在非法访问内存时产生,如野指针、缓冲区溢出)
SIGPIPE(当进程往一个没有读端的管道中写入时产生,代表“管道断裂”)
SIGKILL(该信号用来结束进程,并且不能被捕捉和忽略)
SIGSTOP(该信号用于暂停进程,并且不能被捕捉和忽略)
SIGTSTP(该信号用于暂停进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号)
SIGCONT(该信号让进程进入运行态)
SIGALRM(该信号用于通知进程定时器时间已到)
SIGUSR1/2(该信号保留给用户程序使用)
System V IPC
IPC对象包括:共享内存、消息队列和信号灯集
每个IPC对象有唯一的ID
IPC对象创建后一直存在,直到被显式的删除
每个IPC对象有一个关联的KEY
Ipcs/ipcrm
共享内存
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
由于多个进程可同时访问共享内存,因此需要同步和异步机制配合使用。
共享内存使用步骤:
创建/打开共享内存
映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
读写共享内存
撤销共享内存映射
删除共享内存映射
消息队列
消息队列是System V IPC对象的一种
消息队列由消息队列ID来唯一标识
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列可以按照类型来发送/接收消息
消息队列使用步骤
打开/创建消息队列 msgget
向消息队列发送消息 msgsnd
从消息队列接收消息 msgrcv
控制消息队列 msgctl
消息格式
通信双方首先定义好统一的消息格式。
用户根据应用需求定义结构体类型。
首成员类型必须为long,代表消息类型(正整数)。
其他成员都属于消息正文。
消息长度不包括首类型long。
System V IPC-信号灯
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
Posix无名信号灯
Posix有名信号灯
System V 信号灯
信号灯的含义
计数信号灯
System V IPC
System V信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
System V信号灯使用步骤
打开/创建信号灯 semget
信号灯初始化 semctl
P/V操作 semop
删除信号灯 semctl