欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多进程的编程

程序员文章站 2022-06-19 13:26:21
...

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

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

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

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