2019-04-21 08:00:23 PecoHe 阅读数 3596

1.进程间通信介绍

  • 进程间通信(IPC)指的是2个任意进程之间的通信进程用户空间是相互独立的,一般而言是不能相互访问的。但很多情况下进程间需要互相通信,来完成系统的某项功能。进程通过与内核及其它进程之间的互相通信来协调它们的行为。
  • 一个进程在一个地址空间中它的不同模块之间通信都是很简单的(全局变量、通过函数形参实参传递)。但是2个不同的进程处于不同的地址空间,要互相通信很难。
  • 大部分程序是不需要进程间通信的,因为它们都是单进程的(可以多线程)。复杂、大型的程序(如GUI程序、服务器程序)才可能用到进程间通信,整个程序就设计成多个进程同时工作来完成的模式。

2.Linux内核提供的进程间通信机制

  • 无名管道和有名管道
  • SystemV IPC:信号量、消息队列、共享内存
  • Socket域套接字
  • 信号

3.管道

管道(一般就指无名管道)具有如下特点:

  • 管道的本质就是内核维护的一块内存(内核在内存中创建一个共享文件),管道是单向通信,半双工的,它有读端和写端。
  • 管道只能在父子进程间通信,父进程创建管理后fork子进程,子进程继承父进程的管道fd。

在这里插入图片描述
管道通信的函数:pipe、write、read、close
有名管道(实质就是可以在不是父子进程关系的而两个进程间通信的有名管道)具有如下特点:

  • 有名管道实质也是内核维护的一块内存,表现形式为一个有名字的文件。它不限父子进程,任意2个进程都可。有名管道固定一个文件名,2个进程分别使用mkfifo创建fifo文件,然后分别open打开获取到fd,然后一个读一个写。
  • 管道通信的函数:mkfifo、open、write、read、close

4.SystemV IPC

SystemV IPC通过一些专用API来提供进程间通信功能,分为信号量、消息队列、共享内存,其实质也是内核提供的公共内存。
消息队列

  • 本质上是一个队列,队列可以理解为(内核维护的一个)FIFO。
  • 工作时A和B2个进程进行通信,A向队列中放入消息,B从队列中读出消息。

信号量

  • 实质就是个计数器(其实就是一个可以用来计数的变量,可以理解为int a)。
  • 通过计数值来提供互斥和同步。

共享内存

  • 大片内存直接映射。
  • 类似于LCD显示时的显存用法。

5.剩余的2类进程间通信

1.信号
2.域套接字 socket。

2016-10-15 12:40:29 MR_Allen_Lwx 阅读数 1954

共享库so的认识:
即使不同进程调用同一个so文件,通过共享库并不能实现不同进程间的通信,因为同一个so被不同进程加载到不同的内存空间。这类似windows下的dll动态库。

下面以一个实例来介绍linux下so共享库的使用方法:
1、实现一个so库文件名称为listupper.so,so文件中实现一个函数,函数名为void upper(const char *src, char *desc),调用update后将参数src所指向的字符串中所有字符转化为大写字母,结果放入desc字符串中。分别用C语言编写一个程序test1,调用libupper.so中的upper函数,用C++语言编写一个程序test2,调用libupper.so中的upper函数。

第一步:生成so共享库文件
1、so文件不要有main函数,即使有也不会被执行。
2、编译:gcc要加上-fPIC选项,这可以使gcc产生与位置无关的代码。
3、链接:gcc要加上-shared选项,指示生成共享库文件。
4、共享库文件名要以lib开头,扩展名为.so
编译出so文件还需要头文件,头文件包含各函数的申明。

.SUFFIXES: .c .o

CC=gcc

SRCS=upper.c

OBJS=$(SRCS:.c=.o)
EXEC=libupper.so

all: $(OBJS)
    $(CC) -shared -o $(EXEC) $(OBJS)
    @echo '-------------ok--------------'

.c.o: 
    $(CC) -Wall -g -fPIC -o $@ -c $<

clean:
    rm -f $(OBJS)
    rm -f core*

/* upper.h*/

#ifndef UPPER_H_
#define UPPER_H_

#ifdef __cplusplus
extern "C"
{
#endif

void upper(const char *src, char *desc);

#ifdef __cplusplus
}
#endif

#endif /* UPPER_H_ */
/* upper.c */

#include <stdio.h>
#include <stdlib.h>
#include<string.h>

void upper(const char *src, char *desc)
{
    if ((src==NULL)|(desc==NULL))
    {
        return;
    }
    int i=0;
    char buf[1024];
    memset(buf,0,sizeof(buf));
    strcpy(buf,src);
    int len=strlen(buf);
    while(buf[i]!='\0')
    {
        if((buf[i]>='a')&&(buf[i]<='z'))
        {
            buf[i]=buf[i]-('a'-'A');
        }
        i++;
    }
    strcpy(desc,buf);
    desc[len]='\0';
}

生成libupper.so
这里写图片描述

第二步:如何使用.so文件(配置环境)
1、cd 回到宿主目录
2、vi .bash_profile 修改.bash_profile文件
3、添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. 添加当前路径
4、. .bash_profile 重新运行bash_profile文件
5、链接:gcc -L. -lupper -o a a.o
其中-L.意思为在当前路径寻找so文件
-lupper 意思是链接libupper.so这个库文件

.SUFFIXES: .c .o

CC=g++

SRCS=test2.cpp

OBJS=$(SRCS:.cpp=.o)
EXEC=test2

all: $(OBJS)
    $(CC) -L. -lupper -o $(EXEC) $(OBJS)
    @echo '-------------ok--------------'

.c.o: 
    $(CC) -Wall -g -o $@ -c $<

clean:
    rm -f $(OBJS)
    rm -f core*

c语言版运行结果
这里写图片描述
cpp版运行结果
这里写图片描述

2016-08-18 21:22:39 hzk8656511 阅读数 556

1:信号

信号是UNIX中所使用的进程间通信比较古老的一种方法,linux直接继承而来。信号的本质其实就是一种软中断。在有些程序中需要用到此种方法:比如最为经典的是在终端中输入kill命令,杀死某个进程,该命令就是通知内核来产生SIGKILL signal,来终止一个进程。这是一种比较典型的异步通信方式。
信号可以直接进行用户空间进程和内核空间进程之间的交互,内核进程也可以利用它来通知用户进程发生了哪些系统事件。可以在任何时候发给某个进程,而无需知道进程的状态。如果该进程当前并未处于执行状态,则该信号就由内核保存起来,知道该进程恢复执行再传递给它为止;如果一个信号被该进程设置为堵塞,则该信号的传递被延迟,知道其堵塞被取消时才被传递。

 kill -l命令行可以列出,系统支持的所有signal,它是由内核来定义的
 $ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

signal的缺点就是只支持有限的几种,用户无法定义,以及无法传递数据。

一个完整的信号生命周期可以分为3个重要阶段,这3个阶段可以由4个重要事件来刻画:信号产生,信号在进程中注册,信号在进程中注销,以及执行信号处理函数。见下图(来自于linux应用程序开发标准教程):

这里写图片描述

信号分为不可靠信号和可靠信号。
不可靠信号:如果发现该信号已经在进程中注册,那么久该忽略该信号。因此若前一个信号还未注销又产生了相同的信号就会产生信号丢失。发生不可靠的原因就是在上图中内核发生进程到信号处理函数之间, 有一个用户进程(上图)的信号注册和注销的时间窗口,造成就由可能在这个时间窗口之间,有信号又重新产生。
可靠信号:发生一个信号给进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此信号就不会丢失。
所有可靠信号都支持排队,而所有不可靠信号都不支持排队。

用户对信号的响应有3种方式:
忽略信号:即对信号不做任何处理。但有两个信号不能忽略,即SIGKILL以及SIGTOP
捕捉信号:定义信号处理函数,当信号发生时,执行响应的自定义处理函数
执行缺陷操作,linux对每种信号都规定了默认操作。

2:信号发送API

linux提供给信号发送函数主要有kill(), raise(), alarm()以及pause()

kill()函数,可以发生信号给进程或进程组
函数原型:
    int kill(pid_t pid, int sig)

 raise()可以运行进程向自身发生信号。
     int raise(int sig)

 pause()函数使调用进程挂起至到捕捉到一个信号
    int pause()

 下面是一个signal的例子
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>


int main()
{
    pid_t pid;
    int ret;


    if((pid= fork()) <0) 
    {   
        printf("Fork error\n");
        exit(1);
    }   

    if (0 == pid)
    {   

        printf("Child process is %d\n", getpid());
        raise(SIGSTOP);
        exit(0);
    } else 
    {        sleep(1);
        if ((waitpid(pid, NULL, WNOHANG)) == 0)
        {

            if (kill(pid, SIGKILL) == 0)
            {
                printf("Parent kill %d\n", pid);
            }
         }
         waitpid(pid,NULL, 0);
         exit(0);
     }
}

上述例子来自于《linux应用程序开发标准教程》:首先fork()创建一个子进程,接着为了保证子进程不在父进程调用kill()之前推出,在子进程中使用raise()函数向自身发生SIGSTOP信号,使子进程暂停。同时稍微做了修改,在父进程中增加了1s延迟,父进程中调用kill()进程发生信号到子进程中。

信号发生的另外一个函数alarm()
alarm()函数也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的实际到时,它就向进程发生SIGALARM函数。一个进程只有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所替代。
函数原型
unsigned int alarm(unsigned int seconds)

使用实例:
 #include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{

    int ret = alarm(5);
    pause();
    printf("Wake up\n");
}

3 signal()和sigaction()函数

上节中,我们将到针对信号的处理,用户可以对信号进行捕捉,并进行自定义函数,对该信号的处理。针对特定信号,挂节用户自定义行为,有两种方法来实现,第一种就是signal函数,另外一种就是信号集函数组。

signal函数原型:
    void (*signal(int signum, void (*handler)(int))) (int)
    其中 参数 signum:为制定信号代码
             void (*handler)(int) 为自定义的信号处理函数指针。

    返回值, 出错 -1
            成功:则是以前的信号处理配置。

在《unix环境高级编程》中为了简化对signal函数的使用,可以使用typedef(),将其简化
typedef void Sigfunc(int);
可以将signal简化为
Sigfunc signal(int, Sigfunc)

下面是使用signal的一个例子

#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);
}
运行结果:
$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have get SIGINT (按 ctrl-c 组合键)
$ ./signal
Waiting for signal SIGINT or SIGQUIT...
I have get SIGQUIT (按 ctrl-\ 组合键)   

注意:当一个进程调用fork()时,其子进程继承父进程的信号处理方式。因此子进程在开始时复制了父进程的存储映像,所以信号捕捉函数的地址在子进程中是有意义的。

sigaction()函数也能注册一个自定义的用户处理函数.《unix环境高级编程》中指出 sigaction函数是检查或修改与指定信号相关联的处理动作(或同时执行这两种操作)。主要是取代UNix早期版本使用的signal函数,linux也完全继承了该接口。
函数原型:
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oldact)
参数: int signo 是检测或修改其具体动作的信号编号。若act指针为非空,则要修改其动作,如果oldact为非空,则系统由oldact 指针返回信号的上一个动作。
其中 sigaction的结构:
struct sigaction{
void (sa_handler)(int); /*add of signal handler 信号制定处理函数/
sigset_t sa_mask; /additional signal to block 可以指定信号处理程序执行过程中哪些信号应对被屏蔽,在调用信号捕捉函数之前,该信号集要加入到信号的屏蔽字中/
int sa_flags /* signal options,包涵了许多标志位*/
void (sa_sigaction)(int, sigino_t , void );/*alternate handler /

sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时在将进程的信号屏蔽字复位原先值。这样,在调用信号处理程序时就能阻塞某些信号。在信号处理程序被调用时,操作系统建立的新信号屏蔽字包括正被递送的信号。因此保证了在处理一个给定的信号时,如果这种信号再次发生,那么它会被堵塞到对前一个信号的处理结束为止。若同一信号多次发生,通常并不将它们排队,所以如果在某种信号被堵塞时它发生了五次,那么对这种信号解除堵塞后,其信号处理函数通常只会被调用一次。
sg_sigaction 字段是一个替代的信号处理程序,当在sigaction结构中sa_flags标志位使用了SA_SIGINFO标志时,使用该信号处理程序。对于sa_sigaction字段和sa_handler字段这两者,其现实可能使用同一个存储区,所以应用只能一次使用这两个字段中的一个。
将上述使用signal的例子,用sigaction()函数来实现:

#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()
{
    struct sigaction action;

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

    /*init action */
    action.sa_handler = my_func;
    sigemptyset(&action.sa_mask);
    action.sa_flags =0;

    sigaction(SIGINT, &action, 0);
    sigaction(SIGQUIT, &action, 0);

    pause();
    exit(0);

}
执行结果和signal处理函数一样。

必须要注意的是sigemptyset函数初始化act结构的sa_mask成员。不能保证:act.sa_mask = 0会做同样的事情
与signal()函数相比, sigaction()函数多了许多功能

4: 信号集

上述都是针对一个信号进行操作,有的时候我们需要有一个能表示多个信号-信号集的数据类型,同时对多个信号进行设置。针对信号集,linux提供了一系列API,将这个API按照调用的先后次序可以分为:创建信号集合,注册信号处理函数以及检测信号。
其中与创建信号集合相关的函数:
int sigemptyset(sigset_t *set)
将信号集合初始化为空
int sigfillset(sigset_t *set)
将信号集合初始化为包涵所有已定义的信号集合
int sigaddset(sigset_t *set, int signo)
将指定信号加入到信号集合中去。
int sigdelset(sigset_t *set, int signo)
将指定信号从信号集合中删除
int sigismember(const sigset_t *set, int signo)
查询指定信号是否在信号集合之中。

检测信号函数:
int sigprocmask(int how, const sigset_t restrict * set, sigset_t * oset)
该函数可以检测或更改其信号屏蔽字。
首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。
若set为非空指针,则参数how指示如何修改当前信号屏蔽字。
how 参数
SIG_BLOCK, 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望堵塞的加信号。
SIG_UNBLOCK, 该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集交集的补集。set包含了我们希望解除堵塞的信号。
SIG_SETMASK 该进程新的信号屏蔽字将被set指向的信号集的值代替
可以通过使用SIG_BLOCK,和SIGUNBLOCK来堵塞所选择的信号,是指在一定的代码区间中不响应该信号。通过使用这种技术可以保护不希望由信号中断的代码临界区。注意它只能堵塞信号,当堵塞解除后被堵塞的信号还会发生。即只能延迟信号发生,不能使该信号消失。

int sigpending(sigset_t *set);
返回信号集,其中的各个信号对于调用进程是堵塞的而不能递送,因而也一定是当前未决的。该信号集通过set参数返回。          

注册信号处理函数: 主要决定了进程如何处理信号,使用sigaction()函数进程注册。
注意信号集里面的信号并不是真正要可以处理的信号,只有当信号的状态处于非堵塞状态时才会真正起到作用,才能调到处理函数。因此使用信号集的一般流程是首先使用sigprocmask()函数检测并更改信号屏蔽字。然后使用sigaction()函数来定于进程接收到特定信号之后的行为。检测信号时信号处理的后续步骤,因为被堵塞的信号不会传递给进程。所以这些信号就处于“未处理”状态。sigpending()函数允许进程检测“未处理”信号,并进一步决定对它们作何处理。通过这种技术也可以保护不希望由信号中断的代码临界区。
如下图所示主要展示了信号操作的处理流程:

![这里写图片描述](http://img.blog.csdn.net/20160824203154799)

将上述例子SIGQUIT, SIGINT信号修改为信号集处理方式:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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()
{
    sigset_t set, pendset;
    struct sigaction action1, action2;

    /* init set */
    if (sigemptyset(&set) < 0)
    {
        perror("sigemptyset");
        exit(1);
    }

    /* add SIGQUIT signal into set */
    if(sigaddset(&set, SIGQUIT) < 0)
    {
        perror("sigaddset");
        exit(1);
    }

    /* add SIGINT signal into set*/
    if (sigaddset(&set, SIGINT) < 0)
    {
        perror("sigaddset");
        exit(1);
    }

    /*set SIGINT handler */
    if(sigismember(&set, SIGINT))
        {
        sigemptyset(&action1.sa_mask);
        action1.sa_handler = my_func;
        action1.sa_flags = 0;
        sigaction(SIGINT, &action1, NULL);
    }


    /* set SIGQUIT handler */
    if(sigismember(&set, SIGQUIT))
    {

       sigemptyset(&action2.sa_mask);
       action2.sa_handler = my_func;
       action2.sa_flags = 0;
       sigaction(SIGQUIT, &action2,NULL);
    }


    /* set signal mask */
    if (sigprocmask(SIG_BLOCK, &set, NULL) < 0)
    {
        perror("sigprocmask");
        exit(1);
    }
    else
    {
        printf("Signal set was blocked, Press any key!");
        getchar();
    }

    if (sigprocmask(SIG_UNBLOCK, &set, NULL) < 0)
    {
        perror("Sigprocmask");
        exit(1);
    }
    else
    {
        printf("Signal set is in unblock state\n");
    }


    while(1);
    exit(0);
}

运行结果:
root:/repo/training/fork# ./a.out 
Signal set was blocked, Press any key!^C^\
I have get SIGQUIT
I have get SIGINT
Signal set is in unblock state
^\I have get SIGQUIT
^CI have get SIGINT
^\I have get SIGQUIT
^Z
[1]+  Stopped                 ./a.out

下面一个例子是关于sigprocmask()函数 how为0, set为NULL参数的用法:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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()
{
    sigset_t set, pendset;

    /* init set */
    if (sigemptyset(&set) < 0)
    {   
        perror("sigemptyset");
        exit(1);
    }   

    /* add SIGQUIT signal into set */
    if(sigaddset(&set, SIGQUIT) < 0)
    {   
        perror("sigaddset");
        exit(1);
    }   

    /* add SIGINT signal into set*/
    if (sigaddset(&set, SIGINT) < 0)
    {   
        perror("sigaddset");
        exit(1);
    }   

    /* set signal mask */
    if (sigprocmask(SIG_BLOCK, &set, NULL) < 0)
    { 
           perror("sigprocmask");
        exit(1);
    }

    if(sigprocmask(0,NULL, &pendset) < 0)
    {
        perror("sigprocmask");
        exit(1);
    }


    if(sigismember(&pendset, SIGINT))
    {
        printf("Block SIGINT\n");
    }


    if (sigismember(&pendset, SIGQUIT))
    {
        printf("Block SIGQUIT\n");
    }

}

运行结果:
root:/repo/training/fork# ./a.out 
Block SIGINT
Block SIGQUIT

下面写一个子进程和父进程之间使用signal进行通信的例子:

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

void my_func(int sign_no)
{

    if(sign_no == SIGINT)
    {
        printf("I have got SIGINT\n");
    } else if ( sign_no == SIGQUIT)
    {
        printf("I have got SIGQUIT\n");
    }

}
int main(void)
{
    pid_t result;

    /* create new process */
    result = fork();
    if (-1 == result)
    {
        printf("Fork error\n");
    } else if ( result ==0 ) /*children process */
    {
        printf("Child process id is %d\n", getpid());
        signal(SIGINT, my_func);
        signal(SIGQUIT, my_func);
        while(1)
        {
            printf("loop \n");
        }
    } else
    {

        sleep(1);
        printf("Father process id is %d\n", getpid());
        printf("Send SIGINT signal to %d\n", result);
        kill(result, SIGINT);
        printf("Send SIGQUIT signal to %d\n", result);
        kill(result, SIGQUIT);
        printf("Send SIGKILL signal to %d\n", result);
        kill(result, SIGKILL);
              kill(result, SIGKILL);
    }

    return result;
运行结果:
roo:/repo/training/fork# ./a.out 
Child process id is 4631
Father process id is 4630
loop 
loop 
loop 
loop 
loop 
loop 
loop 
loop 
Father process id is 4621
Send SIGINT signal to 4622
Send SIGQUIT signal to 4622
Send SIGKILL signal to 4622



以上实验代码github地址:https://github.com/zhikunhuo/training.git
2012-11-17 21:03:55 ssdsafsdsd 阅读数 1715

 linux进程间通信简介

      进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源。但是,进程不是孤立的,不同的进程之间需要信息的交换以及状态的传递,因此需要进程间数据传递、同步与异步的机制。

      这些机制并不是由哪些进程来进行直接管理,而是由操作系统进行管理。linux中提供了大量的进程间通信机制来实现同一个主机间的进程间的通信。同时,对于网络间的主机进行通信的机制,linux也同时提供。看下面的图

一、总体简介

【1】同主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO),消息队列(message queue)和共享内存(share memory)。

【2】同主机进程间同步机制:信号量(semaphore)

【3】同主机进程间异步机制:信号(signal)

【4】网络主机间数据交互机制:套接字(socket)

二、unix进程间通信机制

1、无名管道PIPE,只能用于具有亲缘关系(父子进程)间的通信,无名管道在通信双方退出后自动消失。

2、有名管道:克服了无名管道的瞬时性的问题,采用管道文件来实现同一主机间的任意两个进程间的数据的传递。

3、信号:信号其实是一种软中断机制,采用一种异步的机制,在信号发生时处理特定的时间,但是要注意的是,这同硬件中的中断,还是有一定区别的。

三、system Ⅴ进程间通信

1、消息队列:消息队列主要用来实现两个进程间少量的数据的通信,并且接收方可以根据消息的类型,选择接收消息。http://blog.csdn.net/ssdsafsdsd/article/details/8192143

 

2、信号量:信号量是一种进程间的同步进制。对于二元信号量,可以实现两个进程间的同步,对于广泛的信号量,可以表示资源的使用量。

http://blog.csdn.net/ssdsafsdsd/article/details/8204997

3、共享内存:共享内存进制可以实现两个进程间的大数据量的通信,其在内存中专门开辟出一个独立的内存,然后映射到各自的进程之中,进行数据的传输,其通信效率较高。

http://blog.csdn.net/ssdsafsdsd/article/details/8205016

     对于system Ⅴ进程间的通信机制,可以使用ipcs命令进程查看,如下

四、不同主机之间的网络通信

      这部分主要是网络编程的知识,也就是linux网络socket编程,在这里不做重点的介绍。但是同样给出关于socket编程的本博客中出现的代码的几个链接。

      1、使用AF_UNIX实现本机数据流通信示例(TCP协议)

       2、使用AF_INET实现UDP点对点通信示例(UDP协议)

五、对于各种通信机制的实例

      在本blog中,没有对各种通信机制进行详细的讲解,读者想学习可以去找本书,基本讲linux编程的书籍都会讲这个,但是对于每种的通信机制我几乎都写了文章,主要以实例的形式进行出现。在看完基础内容后,把这个例子看懂,会有一定收获。对于已经整理好的内容,我会把链接写在上面的分类后面,对于还没时间整理或者写的部分,没有给出链接,可以在我的文章里面找下。整理好后,我会把链接加上去。例如,对于进程间信号量的学习,我给出了一个使用信号量的例子来实现生产者,消费者问题。这些例子有的来自我所看的书中,也有些我我修改后的,或者自己写的。

 

OVER!!!!!!!!!!

2019-05-02 23:20:30 Hongwei_1990 阅读数 159
Linux进程间通信方式包括管道匿名管道命名管道)、信号信号量共享内存消息队列套接字等方式。
序号 描述
1 管道匿名管道命名管道
2 信号
3 信号量
4 共享内存
5 消息队列
6 套接字Socket

1、管道

管道一个进程连接数据流另一个进程通道,它通常被用来把一个进程的输出端连接到另一个进程的输入端。在Linux命令中通常用符号|来表示管道。例如:

$ ps -ef | grep init

此命令中psprocess status)是一个独立的进程grep也是一个独立的进程,中间的管道ps原本要输出到屏幕数据输出到grep中,作为grep这个进程的输入
管道分为匿名管道命名管道两种:
序号 管道 描述
1 匿名管道 主要用于两个有父子关系进程间通信
2 命名管道 主要用于没有父子关系进程间通信

2、共享内存

共享内存是允许两个不相关进程访问同一个逻辑内存进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。两个进程使用共享内存通信机制如图所示:

在这里插入图片描述

3、信号量

多进程编程中需要关注进程间同步互斥问题。同步是指多个进程为了完成同一个任务相互协作运行,而互斥是指不同的进程为了争夺有限的系统资源(硬件或软件资源)而相互竞争运行
信号量是用来解决进程间同步与互斥问题的一种进程间通信机制,它是一个特殊的变量,变量的值代表着关联资源的可用数量。若等于0则意味着目前没有可用的资源
根据信号量的值可以将信号量分为二值信号量计数信号量
序号 信号量 描述
1 二值信号量 信号量只有01两种值。若资源被锁住,信号量值为0,若资源可用则信号量值为1
2 计数信号量 信号量可在0到一个大于1的数(最大 32767)之间取值。该计数表示可用资源的个数
信号量只能进行两个原子操作P操作V操作
P原子操作V原子操作的具体定义如下:
序号 操作 描述
1 P操作 (1)如果有可用的资源信号量值>0),则占用一个资源将信号量值减1)。
(2)如果没有可用的资源信号量值=0),则进程阻塞直到系统将资源分配给该进程
(进入信号量等待队列,等到资源后再唤醒该进程)。
2 V操作 如果在该信号量等待队列中有进程等待资源,则唤醒一个阻塞进程
如果没有进程等待它,则释放一个资源给信号量值加1)。

linux进程间通信

阅读数 7

没有更多推荐了,返回首页