进程
进程:启动的程序
程序:二进制文件,占用的内存资源
并发:指的是多个事情在同一时间段同时发生了(多个任务之间互相抢占资源)
并行:指的是多个事情在同一时间点同时发生了(多个任务之间不互相抢占资源)
进程id:系统中每个进程都有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数
进程状态:就绪、运行、挂起、停止
1.fork函数
函数原型:pid_t fork(void)
返回值: =0 父进程
>0 子进程
-1 error
父子进程的执行顺序不一定(抢占资源)
父子进程的创建
父进程的结束然后才是子进程的开始
2.ps和kill指令
ps:查看进程信息
a:显示现行终端下的所有程序
u:以用户为主的格式显示程序状况
x:显示所有程序
kill:向指定进程发送信息
ps aux|grep a.out 4482
kill -9 4482
grep+可执行程序:查找相关程序
各个进程的地址空间中的数据是完全独立的
对于同一个变量读时共享,写时分别在物理地址上拷贝一份变量单独读写
父子进程间不可以通过全局变量进行通信(原因:两个进程内存不能共享)
i = 400
i = 200
3.exec函数
(1)execl函数:加载一个进程, 通过 路径+程序名 来加载。
int execl(const char *path, const char *arg, …);
执行指定路径(不常用)
(2)execlp函数: 加载一个进程,借助PATH环境变量.
int execlp(const char *file, const char *arg1, …);
file:要加载的程序的名字
arg1:命令的第一个单词
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid = fork();//创建子进程
if (pid > 0)
{
//执行ls -l 命令
execlp("ls", "ls", "-al",NULL);
}
else if (pid == 0)
{
printf("My parent process id = %d\n", getpid());
}
return 0;
}
4.孤儿进程和僵尸进程
僵尸进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。(Ctrl+c杀不死)
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。
孤儿进程被init进程所收养
只有杀死父进程才会重新回收子进程,直接杀僵尸进程是杀不死的
5.进程回收
(1)wait函数:
· 阻塞并等待子进程退出;
· 回收子进程残留资源
· 获取子进程结束状态
函数原型:pid_t wait (int* status)
1.WIFEXITED(status):为非零,进程正常结束;
WEXISTATUS(status)如上宏为真,使用此宏获取进程退出状态的参数
2.WIFSIGNALED(status):为非零,进程异常退出
WTERMSIG(status)如上宏为真,使用此宏获取进程退出状态的参数
(2)waitpid函数:同wait
pid_t waitpid(pid_t pid,int *status,int options);
6.进程退出
三个终止函数:
void exit(int status);
void _Exit(int status);
void _exit(int status);
exit()函数_exit()函数都是用来终止进程的,exit()是一个标准 C 库函数,而_exit()和_Exit()是系统调用。_exit()和_Exit()两者等价
是用break来结束子进程会导致子进程的结束不会告诉父进程,导致一些值混乱,最好是用exit
7.进程间通信
进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走
IPC
管道:调用pipe系统函数即可创建。
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
④ 只能在有公共祖先的进程间使用管道。
查看管道缓冲区大小:
· ulimit -a命令
· fpathconf函数(管道fd[0],int name)
· pathconf()查看man fpathconf
int pipe(int fd[2])
fd-传出参数:
fd[0]:读
fd[1]:写
返回值:
0:成功
1:失败
管道的建立
实现父子间进程通信: 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
有名管道的进程间通信
int mkfifo(const char *pathname, mode_t mode);
消息队列
是消息的链表,存放内核,一个消息队列有一个标识符来标识
不移动按顺序来读,也可按消息的类型读取。
ipcs -q :查看当前的消息队列
消息队列的发送
接收
内容被读走了所以used-byte = 0;
消息队列多个进程间通信
写:
读:
消息队列进程进行全双工通信
服务端
客户端
8.共享内存
ipcs -m : 查看共享内存
ipcrm -m : 删除
(1)创建共享内存:int shmget (key_t key,size_t size,int shmflg)
· shmget会根据key值创建一个共享内存,当创建多个共享内存时,每一个key值唯一
· 该参数用于确定共享内存大小。
· shmflg : IPC_CREAT | 权限 IPC_CREAT | IPC_EXCL | 权限
返回值;shmid
共享内存的创建
(2)连接 void *shmat(int shmid,const void *shmidaddr,int shmflg)
shmaddr用于确定将共享内存挂在进程虚拟地址哪个位置,一般填NULL代表让内核自己确定位置。
shmflg用于确定挂接方式,一般填0。
(3)分离 int shmdt(const void *shmidaddr)
(4) 销毁 shmctl (int shmid,int cmd,struct shmid_ds *buf)
struct shmid_ds *buf一般填NULL
(5) key_t ftok(“路径”,int project_id)
ftok根据路径名,提取文件信息,再根据这些文件信息及project ID合成key
pathname 是目录还是必须存在的文件的具体路径。
proj-id取值在0~255(unix)
shmat把共享内存映射到当前进程的地址空间
管道读取内存只能读取一次,消息队列、共享内存读取多次后依然存在
//将进程里的地址映射删除
ipcrm -m 32792:删除共享内存
共享内存进程间通信
write
read
read进程shmid不需用 IPC_CREAT|0777 来创建
9.信号
信号通信:只能是内核向用户空间进程发送信息。
kill -l :可以查看已存在的信号
(1)信号的处理
unsigned int alarm(unsigned int seconds);
· 函数说明:alarm()用来设置信号SIGALRM 在经过参数seconds 指定的秒数后传送给目前的进程. 如果参数seconds 为0, 则之前设置的闹钟会被取消, 并将剩下的时间返回.
· 返回值:返回之前闹钟的剩余秒数, 如果之前未设闹钟则返回0.
void (*signal(int sig, void (*func)(int))) (int)
第一个参数sig:指定信号;
第二个参数是函数指针,是我们自己写的处理函数,这个函数的返回值是函数指针。
返回值:成功,返回之前的信号处理方式
失败,-1.
自定义的信号处理函数指针
(2)信号父子间进程通信
(3)信号灯(信号量的集合)
得到一个信号量集标识符或创建一个信号量集对象:semget函数
int semget(key_t key, int nsems, int semflg)
key : 0(IPC_PRIVATE):会建立新信号量集对象
nsems :创建信号量集中信号量的个数,该参数只在创建信号量集时有效
int semctl(int semid, int semnum, int cmd, union semun arg)
semnum:信号灯编号
cmd: 如下
GETVAL | 返回信号量集合内单个信号量的值 |
SETVAL | 设置信号量集合中单个信号量的值 |
IPC_RMID | 从内核中删除信号量集合 |
(4)信号灯PV操作
semop(完成对信号量的P操作或V操作)
int semop(int semid, struct sembuf *sops, unsigned nsops)
semid:信号量集标识符
sops是一个指向结构体数组的指针
struct senbuf
{
unsigned short sem_num;//信号灯编号
short sem_op ;//对该信号的操作,P操作:1,V操作:-1
short sem_flg;//0 阻塞;1 非阻塞
}
nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
PV操作: