进程间通讯IPC(InterProcessCommunication)

来源:互联网 发布:吴承恩与西游记 知乎 编辑:程序博客网 时间:2024/05/21 16:55
    [文件]
    管道
        无名管道(pipe)
        有名管道(fifo)
    信号
    
    消息队列     POSIX /system v
    信号量       POSIX /system v
    共享内存     POSIX /system v
    

    socket(unix域协议)



Pipe:

利用文件系统调用的接口,在内核中实现一种类似水管的
    进程间通讯方式,它的特点:
        1. 有两端,一端读,一端写
        2. 按顺序读,不支持lseek
        3. 内容读走了,就没了
        4. 它随内核持续性(相应的读写端都关闭了才销毁)
        5. 无名管道只能用于具有"亲缘关系"的进程间通讯
            ("亲缘关系", fork产生的子进程,几乎拷贝了父进程的所有,如果无这种拷贝关系,则无亲缘关系)
        
            一个东西的生命周期:
                (0) 随代码块({},函数)持续性(局部变量)
                (1)  随程序持续性(随进程持续性)(全局变量)
                (2) 随内核持续性(如无名管道)
                (3) 随文件系统持续性(文件)
                
    "文件系统调用接口"(read/write,....),前提的是: 文件描述符


1. pipe/pipe2用来在内核创建一个无名管道的

       pipe, pipe2 - create pipe

SYNOPSIS
       #include <unistd.h>
       int pipe(int pipefd[2]);
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>
       int pipe2(int pipefd[2], int flags);
DESCRIPTION
       pipe()  creates  a pipe, a unidirectional data channel that can be used
       for interprocess communication.  The array pipefd is used to return two
       file  descriptors  referring to the ends of the pipe.  pipefd[0] refers
       to the read end of the pipe.  pipefd[1] refers to the write end of  the
       pipe.   Data  written  to  the write end of the pipe is buffered by the
       kernel until it is read from the read end of  the  pipe.   For  further
       details, see pipe(7).

       If  flags is 0, then pipe2() is the same as pipe().  The following val‐
       ues can be bitwise ORed in flags to obtain different behavior:

       O_NONBLOCK  Set the O_NONBLOCK file status flag on  the  two  new  open
                   file  descriptions.   Using  this flag saves extra calls to
                   fcntl(2) to achieve the same result.

       O_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the two new file
                   descriptors.   See  the  description  of  the  same flag in
                   open(2) for reasons why this may be useful.

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and  errno  is
       set appropriately.


NAME
       pipe, pipe2 - create pipe

SYNOPSIS
       #include <unistd.h>

       int pipe(int pipefd[2]);
            pipefd: 整数数组。有两个元素,
            pipefd[0]:保存是读的那个文件描述符
            pipefd[1] : 保存的写的那个文件描述符
        返回值:
            成功返回0,
            失败返回-1, errno被设置

        pipe创建的无名管道默认读写方式是阻塞的。
        读(如果没有数据,或者写端不存在),会阻塞
   
       #define _GNU_SOURCE             
       #include <fcntl.h>             
       #include <unistd.h>

        pipe2同样是用来创建无名管道的,只不过它
        在创建这个管道时,可以指定读写方式为
        非阻塞
       int pipe2(int pipefd[2], int flags);
               pipefd:与pipe一样
               flags:
                   O_NONBLOCK:非阻塞方式
                   0:

FIFO(named pipe, 有名管道)

    "有名":在文件系统中有一个路径名(名字), 它是不是就一定存在于硬盘文件系统中呢???
        不是的。fifo,它只是在文件系统中有一个名字,它实际存在于内核。
        fifo是为了没有亲缘关系的进程间通信的,而在pipe(无名管道)的基础上改进而来的。
        
        
    man 7 fifo

NAME
       fifo - first-in first-out special file, named pipe

DESCRIPTION
       A FIFO special file (a named pipe) is similar to a pipe, except that it
       is accessed as part of the filesystem.  It can be  opened  by  multiple
       processes  for  reading or writing.  When processes are exchanging data
       via the FIFO, the kernel passes all data internally without writing  it
       to  the filesystem.  Thus, the FIFO special file has no contents on the
       filesystem; the filesystem entry merely serves as a reference point  so
       that processes can access the pipe using a name in the filesystem.

       The kernel maintains exactly one pipe object for each FIFO special file
       that is opened by at least one process.  The FIFO  must  be  opened  on
       both  ends  (reading and writing) before data can be passed.  Normally,
       opening the FIFO blocks until the other end is opened also.

       A process can open a FIFO in nonblocking mode.  In this  case,  opening
       for  read-only will succeed even if no-one has opened on the write side
       yet, opening for write-only will fail with ENXIO  (no  such  device  or
       address) unless the other end has already been opened.

       Under  Linux,  opening  a  FIFO for read and write will succeed both in
       blocking and nonblocking mode.  POSIX leaves this  behavior  undefined.
       This  can be used to open a FIFO for writing while there are no readers
       available.  A process that uses both ends of the connection in order to
       communicate with itself should be very careful to avoid deadlocks.



    NAME
       fifo - first-in first-out special file, named pipe

    DESCRIPTION
       
      FIFO(有名管道)与PIPE(无名管道)类似,除了它在文件系统中有一个名字外。
       
       在能够传输数据前, FIFO必须两端(读端和写端)都被打开。
       
       通常情况下,打开一个fifo会阻塞直到该fifo的另外一端也被打开。
    
        一个进程也可以以非阻塞方式(O_NONBLOCK)打开一个fifo,在这种情况下,
           如果你是只读打开该fifo,那么会成功(即使写端没有打开),
           如果你是只写打开该fifo,那么会失败(errno == ENXIO), 除非读端已经被打开。


fifo的系统调用接口:

    mkfifo:


NAME
       mkfifo - make a FIFO special file (a named pipe)
SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       int mkfifo(const char *pathname, mode_t mode);
DESCRIPTION
       mkfifo()  makes a FIFO special file with name pathname.  mode specifies
       the FIFO's permissions.  It is modified by the process's umask  in  the
       usual way: the permissions of the created file are (mode & ~umask).

       A  FIFO special file is similar to a pipe, except that it is created in
       a different way.  Instead of being an anonymous communications channel,
       a FIFO special file is entered into the filesystem by calling mkfifo().

       Once  you have created a FIFO special file in this way, any process can
       open it for reading or writing, in the same way as  an  ordinary  file.
       However,  it  has to be open at both ends simultaneously before you can
       proceed to do any input or output operations on it.  Opening a FIFO for
       reading  normally  blocks  until some other process opens the same FIFO
       for writing, and vice versa.  See fifo(7) for nonblocking  handling  of
       FIFO special files.

RETURN VALUE
       On success mkfifo() returns 0.  In the case of an error, -1 is returned
       (in which case, errno is set appropriately).   

NAME
       mkfifo - make a FIFO special file (a named pipe)

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);
           pathname: 要创建的有名管道在文件系统中的
               文件名(带路径)
           mode:创建的有名管道的权限,有两种方式指定:
               (1) 0664
               (2) S_IRUSR  S_IWUSR S_IXUSR
                    S_IRGRP, S_IWGRP, S_IXGRP
                    S_IROTH, S_IWOTH, S_IXOTH
           返回值:
                如果成功返回0,
                如果失败返回-1, errno被设置


           

signal:



    在程序执行过程中,进程可以发送信号给别的进程,也可以收到别的进程
    发过来信号。信号在实现时,是用软中断来实现的。在实现时,信号就是
    一个整数值,不同的信号值不同,含义也不一样。man 7 signal


    Standard signals:

        Signal     Value     Action   Comment
       ────────────────────────────────────────
       SIGHUP        1       Term    控制终端检测到挂起操作或控制进程死亡产生该信号
 
       SIGINT            2       Term    CTRL+C
       SIGQUIT         3       Core    CTRL+Z
       SIGILL             4       Core    遇到非法指令时产生,Illegal Instruction
       SIGABRT        6       Core    调用abort函数产生,Abort signal from abort(3)
       SIGFPE           8       Core    浮点运算出错,Floating point exception
       SIGKILL          9       Term    Kill signal
       SIGSEGV      11       Core    内存非法引用时产生,Invalid memory reference
       SIGPIPE        13       Term    当pipe是非阻塞方式打开时,如果往管道里写数据时,
                   没有进程读时,产生该信号。 Broken pipe: write to pipe with no  readers
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    终止信号。Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign(忽略)     子进程停止或终止运行时产生。
                   Child stopped or terminated
       SIGCONT   19,18,25    Cont    在停止状态继续运行时产生该信号Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process

      
The signals SIGKILL and SIGSTOP cannot be caught, blocked,
       or ignored.

        一个信号的处理方式有三种:    
            (1) SIG_IGN: ignore 忽略
            (2) SIG_DFL: default默认
            (3) 用户自己定义的处理函数。这种方式,我们称为"捕捉"信号  catch signal

                                                                   ( 运用函数指针,回调)


           
在信号上的操作接口:

1. 发送信号

    kill


NAME
       kill - send signal to a process

SYNOPSIS
       #include <sys/types.h>
       #include <signal.h>

       int kill(pid_t pid, int sig);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       kill(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

DESCRIPTION
       The  kill()  system  call can be used to send any signal to any process
       group or process.

       If pid is positive, then signal sig is sent to the process with the  ID
       specified by pid.

       If pid equals 0, then sig is sent to every process in the process group
       of the calling process.

       If pid equals -1, then sig is sent to every process for which the call‐
       ing  process  has  permission  to  send  signals,  except for process 1
       (init), but see below.

       If pid is less than -1, then sig  is  sent  to  every  process  in  the
       process group whose ID is -pid.

       If  sig  is 0, then no signal is sent, but error checking is still per‐
       formed; this can be used to check for the existence of a process ID  or
       process group ID.

       For  a  process  to  have permission to send a signal it must either be
       privileged (under Linux: have the CAP_KILL capability), or the real  or
       effective  user  ID of the sending process must equal the real or saved
       set-user-ID of the target process.  In the case of SIGCONT it  suffices
       when  the  sending  and receiving processes belong to the same session.
       (Historically, the rules were different; see NOTES.)

RETURN VALUE
       On success (at least one signal was sent), zero is returned.  On error,
       -1 is returned, and errno is set appropriately.

NAME
       kill - send a signal to a process or a group of processes
        kill用来发送一个信号给一个进程或一组进程
           

SYNOPSIS
       #include <signal.h>

       int kill(pid_t pid, int sig);
        pid: 指定信号的接收者(可能是多个进程)
            pid > 0:  接收者进程ID
            pid == 0: 发送信号给与调用进程同组的所有进程
            pid == -1: 发送信号给调用进程有权限发送的所有进程
            pid < -1: 发送信号给组ID等于pid绝对值的所有进程

        sig:要发送的信号(如:SIGINT, SIGUSR1,  ...)
       
    返回值:
        如果成功(至少有一个进程成功接收到信号)返回0
        如果失败返回-1, errno被设置


    raise     发信号给自己   int raise(int sig); <=> kill(getpid(), sig);


NAME
       raise - send a signal to the caller

SYNOPSIS
       #include <signal.h>

       int raise(int sig);

DESCRIPTION
       The  raise()  function sends a signal to the calling process or thread.
       In a single-threaded program it is equivalent to

           kill(getpid(), sig);

       In a multithreaded program it is equivalent to

           pthread_kill(pthread_self(), sig);

       If the signal causes a handler to be called, raise() will  return  only
       after the signal handler has returned.

RETURN VALUE
       raise() returns 0 on success, and nonzero for failure.


   

    alarm :

NAME
       alarm - set an alarm clock for delivery of a signal

SYNOPSIS
       #include <unistd.h>

       unsigned int alarm(unsigned int seconds);

DESCRIPTION
       alarm()  arranges  for  a SIGALRM signal to be delivered to the calling
       process in seconds seconds.

       If seconds is zero, any pending alarm is canceled.

       In any event any previously set alarm() is canceled.

RETURN VALUE
       alarm() returns the number of seconds remaining  until  any  previously
       scheduled alarm was due to be delivered, or zero if there was no previ‐
       ously scheduled alarm.

NAME
       alarm - set an alarm clock for delivery of a signal

SYNOPSIS
       #include <unistd.h>

        每一个进程都有一个闹钟(同一个时刻只有一个闹钟起作用),
        你可以设置闹钟的超时时间,一旦你设置的时间到了 ,
        闹钟就会闹,(发送一个SIGALRM给自身进程)

       unsigned int alarm(unsigned int seconds);
           seconds:你要设置的闹钟超时时间
                   seconds== 0表示cancels alarm,取消闹钟
           返回值:
               成功返回上一个闹钟的剩余时间(秒数)


            alarm(5);
            sleep(1);
            r = alarm(3);


2. 等待信号的到来

    pause


NAME
       pause - wait for signal

SYNOPSIS
       #include <unistd.h>

       int pause(void);

DESCRIPTION
       pause()  causes the calling process (or thread) to sleep until a signal
       is delivered that either terminates the process or causes  the  invoca‐
       tion of a signal-catching function.

RETURN VALUE
       pause()  returns  only when a signal was caught and the signal-catching
       function returned.  In this case pause() returns -1, and errno  is  set
       to EINTR.


    NAME
       pause - wait for signal

SYNOPSIS
       #include <unistd.h>

       int pause(void);

    pause用来等待一个信号的到来。 (要收到一个信号并处理它,pause 才返回)
    该函数会使调用进程(或线程)休眠直到一个信号的到来
    (信号到来,要么终止你的进程,要么执行你的信号处理函数)   
3. 捕捉信号(改变信号的处理方式)
    signal
    sigaction

 捕捉信号(改变信号的处理方式)
    信号处理方式是可以被遗传的(fork后子进程会继承父进程的信号处理方式)

    NAME
       signal - ANSI C signal handling

    SYNOPSIS
       #include <signal.h>

       typedef void (*sighandler_t)(int);  //新定义了一个类型

       sighandler_t: 函数指针。指向的函数是什么类型?指向的函数
                   返回值为void,带一个int的参数

    sighandler_t指向的函数类型,就是你要写的信号处理函数的类型。   


       sighandler_t signal(int signum, sighandler_t handler);
            signum:信号。表示要捕捉的信号
            handler:函数指针,指向的函数即为信号signum的新的处理
                函数。
            
            返回值:
                返回该信号上一次的处理方式(上一个处理函数的指针)
                失败返回SIG_ERR
    ====
    NAME
       sigaction - examine and change a signal action

        struct sigaction结构体表示跟一个信号相关联的处理操作

        struct sigaction表示一个信号处理方式的信息的结构体。
        
        struct sigaction {
               void     (*sa_handler)(int); //指向信号处理函数
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };


    SYNOPSIS
       #include <signal.h>

       int sigaction(int signum, const struct sigaction *act,           struct sigaction *oldact);

           struct sigaction sa;
           memset(&sa, 0, sizeof(sa));

           sa.sa_handler = sig_handler;


        sigaction(SIGINT, &sa, NULL);
    

NOTE:

    信号是通过软中断来实现的,你的信号处理函数,是在中断上下文(context),
    故在信号处理函数中调用的函数必须是线程安全的(可重入的函数).

0 0