多进程的编程

程序是静态的,而进程是动态的,运行的程序的叫做进程

进程具有动态性,并发性,独立性,异步性。

进程的三态,有就绪态,执行态,阻塞态。

进程的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所指向数组的元素个数

  

猜你喜欢

转载自blog.csdn.net/qq_39372207/article/details/82347867