进程控制理论<二>---那些年我们一起学习linux程序设计

进程通信

目的:

为什么进程间需要通信?

(1)  数据传输

     一个进程需要将它的数据发送给另一个进程

(2)  资源共享

多个进程之间共享同样的资源

(3)  通知事件

一个进程需要向另一个或一组进程发送消息,通知他们发生了某事件

(4)  进程控制

有些进程希望完全控制另一个进程的执行(如:debug进程)

此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

 

Linux 进程间通信(IPC)由以下几部分发展而来:

  1. UNIX 进程间通信
  2. 基于System V进程间通信
  3. POSIX进程间通信

POSIX  介绍

   POSIXportable operating system interface

表示可移植操作系统接口。电气和电子工程师协会(institute of Electrical  and Electronics Engineers ,IEEE)最初开发POSIX标准是为了提高UNIX环境下应用程序的可移植性。然而,POSIX并不局限与UNIX,许多其他的操作系统,例如:DEC OpenVMSMicrosoftWindows,都支持POSIX标准。

 

System  V 分类:(必须记住)

现在Linux 使用的进程间通信方式包括:

(1)  管道(pipe)和有名管道(FIFO(信息传输UNIX进程间通信

(2)  信号(signal(控制作用)UNIX进程间通信

(3)  消息队列  system v进程间通信

(4)   共享内存  system v进程间通信

(5)   信号量    system v进程间通信

(6)   套接字(socket

 

关(管)心(信)小(消息队列)攻(共享内存)心(信号量)疼(套)

 

管道通信

什么是管道?

    管道是单向的、先进先出的它把一个进程的输入和另一个进程的输出连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据

数据被一个进程读出后,将被从管道中删除,其他读进程将不能再读到这些数据。

管道提供了简单的流程控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞

 

一.管道创建:

  管道包括无名管道有名管道两种,前者用于父进程和子进程间通信,后者可用于运行于同一系统中的任意两个进程间的他通信。

 

无名管道由pipe()函数创建:

int pipe (int filedis[2]);

 

返回值:若成功则返回0;否则返回-1;错误原因从存于error

当一个管道建立时,它会创建两个文件描述符:filedis[0]用于读管道(管道头部),filedis[1]用于写管道(管道尾部)。

 

二.管道关闭:

关闭管道只需将这两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。

例程:

#include <unistd.h>

#include <error.h>

#include <stdio.h>

#include <stdlib.h>

 

int  main()

{                                                           

       int  pipe_fd[2];

       if (pipe(pipe_fd)<0)                        //(pipe(pipe_fd)已经是在创建管道

       {

          printf(“pipe create error\n”);

          return  -1;

}

else

    printf(“pipe creat success \n”);

close(pipe_fd[0]);

close(pipe_fd[1]);

}

 

三.管道读写

管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。(这样子进程会对父进程与管道相关的信息或者是堆、栈进程拷贝走,让子进程也知道管道在哪)

注意事项:

必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。

 

因为:在fork之后创建按管道,即是在父进程中创建管道,子进程不会知道;但如果在fork之前创建管道,子进程会继承父进程所创建的管道。

  

综合例程:

#include <unistd.h>

#include <sys/types.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

int main()

{

                                                  int pipe_fd[2];

                                                      pid_t pid;

                                                char buf_r[100];

                                                  char* p_wbuf;

                                                     int r_num;

                                                            

                                    memset(buf_r,0,sizeof(buf_r));

                                                            

                                                  /*创建管道*/

                                              if(pipe(pipe_fd)<0)

                                                             {

                                                              printf("pipe create error\n");

                                                              return -1;

                                                             }

                                                            

                                                /*创建子进程*/

                       if((pid=fork())==0)  //子进程 OR父进程?

                                                             {

                                                              printf("\n");

                                                              close(pipe_fd[1]);

                                                              sleep(2); /*为什么要睡眠原因:让父进程先往管道写东西,2秒后子进程再读管道*/

                                                              if((r_num=read(pipe_fd[0],buf_r,100))>0)

                                                              {

                                                                     printf( "%d numbers read from the pipe is %s\n",r_num,buf_r);

                                                              }     

                                                              close(pipe_fd[0]);

                                                              exit(0);

                                                           }

                                                   else if(pid>0)

                                                             {

                                                              close(pipe_fd[0]);

                                                              if(write(pipe_fd[1],"Hello",5)!=-1)

                                                                     printf("parent write1 Hello!\n");

                                                              if(write(pipe_fd[1]," Pipe",5)!=-1)

                                                                     printf("parent write2 Pipe!\n");

                                                              close(pipe_fd[1]);

                                                              sleep(3);

                                                              waitpid(pid,NULL,0); /*waitpid等待指定的子进程结束wait等待某个子进程结束*/

                                                              exit(0);

                                                             }

                                                      return 0;

}

四.命名管道(FIFO

命名管道和无名管道基本相同,有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。

 

  创建有名管道:

#include<sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *pathname ,mode_t mode)

(1)   pathname :FIF 文件名(管道名)

(2)   mode : 属性

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(closeread write等)都可用于FIFO

 

操作:

当打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:

1.      没有使用O_NONBLOCK:访问要求无法满足时将阻塞。如试图读取空的FIFO,将导致进程阻塞。

2.      使用O_NONBLOCK访问要求无法满足时不阻塞,立即出错返回,errnoENXIO

例程分析:

        fifo_write.c

        fifo_read.c

fifo_write.c

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define FIFO_SERVER "/tmp/myfifo"

 

main(int argc,char** argv)

{

                                                                 int fd;

                                                        char w_buf[100];

                                                             int nwrite;

                                                                     

                                                          /*打开管道*/

fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);/**打开的管道名必须和在fifo_read.c创建的管道为同一管道/

                                                                    

                                                            if(argc==1)

                                                                     {

                                                                      printf("Please send something\n");

                                                                      exit(-1);

                                                                     }

                                                                    

                                                   strcpy(w_buf,argv[1]);

                                                                    

                                                  /*向管道写入数据 */

                                        if((nwrite=write(fd,w_buf,100))==-1)

                                                                     {

                                                                      if(errno==EAGAIN)

                                                                             printf("The FIFO has not been read yet.Please try later\n");

                                                                     }

                                                                  else

                                                                      printf("write %s to the FIFO\n",w_buf);

}

fifo_read.c

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define FIFO "/tmp/myfifo"

 

main(int argc,char** argv)

{

                                                        char buf_r[100];

int  fd;

                                                            int  nread;

                                                                    

                                                        /*创建管道 */

                 if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))

                                                                      printf("cannot create fifoserver\n");

                                                                    

                                   printf("Preparing for reading bytes...\n");

                                                                    

                                            memset(buf_r,0,sizeof(buf_r));

                                                                    

                                                        /*打开管道 */

                             fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);

                                                              if(fd==-1)

                                                                     {

                                                                      perror("open");

                                                                      exit(1); 

                                                                     }

                                                               while(1)

                                                                     {

                                                                      memset(buf_r,0,sizeof(buf_r));

                                                                     

                                                                      if((nread=read(fd,buf_r,100))==-1)

                                                                      {

                                                                             if(errno==EAGAIN)

                                                                                    printf("no data yet\n");

                                                                      }

                                                                      printf("read %s from FIFO\n",buf_r);

                                                                      sleep(1);

                                                                     }      

                                            pause(); /*暂停,等待信号*/

                                                unlink(FIFO); //删除文件

}

信号通信:

 

一.信号(signal)机制是Unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号:

1.      当用户按某些按键时,产生信号。

2.      硬件异常产生信号:除数为0、无效的存储访问等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知进程产生一个SIGSEGC信号。

3.      进程用kill函数将信号发送给另一个进程;

4.      用户可用kill命令将信号发送给其它进程。

 

二.信号类型:

三.信号处理:

 

当某信号出现时,将按照下列三种方式中的一种进行处理:

(1)  忽略此信号

     大多数信号都是按照这种方式进行处理的,但是有两种信号却决不能被忽略。它们是:SIGKILL SIGSTOP。这两种信号不能被忽略的原因是:它们向超级用户提供了一种终止或停止进程的方法。

 

(2)  执行用户希望的动作

     通知内核在某种信号发生时,调用一个用户函数。在用户函数中,执行用户希望的处理。

 

(3)  执行系统默认动作

对大多数信号的系统默认动作是终止该进程。

 

四.信号发送:

(1)发送信号的主要函数有 kill raise

区别:

Kill -l 可以看到编号(前面的数字)

 

Kill 既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号。

#include <sys/types.h>

#include <signal.h>

int kill (pid_t pid ,int signo) //第一个参数:发给哪一个进程,这个进程的pid是多少第二个参数:发送什么信号,信号编号是多少

int raise (int signo)  //因为只给自己发,所以只需要信号编号

 

kill pid参数有四种不同的取值情况:

1. pid>0

将信号发送给进程IDpid的进程;

2.pid==0

将信号发送给同组的进程;

3.pid<0

将信号发送给其进程组ID等于pid绝对值的进程;

4.pid==-1

将信号发送给所有进程

 

(2)Alarm

使用alarm函数可以设置一个时间值(闹钟时间),当所设置的时间到了,产生SIGALRM信号。如果不捕捉此信号,则默认动作是终止该进程。(这个信号是发给自己的)

#include <unistd.h>

Unsigned int  alarm (unsigned  int  seconds)

(1)Seconds:经过了指定的seconds秒后会产生信号SIGALRM.

(2)每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它没有超时,以前登记的闹钟时间则被新值代替。

(3)如果有以前登记的尚未超过的闹钟时间,而这次seconds值为0.则表示取消以前的闹钟。

 

3Pause

Pause函数使用进程挂起直至捕捉到一个信号。

#include <unistd.h>

int pause (void)

pause让进程一直等待;pause结束等待的条件,进程收到一个信号

 

信号的处理:

信号处理的主要方法有两种:一种:是使用简单的SIGNAL函数,

                        另一种:是使用信号集函数组。

signal

#include<signal.h>

void(*signal (int signal,void(*func)(int)))(int)

func 可能的值是:

1.SIG_IGN:忽略此信号;

2.SIG_DFL:按系统默认方式处理

3.信号处理函数名:使用该函数处理

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

void my_func(int sign_no) /*所有信号处理函数,只有一个参数,且必须是整型*/

{

                                                   if(sign_no==SIGINT)

                                                                     printf("I have get SIGINT\n");

                                             else if(sign_no==SIGQUIT)

                                                                     printf("I have get SIGQUIT\n");

}

int main()

{

                        printf("Waiting for signal SIGINT or SIGQUIT \n ");

                                                                    

                                                /*注册信号处理函数*/

                                               signal(SIGINT, my_func);

                                              signal(SIGQUIT, my_func);

                                                                    

                             pause();/*等待。。。直到进程收到一个信号*/

                                                              exit(0);

}

 

搜索进程号指令: ps aux

Kill -s  SIGQUIT  3687 (给进程为号3687的进程发送一个SIGQUIT指令)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值