`
T240178168
  • 浏览: 368029 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

进程通信

    博客分类:
  • java
阅读更多
进程通信
(1)进程的同步与互斥
一般来说同步反映了进程之间的协作性质,往往指有几个进程共同完成一个任务时在时间次序上的某种限制,进程相互之间各自的存在及作用,通过交换信息完成通信。如接力比赛中一组队员使用接力棒等。
进程互斥体现了进程之间对资源的竞争关系,这时进程相互之间不一定清楚其它进程情况,往往指多个任务多个进程间的通讯制约,因而使用更广泛。如打篮球时双方挣抢篮板球等。
(2)临界区
并发进程中与共享变量有关的程序段定义为临界区。进入临界区的准则是:①一次只准一个进程进入临界区;②本进程结束负责通知下一进程;③进程调度,不能阻塞。
(3)原语
原语是不可中断的过程。
·加锁/开锁(LOCK/UNLOCK)原语
优点是实现互斥简单;缺点是效率很低。
·信号量(Semaphore)及PV操作
PV操作能够实现对临界区的管理要求。它由P操作原语和V操作原语组成,对信号量进行操作,具体定义如下:
P(S):①将信号量S的值减1,即S=S-1;
②如果S>0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):①将信号量S的值加1,即S=S+1;
②如果S<=0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意信号量的值仅能由PV操作来改变。
一般来说,信号量S0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
      消息缓冲通信原语
        高级通信原语,用于一组信息发送(Send)与读取(Read)。
生产者-消费者问题:
在这个问题上主要考虑的是:缓冲区满或缓冲区空以及竞争条件(race condition)。以下是一个含竞争条件的生产者-消费者问题实例。

#define N 100            /*number of slots in the buffer*/
   int count=0;               /*number of items in the buffer*/
   void producer(void) {
          int item;
          while(TRUE) {
              produce_item(&item);
              if (count==N) sleep();
               enter_item(item);
                count=count+1;
               if (count==1) wakeup(consumer);
           }
}
   void comsumer(void) {
           int item;
           while(TRUE) {
                if(count==0) sleep();
                remove_item(&item);
                count=count-1;
                if (count==N-1) wakeup(producer);
                consume_item(item);
           }
   }
信号量的概念首先由E.W.Dijkstra在1965年提出的。semaphore(信号量)是一个大于等于零的整型变量。 对信号量有两个原子操作:-和+,DOWN()和UP(),SLEEP()和WAKEUP(),P()和V(),Wait() 和Signal()。虽然它们名字都不一样,可意思都是相同的,拿down和up操作来说明。 DOWN(t)操作 递减信号量t的值:先检查t是否大于0,若t大于0则t减去1;若t的值为0,则进程进入睡眠状态,而此时的DOWN操作并没有结束。这是原子操作,用来保证一旦一个信号量的操作开始,则在操作完成或阻塞之前别的进程不允许访问该信号量。 UP(t)操作 递增信号量t的值:如果一个或多个进程在该信号量t上睡眠,则无法完成一个先前的DOWN操作,则由系统选择其中的一个并允许起完成它的DOWN操作。因此,对一个有进程在起上睡眠的信号量执行一次UP操作以后,该信号量的值仍旧是0,但在其上睡眠的进程却少了一个。递增信号量的值和唤醒一个进程的操作也是原子操作,不可分割的。这样做保证不会有进程因为执行UP操作而被阻塞。

多线程程序中最简单也是最常用的同步机制要算是mutex(互斥量)。
一个mutex提供两个基本操作:DOWN和UP。一旦某个线程调用了DOWN,其它线程再调用DOWN时就会被阻塞。当这个线程调用UP后,刚才阻塞在DOWN里的线程中,会有一个且仅有一个被唤醒。换句话说,对于一个给定的mutex,只有一个线程可以在DOWN和UP调用之间获取处理器时间。这里的mutex 用来保护访问的临界区,避免数据出现竞争条件。
#include<sys/types.h>
#include<linux/sem.h>
#include<linux/shm.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include<time.h>
#define MAXSHM 5 //定义缓冲区数组的下标变量个数

/*        定义3个信号量的内部标识 */
int fullid;
int emptyid;
int mutexid;
/* 主函数 */
int main()
{
/* 定义2个共享内存的ID */
int arrayid;
int getid;
/* 定义共享内存虚拟地址 */
int *array;
int *get;
    /* 创建共享内存 */
arrayid=shmget(IPC_PRIVATE,sizeof(int) *MAXSHN,IPC_CREAT|0666);
getid=shmget(IPC_PRIVATE,sizeof(int),IPC_CREAT|0666);
    /* 初始化共享内存 */
array=(int *) shmat(arrayid,0,0);
get=(int *) shmat(getid,0,0);
*get=0;
    /* 定义信号量数据结构 */
struct sembuf P,V;
union semun arg;
    /* 创建信号量 */
fullid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
emptyid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
mutexid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
    /*初始化信号量 */
arg.val=0;            //初始时缓冲区中无数据
if(semctl(fullid,0,SETVAL,arg)==-1)
   perror("semctl setval error");
arg.val=MAXSHM;       //初始时缓冲区中有5个空闲的数组元素
if(semctl(emptyid,0,SETVAL,arg)==-1)
   perror("semctl setval error");
arg.val=1;            //初始时互斥信号为1,允许一个进程进入
if(semctl(mutexid,0,SETVAL,arg)==-1)
        perror("semctl setval error");
    /* 初始化 P V操作 */
P.sem_num=0;
P.sem_op=-1;
P.sem_flg=SEM_UNDO;
    V.sem_num=0;
V.sem_op=1;
V.sem_flg=SEM_UNDO;
    /*   生产者进程 */
if(fork()==0)
{
   int i=0;
   int set=0;
   while(i<10)
   {
    semop(emptyid,&P,1);         //对 emptyid执行P操作
    semop(mutexid,&P,1);         //对 mutexid执行 P操作
    array[set%MAXSHM]=i+1;
    printf("Producer put number %d to No.%d\n",array[set%MAXSHM],set%MAXSHM);
    set++;                       //写计数加1
    semop(mutexid,&V,1);         //对mutexid执行 V 操作
    semop(fullid,&V,1);          //对fullid执行 V 操作
    i++;
   }
   sleep(3);                    //SLEEP 3秒,等待消费者进程执行完毕
   printf("Poducer if over\n");
   exit(0);
}
else
{
   /* 消费者A进程 */
   if(fork()==0)
   {
    while(1)
    {
     if(*get==10)
      break;
     semop(fullid,&P,1);        //对fullid执行 P 操作
     semop(mutexid,&P,1);       //对mutexid执行 P 操作
     printf("The ConsumerA get number from No.%d\n",(*get)%MAXSHM);
     (*get)++;                   //读计数加1
     semop(mutexid,&V,1);        //对mutexid执行 V 操作
     semop(emptyid,&V,1);        //对fullid执行 V 操作
     sleep(1);
    }
    printf("ConsunerA is over\n");
    exit(0);
   }
   else
   {
     /*消费者B进程 */
    if(fork()==0)
    {
     while(1)
     {
      if(*get==10)
       break;
       semop(fullid,&P,1);       //对fullid执行 P 操作
     semop(mutexid,&P,1);      //对mutexid执行 P 操作
     printf("The ConsumerA get number from No.%d\n",(*get)%MAXSHM);
     (*get)++;                 //读计数加1
     semop(mutexid,&V,1);      //对mutexid执行 V 操作
     semop(emptyid,&V,1);      //对emptyid执行 V 操作
     sleep(1);
    }
    printf("ConsunerB is over\n");
    exit(0);
    }
   }
}
/*   父进程返回后回收3个子进程 */
wait(0);
wait(0);
wait(0);
/* 断开并撤消2个共享内存 */
shmdt(array);
shmctl(arrayid,IPC_RMID,0);
shmctl(get);
shmctl(getid,IPC_RMID,0);
/*   撤消3个信号量集 */
semctl(emptyid,IPC_RMID,0);
semctl(fullid,IPC_RMID,0);
semctl(mutexid,IPC_RMID,0);
exit(0);
}


主要函数及数据结构:
signal();//安装信号用。signalaction 也可以,但好像太繁,小程序不用
sigxxxset();//信号集操作
sigprocmask();//设置信号集
sigsuspend();//等待相关信号

代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void wake(){ return;}//do nothing but return immediately to wake up the process

int main()
{
sigset_t      set;
pid_t            pid;
int                 i;

sigfillset(&set);
sigdelset(&set,SIGINT);
sigprocmask(SIG_SETMASK,&set,NULL);//for user control
if( (pid=fork() ) ==0 )//child
{
    sigdelset(&set,SIGUSR2);
    signal(SIGUSR2,wake);//install the child handler
    sleep(1);
    puts("This is child. I am going to wake parent first.");
    for( i=1;i<=4;i++)
    {
      kill(getppid(),SIGUSR1);//send singal to parent
      sigsuspend(&set);//waiting for parent's signal
      puts("Parent wake me up!");
/*
   critical code
*/
      printf("Child: i=%d.\n",i);
    }
    _exit(0);
}
//parent

sigdelset(&set,SIGUSR1);
signal(SIGUSR1,wake);
puts("I am the parent!");
for( i=1;i<=4;i++)
{
    sigsuspend(&set);//waiting for child's signal
    puts("Child wake me up!");
/*
   critical code
*/
    printf("Parent: i=%d.\n",i);
    kill(pid,SIGUSR2);//sending child signal
}

return 0;

}

运行结果:
I am the parent!
This is child. I am going to wake parent first.
Child wake me up!
Parent: i=1.
Parent wake me up!
Child: i=1.
Child wake me up!
Parent: i=2.
Parent wake me up!
Child: i=2.
Child wake me up!
Parent: i=3.
Parent wake me up!
Child: i=3.
Child wake me up!
Parent: i=4.
Parent wake me up!
Child: i=4.

解释:
一开始把信号集填满并设置是为了避免没必要的信号的干扰。并且,在父子进程还没运行到要接受信号的地方时,互相受影响。当然,你如果把这一步省了你就会看到,运行结果不像你想像的那样了。

更复杂的延伸。利用信号进行数学计算控制。实质上,这里可以搞并行计算的(smp required)。但只是为了简单,所以没有写成并行而已。


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
#include <math.h>

double ac(double);
double bc(double,double);
double cc(double,double);
double dc(double,double);
void   wake(){return;}

int main()
{
pid_t pid;
key_t key=ftok("/dev/shm/",3);
int shmid;

sigset_t    set;
typedef struct
{
   double a;
   double b;
   double c,d;
}data;
data* ptr;


sigfillset(&set);
sigdelset(&set,SIGINT);
sigprocmask(SIG_SETMASK,&set,NULL);

if( (shmid=shmget(key,sizeof(data),IPC_CREAT|0666)) == -1 )
{
   perror("shmget fail");
   exit(1);
}
if( (ptr=(data*)shmat(shmid,NULL,0)) == NULL)
{
   perror("shmat fail");
   exit(1);
}

ptr->a=1.0;
ptr->b=2.0;
if( (pid=fork() ) ==0)
{
   sigdelset(&set,SIGUSR2);//parent will wake me up through this signal
   signal(SIGUSR2,wake);
   puts("\
        Hello, this is the child! I will be sleeping\n\
        until the parent finish the 1st part calculation.\n\
        Then he will wake me up. Now, I go to bed!");
   sigsuspend(&set);//waiting for parent's signal, because parent will send the first signal
   ptr->b=bc(ptr->a,ptr->b);
   puts("2nd part finished!");//actually, you don't need this.
   kill(getppid(),SIGUSR1);//tell parent that he has done the jub
   sigsuspend(&set);//waiting for the parent's wakeup again.
   ptr->d=dc(ptr->b,ptr->c);
   puts("4th part finished!");//actually, you don't need this.
   puts("OK, the child has done his job!");
   shmdt(ptr);
   kill(getppid(),SIGUSR1);
   _exit(0);
}

// parent now
sigdelset(&set,SIGUSR1);
signal(SIGUSR1,wake);
puts("Hello, this is parent!");
sleep(1);//let the user to read clearly
ptr->a=ac(ptr->a);//calculate the first part
puts("first part finished!");//actually, you don't need this.
kill(pid,SIGUSR2);
sigsuspend(&set);
ptr->c=cc(ptr->a,ptr->b);
puts("3rd part finished!");//actually, you don't need this.
kill(pid,SIGUSR2);
sigsuspend(&set);
puts("OK, all the caculation has been done. The result is:");
printf("a=%f, b=%f, c=%f, d=%f.\n",ptr->a,ptr->b,ptr->c,ptr->d);

shmctl(shmid,IPC_RMID,NULL);

return 0;


}

double ac(double x)
{
return (sin(x)+cos(x)+tan(x) );
}

double bc(double x,double y)
{
return (tan(x)+sin(y)+tan(y) );
}

double cc(double x,double y)
{
return (atan(x)+tan(x)+cos(y) );
}

double dc(double x,double y)
{
return ( atan(x*y) );
}


运行结果:
[sody@longtem os]$ ./a.out
        Hello, this is the child! I will be sleeping
        until the parent finish the 1st part calculation.
        Then he will wake me up. Now, I go to bed!
Hello, this is parent!
first part finished!
2nd part finished!
3rd part finished!
4th part finished!
OK, the child has done his job!
OK, all the caculation has been done. The result is:
a=2.939181, b=-1.480964, c=1.127340, d=-1.031139.
分享到:
评论

相关推荐

    实验 Linux进程通信的参考答案

    实验 Linux 进程通信的参考答案 Linux 进程通信是指在操作系统中,多个进程之间进行数据交换和同步的机制。在 Linux 中,进程通信可以通过信号、管道和共享内存等方式实现。 一、信号机制 在 Linux 中,信号是一...

    进程通信实验报告 进程通信实验报告 进程通信实验报告

    根据提供的文件信息,本次进程通信实验报告主要围绕进程通信这一核心概念展开,下面将对这一主题进行详细解析。 ### 一、进程通信简介 进程通信(Inter-Process Communication,IPC)是指不同进程之间互相传递数据...

    操作系统实验-进程和进程通信

    操作系统实验-进程和进程通信是计算机科学中一个重要的实践环节,它涉及到操作系统核心概念的实践应用,特别是进程管理和进程通信。在这个实验中,你需要编写一个程序,该程序创建父子进程,通过协调工作来完成特定...

    实验2-进程和进程通信实验报告

    【进程和进程通信】是操作系统中的重要概念,它涉及到多个执行单元如何在同一系统资源下协作完成任务。在本实验中,学生需要通过编程实践来深入理解这一概念。 实验的目的是让学生通过实际操作来掌握进程的基本概念...

    2.4 实验四:使用命名管道实现进程通信.doc

    使用命名管道实现进程通信实验报告 在操作系统中,进程通信是指在多个进程之间进行数据交换和同步的机制。命名管道是Windows系统提供的一种进程通信机制,通过使用命名管道,两个进程可以进行通信。在本实验中,...

    共享内存,进程通信,进程同步 代码

    本篇文章将深入探讨共享内存、进程通信以及进程同步的概念,并通过`ipc_client`和`ipc_server`两个示例程序,阐述其在实际编程中的应用。 一、共享内存 共享内存是进程间通信的一种直接方式,它允许多个进程共享一...

    实验一 进程通信——管道和信号实验报告.doc

    本次实验是关于操作系统中的进程通信,主要涉及了进程的创建、控制、以及多种通信方式,包括信号通信和管道通信。通过实验,目的是加深对进程概念的理解,掌握并发执行的本质,并学习如何解决进程间的互斥问题。实验...

    计算机操作系统实验-进程通信(一).doc

    计算机操作系统实验-进程通信(一) 本实验的目的是学习如何利用管道机制和共享存储区机制进行进程间的通信,并加深对上述通信机制的理解。在实验中,我们将学习如何使用系统调用 pipe()、shmget()、shmat()、shmdt...

    操作系统进程通信

    "操作系统进程通信" 在操作系统中,进程通信是指不同的进程之间进行信息交换和协调的机制。进程通信是操作系统中最基本也是最重要的机制之一,它使得不同的进程能够相互协作和协调,从而实现复杂的任务。 进程通信...

    进程通信实验报告

    根据给定的文件信息,我们可以深入探讨Linux进程通信的关键知识点,包括信号通信、管道通信以及消息传递机制。以下是对这些概念的详细解析: ### 进程通信基础 进程通信是操作系统中的一个核心概念,它允许不同...

    VC进程通信-VS

    在IT领域,进程通信(IPC, Inter-Process Communication)是一种技术,使得运行在操作系统中的不同进程之间能够交换数据。在Windows环境下,Visual C++(VC)开发者经常利用Visual Studio(VS)来实现进程间的通信。...

    进程通信示例代码

    在IT领域,进程通信是操作系统中的重要概念,特别是在多任务并行处理的系统中。它涉及到不同进程间如何交换数据、协调工作,是理解和实践并发编程的关键。本资源包提供了有关进程通信的教程和示例代码,对于初学者来...

    操作系统实验二 进程通信机制的应用

    操作系统实验二“进程通信机制的应用”旨在通过实践深入理解进程并发执行的概念,以及掌握进程和线程的创建、控制及通信方法。实验报告要求学生在Linux环境下,运用系统调用进行进程和线程的编程与调试,从而更好地...

    实现进程通信实验指导

    在计算机科学领域,进程通信是操作系统中至关重要的概念,它涉及到不同进程间的资源共享和信息交换。本实验指导将深入探讨如何实现进程通信,特别是在中间件技术的背景下。X大软件的中间件基础实验旨在帮助学生理解...

    qt使用QProcess实现父子进程通信,适用linux平台

    在Linux系统中,每个运行的程序都是一个独立的进程,父子进程通信可以帮助不同进程间共享数据、协同工作。 父子进程通信的主要方法包括管道(Pipe)、信号量(Semaphore)、共享内存(Shared Memory)、消息队列...

    android多进程通信二

    在Android系统中,多进程通信(Multi-Process Communication, 简称MPC)是一种重要的技术,用于在不同进程间交换数据和协调操作。在Android应用开发中,一个应用程序默认运行在一个单独的进程中,但有时我们需要创建...

    计算机操作系统实验-进程通信(二).doc

    计算机操作系统实验-进程通信(二) 本实验的主要目的是学习如何利用消息缓冲队列进行进程间的通信,并加深对消息缓冲队列通信机制的理解。通过实验,我们可以了解系统调用 msgget()、msgsnd()、msgrcv()、msgctl()...

    几种进程通信方法的研究和比较

    ### 几种进程通信方法的研究和比较 #### 进程通信的重要性 进程间通信(IPC,Inter-Process Communication)作为操作系统内核层的关键组成部分,对于实现多进程间的协作与同步至关重要。不同进程运行在独立的内存...

    window进程通信方法

    本文将详细讲解Windows进程通信中的一个常见方法——使用WM_COPYDATA消息。 WM_COPYDATA是一个预定义的消息,允许进程间传递只读数据。它依赖于Win32 API中的`SendMessage()`函数,此函数会将消息发送给指定的窗口...

Global site tag (gtag.js) - Google Analytics