进程间通信--让多个进程的推进合理有序

进程同步----让多个进程的推进合理有序

信号与信号量

  • 当多个进程同时进行事,可能会发生多个进程同时对同一个区域进行操作,比如进程A和进程B同时对一块它们都可以进行访问的内存C进行写入操作,那么当进程A在区域C执行写入操作之后,进程B继续执行写入操作,导致进程A写入的数据遗失。

  • 解决进程同步问题的方法一-------信号

    要解决同步问题,首先就要让进程知道自己在什么时候该停下来(sleep),什么时候该运行(wake_up),这里引入生产者–消费者问题,生产者从磁盘中读取数据放到共享缓冲区中,消费中将共享缓冲区中的内容拿出传给客户。

#define buffer_size 8 //共享缓冲区的数量
int empty; //发送给生产者缓冲区的空闲数量信号
int full;  //发送给消费者缓冲区有内容单元的数量信号
int counter; //数据项目

void Producer() //生产者;
{
 	while(true)
    {
        if(counter==buffer_size)
        {
            sleep(empty); //缓冲区满,生产者睡眠;
        }
        item=produce_item();//生产数据项目;
        insert_item(); //将生产号的数据项目放入缓冲区;
        counter++;
        if(counter==1)
        {
            wake_up(Consumer); //缓冲区不为空,唤醒消费者;
        }
    }
}
void Consumer() //消费者;
{
    while(true)
    {
        if(counter==0)
        {
            sleep(full); //缓冲区空,消费者睡眠;
        }
        remove_item(); //消费一个数据项目;
        counter--;
        if(counter==buffer_size-1)
        {
            wake_up(Producer); //缓冲区还有空闲资源,唤醒生产者;
        }
	}
}

但是设置信号并不会解决所有问题,设想一下,当有两个生产者P1,P2,一个消费者C的情况,此时假设缓冲区是满的,P1到来发现缓冲区满,睡眠,P2到来,也睡眠,当C到来时,消费一个资源 ,此时counter==buffer_size-1;唤醒生产者P1,counter ==buffer_size-2,不会唤醒生产者P2,P2可能会一直睡眠。

  • 信号量的引入

    面对多个生产者和消费者时,可以引入信号量来记录空闲资源的数量,可以用empty的负数形式来进行表示,当empty==2时,表示还有两个空闲资源可以使用,当empty ==-2时,表示资源区满,有两个生产者进程正在进行睡眠等待,消费者根据|empty|的值来进行几次调用,为等待的生产者留下2个可以利用的资源。

临界区----对信号量的保护

信号量应该实时地反应资源区的情况,设置临界区对信号量执行保护措施,不能允许两个及其以上的进程对信号量进行修改,在同一个时间内只有一个进程能够进入临界区对信号量进行修改。

临界区的进入需要满足三个要求:

互斥进入,在有多个进程要求进入临界区时,一次仅允许一个进程进入,一旦有进程进入,则其他所有试图进入的进程都必须等待。

有空让进,如果没有进程在临界区内,也没有进程要求进入临界区,则当有一个进程要求进入时,应该让它进入。

有限等待,任何一个进程等待临界区的时间都是有限次的。

1.Peterson算法

//生产者P0;
flag[0]=true;  //P0要求进入;
turn=1;        //表示互斥;
while(flag[1] && turn ==1)
{
    empty--;
}
flag[0]=false; //离开后标记为false;
//生产者P1;
flag[1]=true;    //P1要求进入
turn=0//表示互斥;
while(flag[0] && turn==0)
{
    empty--;
}
flag[1]=false;  //离开后标记为false;

条件一,互斥进入:如果P0 和 P1都进入了临界区,那么都做了请求的标记,有flag[0]=flag[1]=true;此时的P0,turn=0,;P,turn=1; 这是不可能的,说明P0和P1不会同时进入;

条件二,有空让进:可以分为两种情况,第一种,其中一个进程不想进入,那它的flag就是false;则另外一个进程想要进入肯定是可以的,第二种,两个进程都想要进入,此时flag[0]=flag[1]=true;此时就是看turn了,如果P0先申请,先将turn=1;P1后申请,将turn=0,此时P0就可以进入,当P0退出时,它的flag=false,P1就可以进入。

条件三,有限等待:假设P0不能进入临界区,那条件一定是flag[1]=true && turn =1,那么此时P1肯定可以进入,P1离开之后,P0就满足了进入的条件。

2.互斥量

互斥量是信号量的一个简化版本,是直接通过硬件来保证两个进程之间的进行,互斥量有两种情况:解锁和加锁,这里用0

表示解锁,进程可以进入临界区,1表示加锁,在临界区外等待。

//生产者P0
while(mutex_lock(&lock)) {;} //如果互斥量为false,解锁,返回false,并将lock设置为true;进入临界区

empty--;

lock=false; //离开临界区后,将lock设置为false,让另外要求进入的进程可以进入;
//生产者P1
while(mutex_lock(&lock)) {;}//如果P0已经进入了,那么此时lock的值为true;生产者P1不能进入
empty--;
lock=false;

死锁现象及死锁处理

当一个进程P0申请一块资源,而这块资源被另外一个进程P1所占用,此时P1又申请P0所占用的资源,这种互相等待对方空出资源的情况就是死锁。

将死锁概念化,抽象化后,死锁发生的四个基本条件:

互斥:资源不能被共享,一个资源每次只能被一个进程所使用。

不可剥夺:进程正在使用的资源,在未使用完之前不可以被剥夺

请求与保持:一个进程因请求资源而阻塞时,对已经获得的资源保持不放。

循环等待:若干进程之间形成一种头尾相接的循环性资源等待关系。

只要可以破坏其中的一个条件,死锁就不会形成,但是这些条件有些是系统自身的调用,有些是为了对共享缓冲区的保护,对临界区的保护。因此,要破坏死锁,只能去预防死锁。

银行家算法

int available[m];     //m种资源的剩余数量;
int allocation[n][m]; //n个进程中,每个进程已经掌握m种资源的数量情况。 也可以用一维数组int allocation[n+m];
int Max[n][m];        //n个进程中,每个进程对m中资源所需要的总数;
int work[m];          //每次回收已经完成工作的进程资源后的资源数量
bool finish[n]={false};       //n个进程的结束情况 0:没有结束;1:结束
Work=Available;
while(true)
{
    bool find=false; //判断资源能否合理有序地进行分配。
    for(int i=1;i<=n;i++)  
    {
        //如果进程没有结束,而且现有的剩余资源量加上已经分配好的资源量可以满足进程i所需要的总量,则将资源
        //分配给进程i;
        if(finish[i]==false && Work + allocation[i]>=Max[i])
        {
         	finish[i]=true;
            Work+=allocation[i];  //进程i结束,将进程i的资源回收;
			find=true;
        }
    }
    if(find==false) 
    {
        goto END;
    }
}
//如果资源一直被某个进程所占有,那么造成死锁。
END:
for(int i=1;i<=n;i++)
{
    if(finish[i]==false) return "deadlock";
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
进程通信(IPC,Inter-Process Communication)是指在多个进程传递信息和共享数据机制。其中,套接字(socket)是一种常用的进程通信方式之一。 套接字是一种通过网络进行通信的机制,它允许不同进程在同一台计算机或不同计算机之进行通信。套接字提供了一种标准的接口,使得进程可以通过发送和接收数据来实现通信。 在使用套接字进行进程通信时,通常会有一个进程作为服务器端(监听端),另一个或多个进程作为客户端(连接端)。服务器创建一个套接字并绑定到一个特定的地址和端口上,然后监听客户端的连接请求。客户端通过创建一个套接字并连接到服务器指定的地址和端口,与服务器建立连接。一旦建立连接,服务器和客户端之就可以通过发送和接收数据来进行通信。 套接字可以使用不同的协议和传输层协议,例如TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)。TCP提供可靠的连接,确保数据的有序传输和错误检测;而UDP是一种无连接的协议,数据传输速度快但不保证可靠性。 在使用套接字进行进程通信时,需要考虑以下几个步骤: 1. 创建套接字:使用socket()函数创建一个套接字。 2. 绑定地址和端口:对于服务器端,使用bind()函数将套接字绑定到一个特定的地址和端口上。 3. 监听连接请求:对于服务器端,使用listen()函数开始监听客户端的连接请求。 4. 建立连接:对于客户端,使用connect()函数与服务器建立连接。 5. 发送和接收数据:使用send()和recv()函数向对方发送数据和接收数据。 需要注意的是,套接字通信是一种低级的通信方式,需要开发者自己处理数据的格式、解析等问题。在实际应用中,可以使用更高层次的通信框架或库来简化进程通信的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TIEZ@han

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值