三、进程间通信一

来源:互联网 发布:中国商业数据网 编辑:程序博客网 时间:2024/04/29 07:49

一、进程间通讯概述
1、原因:为什么进程间需要通讯
   a.数据传输:一个进程需要将它的数据发送给另一个进程。
   b.资源共享:多个进程之间共享同样的资源。
   c.通知事件:一个进程需要向另一个或者一组进程发送消息,通知它们发生了某事件。主要是在进程同步时,A进程完了需要向B进程发送消息,告知自己结束了。
   d.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另外一个进程的所有操作,并能够及时指导它的状态改变。
2、现在linux使用的进程间通信方式包括:
    a.[无名]管道(pipe)和有名管道(FIFO)
    b.信号(signal)
    c.消息队列
    d.共享内存
    e.信号量
    f.套接字(socket)
二、管道通讯
1、管道通信
  什么是管道:管道是单向的,先进先出的,它把进程的输出和另一个进程的进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
2、管道创建
   管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一系统的任意两个进程间的通信。
 无名管道有pipe()函数创建:
 int pipe(int filedis[2]);  当一个管道建立时,它会创建两个文件描述符,并由参数filedis数组返回: filedis[0]用于读管道,filedis[1]用于写管道。成功时,函数返回值为0,否则返回-1。
3、(无名)管道读写
   管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。
注意::必须在系统调用fork()前调用pipe(),否则子进程讲不会继承文件描述符。如果是fork先执行,那由于子进程会共享父进程的程序,所以也会创建一个管道,此时,子进程和父进程各自运行自己的管道,无法达到通信的目的。
4、命名管道(FIFO)-创建
  #include <sys/types.h>
  #include <sys/stat.h>
  int mkfifo(const char* pathname,mode_t mode)
 pathname:FIFO文件名
 mode:属性(见文件操作章节) 一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
5、命名管道-操作
   当打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:
   a.没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞。
   b.使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO.
注意::读写管道时,当数据被从管道中读走之后,数据就不存在于管道中了。 
三、信号通讯
1、产生信号的条件:
 a.用户按下某些按键时
 b.硬件异常产生信号
 c.进程用kill函数将信号发送给另一个进程
 d.用户可用kill命令讲信号发送给其他进程。
2、信号类型(常见)
 SIGHUP:从终端上发出的结束信号
 SIGINT:来自键盘的中断信号(Ctrl-c)
 SIGKILL:该信号结束接收信号的进程
 SIGTERM:kill命令发出的信号
 SIGCHLD:标识进程停止或者结束信号
 SIGSTOP:来自键盘(Ctrl-Z)或者调试程序的停止执行信号
3、信号处理
   1)、忽略此信号:大多数信号都是按照这种方式进行处理的,但是有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP.这两种信号不能忽略的原因是:它们向超级用户提供一种终止或停止进程的方法。
   2)、执行用户希望的动作:通知内核在某种信号发生时,调用一个用户函数。在用户函数中,执行用户希望的动作。
   3)、执行系统默认动作:对大多数信号系统默认动作是终止该进程。
4、信号发送
    1)、发送信号的主要函数有kill和raise。主要区别:kill既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号。
 #include <sysy/types.h>
 #include <signal.h>
 int kill(pid_t pid,int signo)   pid:进程的ID号,signo:要发送的信号。
 int raise(int signo)
    kill的pid参数的四种情况:
       pid>0:将信号发送给进程ID为pid的进程
       pid==0:将信号发送给同组的进程
       pid<0:将信号发送给其进程组ID等于pid绝对值的进程
       pid==-1:将信号发送给所有进程。
    2)、Alarm:使用alarm函数可以设置一个时间值(闹钟时间),当所设置的时间到了时,产生SIGALRM信号。如不捕捉此信号,则默认动作是终止进程。
 #include <unistd.h>
 unsigned int alarm(unsigned int seconds)
  seconds:经过了指定的seconds秒后会产生信号SIGALRM。
    3)、Pause:pause函数使调用进程挂起直至捕捉到一个信号。
 #include <unistd.h>
 int pause(void)
 只有执行了一个信号处理函数后,挂起才结束。
5、信号的处理
主要有两种方式:一种是使用简单的signal函数,另一种是使用信号集函数组。
    signal函数
 #include <signal.h>
 void(*signal(int signo,void(*func)(int)))(int)
 可理解为signal(int signo,func)  func(int) func函数里面就是当实现signo信号时(signo是要想实现的信号类型),函数要做的动作
 func可能的值是:
    a.SIG_IGN:忽略此信号
    b.SIG_DFL:按系统默认方式处理
    c.信号处理函数名:使用该函数处理(此函数被称为“捕捉信号函数”)signal函数是信号的接收器,那么kill函数就是信号的发送器int kill(pid_t pid,int signo)
//在终端下输入信号的方法:使用kill命令,kill -s 信号类型 PID
四、共享内存
   共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
 1、共享内存实现分为两个步骤:
   a.创建共享内存,使用shmget函数
   b.映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat数。
                                                                                 

  2、创建
 int shmget(key_t key,int size,int shmflg)
  key标识共享内存的键值:0/IPC_PRIVATE.当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。
  返回值:如果成功,返回共享内存标识符;如果失败,返回-1.  
 
                                             

3、映射
 int shmat(int shmid,char *shmaddr,int flag)
   参数:shmid:shmget函数返回的共享存储标识符
  flag:决定以什么方式来确定映射的地址(通常为0)
  shmaddr:自己设定的映射地址,一般设为0,此时系统会自动为我们分配地址,并且返回值会返回这个地址。
  返回值:成功则返回共享内存映射到进程中的地址,失败返回-1.
4、释放
 int shmdt(char *shmaddr)
  当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离。