多进程的编程
程序是静态的,而进程是动态的,运行的程序的叫做进程
进程具有动态性,并发性,独立性,异步性。
进程的三态,有就绪态,执行态,阻塞态。
进程的ID(PID):表示进程的唯一数字
父进程的ID(PPID):
启动进程的用户ID(UID)
进程互斥,若干进程都要使用同一共享资源,任何时刻最多只能一个进程访问
只能有一个进程访问的资源叫做临界资源
控制访问临界资源的代码叫做临界区
一组并发进程按一定顺序执行的过程叫做进程间的同步,具有同步关系的一组并发进程成为合作进程,
合作进程间的互相发送消息的信号称为消息或事件,
进程的调度,按照一定的算法,从一组待运行的进程中选出一个来占有cpu运行,
分为抢占式调度和非抢占式调度;
都有先来先服务调度算法,还有短进程优先算法,高优先级优先调度算法,时间片轮转法,
死锁,多个进程因竞争资源而形成一种僵局,这些进程都不能向前继续推进,
进程控制的编程
#include<sys/types.h>
#include<unistd.h>
获取本进程ID,pid_t getpid(void)
获取父进程的ID pid_t getppid(void)
创建子进程 pid_t fork(void)
父子进程的执行顺序不确定
在父进程中,返回新创建子进程的PID,在子进程中,返回值为0,出现错误返回负值
子进程的数据空间和堆栈空间都会从父进程得到拷贝,里面的数据不会受到父进程的影响
创建子进程 pid_t vfork(void)
子进程与父进程共享数据段
子进程先运行,父进程后运行
exec函数族
exec启动一个新程序,替换原有的进程,进程的ID号并没有变化,
int execl(const char* path,const char* arg1,...)
path :被执行的程序名(有完整的路径)
arg1到arg2:被执行程序所需要的命令行参数,含程序名,以null指针结尾;
execl("/bin/ls","ls","-al","/etc/paswd",(char *)0)
int execlp(const char* path,const char* arg1,...)
path:被执行程序名,(不含有路径,将从path路径下去寻找)
int execv (const char*path,char*const argv[ ])
argv[ ]被执行程序所需要的参数数组
int system(const char*string)
调用fork产生子进程,由子进程来调用/bin/sh -c string 来执行参数string所代表的命令
进程的等待
#include<sys/wait.h>
pid_t wait (int *status) 阻塞该进程,直到某个子进程退出;
进程间的通信
分为管道通信,信号通讯,共享内存三种通信方式
常用的通信方式
1管道通信(pipe)和有名管道(FIFO)
2信号
3消息队列
4共享内存
5信号量
6套接字(socket)
管道通信
管道是单向的,先进先出的,他把一个进程的输入和另一个进程的输出连接在一起的,
一个进程在管道的尾部写入数据,在管道的头部读入数据。
管道分为无名管道和有名管道,无名管道用于父进程和子进程的通信,有名管道可用于同一系统的任意两个进程
无名管道
无名管道由int pipe(int filedis[2]),当一个管道建立时,他会创建两个文件描述符,filedis[0]用于读管道,filedis[1]用于写管道
关闭管道只需要将这两个文件描述符关闭,可以用close函数逐个关闭;
通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道,
命名管道
int mkfifo(const char* pathname,mode_t mode)
pathname:FIFO文件名
mode:属性(可以用read write close)
非阻塞标志(O_NONBLOCK)
没有使用非阻塞标志,访问无法满足需求时,进程阻塞,访问空的管道导致进程阻塞
使用,访问无法满足要求时,立即出错返回。
消息队列
消息队列就是一个消息的链表,具有特定的格式
主要有两种:POSIX消息队列和系统V消息队列
系统V消息队列只有在内核重启或者被人工删除时,该消息队列才会被删除;
消息队列在系统范围内对应唯一的键值,要获得一个消息队列的描述字,必须提供该消息队列的键值
key_t ftok(char*pathname,char proj) 返回文件名对应的键值
pathname 文件名 proj 项目名(不为0即可)
int msgget(key_t key,int msgflg) 返回与键值key对应的消息队列的描述字,
key:键值,ftok获得 msgflg 标志位
标志位有以下几种:IPC_CREAT 创建一个新的消息队列
IPC_EXCL 与IPC_CREAT 一同使用,表示如果创建的消息队列如果存在,返回错误
IPC_NOWAIT 读写消息队列无法得到满足时,不阻塞
创建一个新的消息队列:
如果没有与键值相对应的消息队列,并且在msgflg中包含了IPC_CREAT标志位
key参数为IPC_PRIVATE也可以创建
发送消息
int msgsnd(int maqid,struct msgbuf*msgp,int msgsz,int magflg)
向对列中发送一条消息
msqid :已打开的的消息队列的id
msgp:存放消息队列的结构
msgsz:消息数据的长度
msgflg: 标志位,有意义的是IPC_NOWAIT
消息的格式:
struct msgbuf
{
long mtype;//消息类型>0
char mtext;//消息数据的首地址
}
接受消息:
int msgrcv(int msqid,stuct msgbuf*msgp,int msgsz,long msgtyp,int msgflag)
从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构中,在成功读取了一条消息以后,队列中的这条消息将被删除。
信号通讯
进程可以使用kill函数将一个信号发送给另一个进程,用户可以用kill命令将信号发送给其他进程
信号的类型有很多,下面实际中常见的信号:
SIGHUP :从终端上发出的结束信号
SIGINT:来自键盘的中断信号
SIGKILL:该信号结束接受进程的信号
SIGTERM:kill命令发出的信号
SIGCHLD:标识子进程停止或结束的信号
SIGSTOP:来自键盘(ctrl_z)或者调试程序的停止执行信号
信号处理的三种方式
1.当收到某种信号时,忽略信号,但有两种不能被忽略,(SIGKILL和SIGSTOP),他们向用户提供了一种终止或停止进程的办法,
2.执行用户希望的动作,在某种信号发生时,调用一个函数处理
3.执行系统的默认动作,默认动作是终止该进程
信号发送
kill既可以给自身发送信号,也可以向其他进程发送信号,与kill函数不同的时,raise函数是向进程自身发送信号,
int kill(pid_t pid,int signo)
pid>0 将信号发给进程id为pid的进程 pid==0发给同组的进程
pid<0 将信号发送给其他进程id等于pid绝对值的进程 pid==-1 将信号发送给所有的进程
int raise(int signo)
alarm函数 unsigned int alarm(unsigned int seconds)
经过了指定的seconds秒之后产生SIGALRM信号发送给自己
pause 函数 int pause(void)
只有执行了一个信号处理函数后,挂起才结束。
信号的处理
当系统捕捉到某个信号时,可以忽略该信号或者是使用指定的处理函数来处理该信号,或者是使用系统的默认方式,
信号处理的方式有两种:用简单的signal函数或者信号集函数组
void(*signal(int signo,void(*func)(int)))(int)
返回值是函数指针
func的值可能是:SIG_IGN:忽略此信号 SIG_DFL:按系统默认方式处理 信号处理函数名:使用该函数处理
共享内存
实现共享内存分为两个步骤:
1.创建共享内存,使用shmget函数
int shmget(key_t key,int size,int shmflg)如果成功返回内存标识符,如果失败,返回-1
key标识共享内存的键值:0/IPC_PRIVATE.key为IPC_PRIVATE时,则创建一块新的共享内存,如果为0,而参数shmflag有设置为IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。size表示共享内存的大小。
2.映射共享内存,将这段创建的共享内存映射到具体的进程中去,使用shmat函数,
int shmget(int shmid,char*shmaddr,int flag) 如果成功,返回共享内存映射到进程中的地址,失败,返回-1
shmid :shmget函数返回共享存储标识符
flag:决定以什么方式映射,通常为0
当一个进程不需要共享内存时,需要把它从进程地址空间中脱离出来。
int shmdt(char * shmaddr)
信号量
信号量又名信号灯,主要用途是保护临界资源,进程可以根据他判断是否可以访问某些共享资源,还可用于进程同步。
主要分为二值信号灯和计数信号灯
二值信号灯只能取值为0或1,只要共享资源可用,其他进程同样可以修改信号灯的值
计数信号灯的值可以取任意非负值,表示可以多可进程访问
int semget(key_t key,int nsems,int semflag) 打开或创建一个信号量集合
key 键值,由ftok获得
nsems 指定打开或者新创建的信号灯集中将包含信号灯的数目,
semflg 标识,同消息队列
int semop(int 色眯的,struct sembuf*sops,unsigned nsops) 对信号量进行控制
semid 信号量集的ID
sops 是一个操作数组,表明要进行什么操作
struct sembuf
{
unsigned short sem_num;//你现在操作的信号量在这个信号集中是第几个
short sem_op;//你到底获取信号量还是释放一个信号量
short sem_flg; //标志
}
nsops:sops所指向数组的元素个数
上一篇: 多进程编程
下一篇: xv6操作系统源码阅读之锁