精华内容
下载资源
问答
  • Linux 进程通信

    万次阅读 2018-05-06 14:31:38
    Linux 进程通信 1.传统进程通信 1.1 信号 信号机制是在软件层次上对中断机制的一种模拟。 信号的捕获与处理也成为系统的“软中断”机制。 1.1.1 常用信号 每个信号都有一个编号和宏定义的名称,这些名字...

    Linux 进程通信

    1.传统进程通信

    1.1 信号

    信号机制是在软件层次上对中断机制的一种模拟。
    信号的捕获与处理也成为系统的“软中断”机制。

    1.1.1 常用信号

    每个信号都有一个编号和宏定义的名称,这些名字都已SIG开头。宏定义在signal.h头文件中。

    1.1.2 信号的处理

    缺省操作,默认的信号处理方式。SIG_DFL
    忽略信号,进程忽略接收到的信号,但SIGKILL和SIGSTOP不能忽略。SIG_DFL
    捕获信号,提供一个信号处理函数,而不是使用默认的信号处理方式。要求内核此时切换到用户态。

    CUP的两种状态:内核态和用户态
    点击这里有详细介绍

    1.1.3 信号处理函数
    1. signal() 定义进程收到信号后的处理方法
    #include<signal.h>
    void (*signal(int signum, void(*func)(int)))(int);

    signum: 信号名称,整数值
    func:一个指针,指向信号处理函数。该参数还可以是SIG_DFL或SIG_IGN(前者为默认的缺省操作的函数,后者为默认的忽略信号的函数)
    调用成功,返回原来的信号处理函数的指针;失败则返回SIG_ERR

    1. kill() 向进程或进程组发送一个信号
    #include<signal.h>
    int kill(pid_t pid, int sig);

    pid: 接收信号的进程ID。当pid为0时,发送给与当前进程同组的所有进程。
    sig: 信号名称
    信号发送成功返回0;信号发送失败返回-1。

    1.2 管道

    连接两个进程的连接器,数据在其中单向流动。

    1.2.1 普通管道

    位于内存中。只能用于有亲缘关系进程(父子进程)之间的单向通信。
    当一个管道建立后,将获得两个文件描述符,分别用来对管道进程读取和写入。通常将其称为管代的写入端和管道的读取端。

    这里的写入端和读取端指的是对管道的写入和读取。

    pipe() 创建一个普通管道

    #include<unistd.h>
    int pipe(int filedes[2])

    filedes[0]: 读端口的文件描述符
    filedes[1]: 写端口的文件描述符
    调用成功返回0;调用失败返回-1。

    普通管道读写规则:
    1. 读规则
    关闭管道的写端: close (fd[1])
    读出: read(fd[0], buf, size); 从管道读端口fd[0]读出size个字符放到buf中
    读出后关闭管道的读端: close(fd[0])
    2. 写规则
    关闭管道的读端: close(fd[0])
    写入: write(fd[1], buf, size); 把buf中的长度为size的字符送到管道写端口fd[1]
    写入后关闭管道的写端: close (fd[1])

    两个进程通过一个管道只能实现单向通信,如果需要父子进程双向通信,就必须另打开一个管道。
    在管道满时,写管道操作将被阻塞;在管道空时,读管道操作将被阻塞。

    例子:

    /*
     * 利用两个管道进行双向通信,实现父子进程协作把整数x1加到10
     */
    #include<unistd.h>
    #include<sys/types.h>
    #include<stdlib.h>
    #include<stdio.h>
    int main()
    {
        int x = 1;
        int pipe1[2], pipe2[2];
        pid_t val;
        pipe(pipe1);
        pipe(pipe2);
        if((val = fork()) <0 )
            exit(1);
        if(val == 0)
        {
            close(pipe1[1]);
            close(pipe2[0]);
            while(x<=9)
            {
                read(pipe1[0], &x, sizeof(int));
                printf("Child:%d, read:%d\n", getpid(), x++);
                write(pipe2[1], &x, sizeof(int));
            }
            close(pipe1[0]);
            close(pipe2[1]);
        }
        else
        {
    
            close(pipe2[1]);
            close(pipe1[0]);
            while(x<=10)
            {
                write(pipe1[1], &x, sizeof(int));
                read(pipe2[0], &x, sizeof(int));
                printf("Parent:%d, read%d\n", getpid(), x++);
    
            }
            close(pipe2[0]);
            close(pipe1[1]);
        }
        return 0;
    }
    
    1.2.2 命名管道

    位于文件系统中。可以实现不同进程间的双向通信。

    mkfifo() 创建命名管道,将产生一个FIFO文件

    #include<sys/types.h>
    #include<unistd.h>
    int mkfifo(const char * pathname, mode_t mode);

    pathname:创建的FIFO文件名
    mode:规定FIFO文件的读写权限
    调用成功时返回0;调用失败返回-1

    命名管道必须同时读和写,否则会阻塞

    2. System V IPC 进程通信

    2.1 IPC

    进程间通信(Inter-Process Communication)

    2.1.1 查看ipc对象的信息

    ipcs [-asmq]
    -a:all,查看全部的IPC对象信息
    -s:signal,查看信号量集
    -m:memory,查看共享内存
    -q:queue,查看消息队列

    IPC标识符由内核分配给IPC对象,在系统内部唯一,给系统看
    IPC关键字由程序员选择,全局唯一,给人看,32字长
    每个进程都可以建立一个键值为IPC_PRIVAE的私有IPC对象

    ftok() 生成唯一的键值

    #include<sys/types.h>
    #include<sys/ipc.h>
    key_t ftok(char *filename, int id);

    filename:文件名,可以使用绝对路径或相对路径
    id:整型变量。可通过指定相同文件的文件名和不同的整型变量,生成不同的键值。
    调用成功返回所生成的键值;失败返回-1。

    /*
     *
     利用消息队列实现父子进程的通信。
     父进程创建消息队列,并向消息队列发送消息;子进程接受消息;
     父进程等待子进程接收消息后删除消息队列。
     *
     */
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/msg.h>
    #include<string.h>
    struct msgbuf{
        long msgtype;
        char msgtext[255];
    };
    
    int create_queue(int key)
    {
        int qid = -1;
        if(key == -1)
            perror("ftok");
        else if( (qid= msgget(key, IPC_CREAT|0666)) < 0)
            perror("msgget");
        else return qid;
    }
    
    void  send_message(int qid, struct msgbuf *msg, char *message)
    {
        strcpy(msg->msgtext, message);
        if(msgsnd(qid, msg, strlen(msg->msgtext), 0) < 0)
            perror("msgsnd");
    }
    
    void recive_message(int qid, struct msgbuf *msg)
    {
        memset(msg->msgtext, 0, 255);
        if(msgrcv(qid, msg, sizeof(msg->msgtext), msg->msgtype, 0) < 0)
            perror("msgrcv");
    }
    
    void distory_queue(int qid)
    {
        struct msqid_ds qds;
        if(msgctl(qid, IPC_RMID, &qds) < 0)
            perror("msgctl");
    }
    
    int main()
    {
        struct msgbuf msg;
        int qid;
        pid_t pid;
        key_t key = ftok(".", 1);   //生成唯一的键值
        msg.msgtype = 1;
        qid = create_queue(key);
        pid = fork();
        if(pid < 0)
            perror("fork");
        else if(pid > 0)
        {
            send_message(qid, &msg, "This is the input!");
            printf("Parent: Send to the message queue successfully!\n");
            printf("The message sent is :%s\n", msg.msgtext);
            wait(NULL);
            distory_queue(qid);
        }
        else
        {
            recive_message(qid, &msg);
            printf("Clild: Receiving from the message queue:%s\n", msg.msgtext);
        }
    
    
        return 0;
    }
    

    2.2 消息队列

    以异步方式为通信频繁但数据量少的进程通信提供服务
    存储消息得的线性表,按照先进先出的原则,对数据输入输出。

    系统中消息队列最多个数:256
    每条消息的最大字节数:8KB
    每个队列能容纳的最大字节数:16KB

    2.2.1 消息结构模板msgbuf

    include/linux/msg.h

    struct msgbuf
    {
        long msgtype;   //消息类型
        char mtext[1];  //消息内容,可自行定义消息的长度
    };
    2.2.2 基本操作:
    1. 创建或打开消息队列:msgget()
    #inclide<sys/types.h>
    #include<sys/msg.h>
    int msgget(key_t, int flags);

    key:键值
    flags:标识和权限信息的组合。若标识部分为0,则获取一个已存在的消息队列的标识符;若为IPC_CREAT:如果消息队列不存在则创建,存在则引用。
    调用成功,返回消息队列的标识符;否则,返回-1。

    1. 向消息队列发送消息:msgsnd()
    #inclide<sys/types.h>
    #include<sys/msg.h>
    int msgsnd(int msgid, struct msgbuf *msgp, size_t size, int flag);

    msgid:消息队列的标识符
    msgp:指向消息结构的指针
    size:消息内容的长度
    flag:发送消息可选标志。若flag为0,则当消息队列满时,发送操作阻塞;若为IPC_NOWAIT时,当消息队列满时,立即返回-1。
    调用成功,返回0;否则,返回-1。

    1. 从消息队列读出消息:msgrcv()
    #inclide<sys/types.h>
    #include<sys/msg.h>
    int msgrcv(int msgid, struct msgbuf *msgp, \
                  size_t size, long type, int flag);

    msqid:消息队列的标识符
    msgp:消息结构指针
    size:要读取消息的长度
    type:要读取消息的类型
    flag:接收消息可选标志。若flag为0,当消息队列为空时,进程阻塞;若flag为IPC_NOWAIT,当消息队列空时,进程不阻塞,立即返回-1;若flag为MSG_NOERROR,当消息长度大于接收缓冲区长度,截断消息返回,小于等于则不接受该消息,出错返回。
    调用成功,返回实际读取到的消息数;否则,返回-1。

    1. 删除消息队列:msgctl()
    #include <sys/msg.h>
    int msgctl(int msgid, int cmd, struct msgid_ds *buf);

    msgid:消息队列的标识符
    buf:指向msgid_ds结构的指针
    cmd:控制命令。若cmd为IPC_RMID, 则删除消息队列;若为IPC_STAT,则获取消息队列的结构,保存在buf所指向的缓冲区中;若为IPC_SET,则按照buf指向的结构来设置该消息队列的结构。
    调用成功,返回0;否则,返回-1。
    获取和修改消息队列属性信息
    查询消息队列描述符
    修改消息队列许可权
    删除该队列等

    2.3 共享主存

    2.4 信号量集

    远程通信

    展开全文
  • linux 进程通信

    2011-05-24 22:48:00
    linux 进程通信

    2011-3-27

    现在linux下的进程间通信方式主要有以下几种

    (1)                管道以及有名管道: 管道可以用于有亲缘关系的进程通信,而有名管道除了具有管道的功能外,还允许无亲缘关系进程间通信。

    (2)                信号:信号是在软件层次上的中断机制的一种模拟

    (3)                消息队列:消息的链表,具有写权限的进程可以向消息队列中按照一定的规则添加新消息

    (4)                共享内存:对于共享内存,需要同步机制,如信号量和互斥锁等

    (5)                Socket:这是更为一般的通信机制,可以用于不同机器之间进程通信。

    一.     管道和有名管道(FIFO):

    管道是linux中一种重要的机制,它是将一个程序的输出直接连接到另一个程序的输入,对于ls –l |grep XX这样的操作,使用的就是管道的原理。

    管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read,write,但是它不是普通的文件,不属于任何文件系统,只是存在于内存中。

    1)创建管道

    #include<unistd.h>

    管道的创建使用pipe函数,通过得到两个描述符,fd[0],fd[1],实现读写,fd[0]用于读,fd[1]用于写

    Int pipefd[2];

    Pipe(pipefd);

    2)管道读写

    事实上,在一个进程中对管道读写对于实现进程间通信没有任何意义,所以需要通过fork函数创建子进程,通过继承父进程创建的管道,然后再去除不必要的fd[0],fd[1],就能实现父进程写子进程读和子进程写父进程读的效果。父进程可以创建许多子进程,则这些子进程只要关闭相应的端口就可以实现子进程之间的通信

     

    3)标准流管道

    Popen,pclose,对于popen创建的管道,必须使用标准IO函数进行操作,而不能使用read,write这些不带缓冲的I/O函数

    Popen完成的工作:

    创建一个管道

    Fork一个子进程

    在父子进程中关闭不需要用到的文件描述符

    执行exec函数族调用

    执行函数中指定的命令

    例如,

    FILE *fp;

    Char *cmd=”ps”;

    Char buf[1024];

    Fp=popen(cmd,”r”);//r为标准输出(将输出放到缓存中,w为标准输入,

    While(fgets(buf,1024,fp)!=NULL)//通过调用带I/O缓存的函数,读取数据

               {…}  

    Pclose(fp);

     

    4FIFO有名管道

    有名管道(称作FIFO)最大的好处是实现互不相关的进程之间的通信。读管道可以通过路径名来指出,并且在文件系统中是可见的。在建立管道之后,两个进程可以把它当做普通文件一样进行读写操作,不过FIFO是遵循先进先出原则的。对于管道以及FIFO的读总是从开始出返回数据,对其写则是将数据添加到尾部,不支持lseek()等文件定位操作。

    还可以使用mknod管道名p来创建有名管道。

    对于阻塞和非阻塞,记录如下,

    读进程:O_NONBLOCK

    若管道阻塞, FIFO无数据,则读进程一直阻塞直到有数据写入

    如管道非阻塞,不论FIFO中是否有数据,都会立刻进行读操作。

    写进程:

    若管道阻塞,FIFO中数据满,则一直阻塞直到读进程读取数据。

    若管道非阻塞,FIFO中没有读操作,则写进程立即执行读操作。

    #include sys/types.h sys/state.h

    Int mkfifo(const char *filename,mode_t mode)

    二.     信号实现进程通信

    类似于软中断

    1. 信号发送与捕捉

    Kill raise

    子进程 raise(SIGSTOP) 子进程发出SIGSTOP信号

    父进程 收集子进程发出的信号

               Waitpid(pid,NULL,WNOHANG)

               Kill(pid,SIGKILL) 调用kill进行相应的处理

    alarm pause

        alarm alarm(5) 5秒之后,发送SIGALARM信号,SIGALARM默认的信号操作为终止进程

        pause用于将调用进程挂起直至捕捉到信号为止,这样alarm pause就形成了sleep功能。

           2. 信号的处理

                  有简单的signal和信号集函数处理

                  只说简单的signal处理

                  Signal(SIGINT,func);

                  Signal(SIGQUIT,func);

                  即收到信号后,调用对应的处理函数。保证程序的安全退出常用到。

    三.     共享内存

    共享内存是一种最为高效的进程间通信方式。为了实现多个进程交换信息,内核专门留出一块内存区,这段内存区可以有需要访问的进程将其隐射到自己的私有数据空间,进程可以读写这块内存区而不需要进行数据的拷贝,从而提高效率。当然访问需要同步机制。

    #include sys/types.h sys/ipc.h sys/shm.h

    Shmget shmat shmdt shmctl

    shmget(key_t key,int size,int shmflag)

    Key:IPC_PRIVATE,返回shmid

    Shmflag:同open的权限位,0666

    shmat(int shmid,const void *shmaddr,int shmflag)

    shmaddr:将共享内存attach到指定位置,如果0则将这段内存映射到调用进程的地址空间

    shmdt(const void * shmaddr)

    shmctl(int shmid,int cmd,struct shmid_ds *buf)

    cmd控制命令IPC_RMID删除共享内存

    shmctl(shmid,IPC_RMID,NULL);

    Ipcs,用于查看报告进程间通信机制状态的命令,可以查看共享内存,消息队列等各种进程间通信机制的情况,这里使用system函数用于调用shell命令”ipcs”

    这里查看共享内存,使用system” ipcs –m ”;

    调试程序时,有时会用CTRL+C发送中断信号结束程序,这个时候申请的共享内存得不到释放,这个时候处理两种方法:

    1.     在收到这个信号时,先释放共享内存在退出程序

    2.     使用linux下的命令ipcrm shm shmid来释放

    四.     消息队列

    消息队列的实现包括创建和打开消息队列,添加消息,读取消息和控制消息队列。

    创建和打开消息队列 msgget

    添加消息队列 msgsnd

    读取消息队列 msgrcv 可以指定读取某一条消息

    控制消息队列 msgctl  IPC_RMID 从系统内核中移走消息队列

    消息结构:

    Struct msgbuf{

    Long mtype;

    Char mtext[SIZE];

    }

    例如,

    Key_t key;

    Key=ftok(“.”,’a’) 通过ftok产生key

    Int qid=msgget(key,IPC_CREAT | 0666)

    Fgets(&msg->mtext,SIZE,stdin)

    Msgsnd(qid,&msg,len,0)

    Msgrcv(qid,&msg,SIZE,0)

    Msgctl(qid,IPC_RMID,NULL)

    以上就完成消息队列的一些列操作。

    五.Socket

           将结合电源管理程序来说明

    展开全文
  • linux进程通信

    2017-10-11 15:26:56
    学习linux进程通信即可以了解系统的运行方式,也可以加深对线程通信的了解。 linux通信可以使用信号量,自旋锁,systemV,管道通信,套接字通信。 1、信号量如何保证原子性,使用atomic原子操作;非原子操作修改是先...

    学习linux进程通信即可以了解系统的运行方式,也可以加深对线程通信的了解。
    linux通信可以使用信号量,自旋锁,systemV,管道通信,套接字通信。
    1、信号量如何保证原子性,使用atomic原子操作;非原子操作修改是先将数据读入CPU,然后运算,再将数据写入内存,这三个步骤中是无法保证原子性。适用于代码量大,进程会休眠。
    2、自旋锁适用与短代码量,这样线程不会休眠,否则会影响速度和效率。
    3、systemV 信号量,消息队列,共享内存。
    4、管道通信,子进程fork了父进程,所以也复制了父进程中的管道对象,所以可以跨进程通信。
    5、套接字网络通信

    展开全文
  • Linux进程通信

    千次阅读 2019-05-03 16:41:09
    信号是Linux进程间异步通信的唯一机制,用于通知进程一个特定的事件并强迫进程执行对应的处理程序,如用户在键盘上按crtl+c,内核会给当前控制台上正在运行的进程发送一个SIGINT的信号,进程收到该信号执行默认处理...

    目录

    一、信号

     二、管道与FIFO文件

    三、System V IPC

    四、POSIX消息队列

    五、Socket套接字


    一、信号

         信号是Linux进程间异步通信的唯一机制,用于通知进程一个特定的事件并强迫进程执行对应的处理程序,如用户在键盘上按crtl+c,内核会给当前控制台上正在运行的进程发送一个SIGINT的信号,进程收到该信号执行默认处理程序,即终止进程。在代码中信号是一组预定义的无符号整数,用两个字节来保存,所以最多是64个,执行kill -l命令会列出Linux支持的所有的信号,如下图:

    其中前31个是继承自Unix的标准信号,其余的都是POSIX标准引入的扩展信号,叫做实时信号,两者区别如下:

    1、当一个标准信号被阻塞时,后续发送给同一进程的同一信号则都会被丢弃。实时信号不会丢弃,而会进入一个待处理队列排队,同一类型信号按照发送的顺序处理。对不同类型的信号按照下列顺序处理:SIGILL(4),SIGTRAP(5),SIGBUS(7),SIGFPE(8),SIGSEGV(11), SIGSYS(31),其他标准信号(优先处理取值小的),实时信号。

    2、标准信号传递到目标进程时没有携带任何信息,实时信号可以携带额外的如发送方相关的信息。

    3、实时信号处理的优先级高于标准信号

    4、实时信号没有明确的含义,由使用者自己来决定如何使用,标准信号有明确的含义和用途。

    5、实时信号的默认都是结束当前的进程,标准信号有多种默认操作。

          按照信号的用途可以分为以下几类:

    1、进程控制,如SIGHUP挂机进程,SIGKILL强制终止进程,SIGTERM终止进程,SIGSTOP停止进程执行等

    2、进程执行异常通知,如SIGILL非法指令,SIGABRT异常结束,SIGFPE浮点计算异常,SIGSEGV无效内存引用等

    3、硬件错误通知,如SIGBUS总线错误,SIGSTKFLT协处理器栈错误,SIGPWR电源故障

    4、跟踪进程执行,只有一个SIGTRAP

    5、用户键盘操作通知,如SIGINT对应键盘的ctrl+c, SIGQUIT对应键盘的Ctrl+\,与SIGINT区别是会执行内存数据dump操作

    6、定时通知,如SIGALRM,达到进程设置的时间后通知进程。

         进程接收信号的本质是内核将该信号写入进程描述符中信号相关的字段中,进程处理信号有三种方法:

    1、显示的忽略信号;

    2、执行与信号相关的默认操作,默认操作取决于信号的类型,包含以下几种:Treminate(终止进程),Dump(终止进程并尽可能做核心转储),Ignore(忽略信号),Stop(停止进程执行,即把进程置为TASK_STOPPED状态),Continue(让TASK_STOPPED状态的进程继续执行)

    3、调用自定义的信号捕获程序

         信号如果不需要立即处理,可以阻塞信号,被阻塞的信号称为挂起信号(pending signal)。进程执行某个信号的处理程序时会自动阻塞该信号,即进程在处理完成前不会再接收同一信号,不会被同一信号中断,但是如果接收了其他信号则可能中断当前处理程序。注意SIGKILL和SIGSTOP信号不能被显示的忽略,捕获或者阻塞,必须执行默认的操作,从而保护系统安全。

         内核收到一个信号时,判断目标进程不是被跟踪,没有阻塞该信号,没有忽略该信号则将该信号加入到挂起信号队列上,然后通知进程有新的挂起信号,如果此时目标进程是TASK_INTERRUPTIBLE状态,则会唤醒信号将其状态改成TASK_RUNNING,注意如果进程是因为系统调用进入到TASK_INTERRUPTIBLE状态,此时唤醒可能系统调用尚未完成需要重新执行系统调用。当进程从内核态切换到用户态时,内核会检查是否存在待处理的信号,如果存在则调用对应的处理程序,如果是用户自定义的捕获函数,则切换到用户态后开始执行捕获函数,信号处理完成从挂起队列中删除该信号,然后恢复正常程序执行。

       参考:Linux signal 那些事儿(4)信号的deliver顺序

                 Linux 的各种 signal

                 Linux Signal

     二、管道与FIFO文件

            管道是进程之间通信使用的一个单向数据流,部分Unix系统实现了双向数据流即全双工的管道。管道被看做是一个打开的文件,但是文件系统中没有对应的映像,执行pipe()系统调用创建一个新的管道时会返回两个文件描述符,其中一个用于往管道写入数据,一个往管道读取数据,进程只能使用其中的一个文件描述符,注意Linux中不需要关闭另一个没有使用的文件描述符。因为管道的读写基于文件描述符而文件描述符可以在进程创建时传递给子进程,所以管道可以被同一个祖先的多个进程同时使用,使用时需要借助文件加锁机制进行同步。因为管道在文件系统中没有对应的映像,所以一个与创建管道的进程没有任何关系的进程无法获取该管道的文件描述符,即无法打开已经存在的管道。

            管道在内核中是一个由16个管道缓冲区组成的环形缓冲区,每个管道缓冲区是一个单独的页框,写进程不断的往缓冲区写入数据,如果当前所在的管道缓冲区剩余空间不足则利用写入下一个管道缓冲区,已经写入待读出的数据称为管道大小;读进程不断的从缓冲区中读取数据,由内核记录下一个待读取的字节所在的管道缓冲区和页框中的偏移量。从管道读取数据时,如果管道的缓冲区为空或者没有包含所请求的字节数时可能阻塞读进程,也可以不阻塞,有多少字节数读取多少,依据创建管道时的配置选项而定,读取过程中如果缓冲区数据已空则会释放对应页框。往管道写入数据时,如果写入字节数小于管道缓冲区大小时需要原子的执行,且不能与其他进程对同一缓冲区的写入操作交叉执行;如果管道没有对应的读进程,写入管道时内核会给该进程发送SIGPIPE信号并终止写入;写入时若管道缓冲区未分配页框会从伙伴系统申请页框。

            Linux中执行shell命令时,可以通过"|" ,">" 或者 "<" 利用管道,如执行ls | more命令,就是shell进程创建了一个管道和分别执行ls和more命令的两个子进程,并将返回的两个文件描述符传给子进程,执行ls命令的子进程往管道写入数据,执行more命令的子进程从管道读取数据,如下图所示

          为了解决管道复用的缺陷,Unix系统引入了命名管道,又称FIFO文件。FIFO文件同样通过内核缓冲区在进程间传递数据,同一个进程对同一个FIFO文件只能读或者写,与管道的区别是,FIFO文件有对应的磁盘索引节点,可以在系统的文件目录树中查看,因此命名管道可以被任何进程访问。以数据库服务器上的服务进程和客户端进程通信为例,数据库启动时创建一个FIFO文件,由客户端负责将请求写入该FIFO文件;客户端启动时创建一个专属的FIFO文件,客户端发出请求时告诉服务端该文件的名称,服务端从自己的FIFO文件读取请求后将处理结果写入到客户端对应的FIFO文件中,客户端读取自己的FIFO文件内容并把结果展示在控制台上,示例中服务端和客户端的FIFO文件都是可读可写的。

         参考:  《深入理解Linux内核》(第三版)

    三、System V IPC

        IPC(Inter-Process Communication)是指多个进程之间相互通信,交换信息的方法,System V是Unix操作系统最早的商业发行版,由AT&T(American Telephone & Telegraph)开发。System V IPC是指Linux引入自System V的进程通信机制,一共有三种:(1)信号量,用来管理对共享资源的访问;(2)共享内存,用来高效地实现进程间的数据共享;(3)消息队列,用来实现进程间数据的传递。这三种统称IPC资源,每个IPC资源都是请求时动态创建的,都是永驻内存,除非被进程显示释放,都是可以被任一进程使用。每个IPC资源都使用一个32位的IPC关键字和32位的IPC标识符,前者类似文件系统中的路径名,由程序自由定制,后者类似打开文件的文件描述符,由内核统一分配,在系统内部是唯一的,当多个进程使用同一个IPC资源通信时需要该资源的IPC标识符。

         创建新的IPC资源时需要指定IPC关键字,如果没有与之关联的IPC资源,则创建一个新的IPC资源;如果已经存在,则判断当前进程是否具有访问权限,是否超过资源使用限制等,如果符合条件则返回该资源的IPC标识符。为了避免两个不同的IPC资源使用相同的IPC关键字,创建时可以指定IPC关键字为IPC_PRIVATE,由内核负责生成一个唯一的关键字。

        创建新的IPC资源时最后一个参数可以包括三个标志,PC_CREAT说明如果IPC资源不存在则必须创建它,IPC_EXCL说明如果资源已经存在且设置了PC_CREAT标志则创建失败,IPC_NOWAIT说明访问IPC资源时进程从不阻塞。

         IPC信号量支持一个信号量或者多个信号量组成的信号量集,使用单个信号量时同内核信号量一样,信号量大于0表示受保护的资源可用,等于0表示不可用;使用信号量集的时候,如果信号量集中每个信号量对应的资源对当前进程都可用时才能访问这些资源,如果只是部分资源可用,则进程必须释放已占用资源,对应信号量加1。 IPC信号量还提供了一种安全时效机制,如果进程成功获取信号量对应的资源后因为异常终止而没有释放对应的资源,IPC信号量能够恢复信号量,避免其他进程访问相同资源时因为信号量没有恢复导致无限阻塞。IPC信号量通过三个链表实现,每个进程维护一个undo_list链表,记录了该进程执行可取消操作的所有信号量,每个信号量维护一个undo链表和一个进程挂起队列,前者记录了对该信号量执行的所有可取消操作,后者记录了所有请求当前信号量而被挂起的进程,按照FIFO原则处理。当进程退出时,内核遍历undo_list链表找到执行了可取消操作的信号量,然后遍历信号量的undo链表,逐一回退该进程执行的可取消操作。

        使用IPC消息时,进程产生的每条IPC消息都会发送到一个消息队列中,消息由固定大小的首部和可变长度的正文组成的,通过一个整数值来表示消息类型,允许进程有选择的从消息队列中获取消息,当消息队列满或者达到了消息队列最大字节数则阻塞发送消息的进程,被阻塞的进程以链表形式保存。只要进程从IPC消息队列读出一条消息,内核就把这个消息删除,即每条消息只能有一个接受进程,如果队列为空或者指定的消息类型不存在时则会阻塞获取消息的进程,被阻塞的进程同样以链表形式保存。

        最有用的IPC机制是共享内存,允许多个进程通过把公共数据结构放入共享内存区来访问。访问共享内存区时,进程首先需要在自己的地址空间内划分一个线性区,但是此时页表并没有修改,即没有完成线性区到共享内存区的页框的映射。当进程开始访问共享内存区的某个数据时,会触发缺页异常,由缺页异常程序负责修改页表,完成共享内存区页框到进程线性区的映射。共享内存因为是进程直接读写内存,避免了数据在用户空间和内核空间之间的多次拷贝,所以效率是最高的。

           参考: 【Linux】Linux的信号量集

                          linux System V IPC总结

                        linux基础编程:进程通信之System V IPC:消息队列,信号量,共享内存

    四、POSIX消息队列

        POSIX消息队列是POSIX标准在2001年定义的一种IPC机制,与System V中的消息队列相比有如下差异:

    • 更简单的基于文件的应用接口,Linux通过mqueue的特殊文件系统来实现消息队列,队列名跟文件名类似,必须以"/"开头,每个消息队列在文件系统内都有一个对应的索引节点,返回的队列描述符实际是一个文件描述符
    • 完全支持消息优先级,消息在队列中是按照优先级倒序排列的(即0表示优先级最低)。当一条消息被添加到队列中时,它会被放置在队列中具有相同优先级的所有消息之后。如果一个应用程序无需使用消息优先级,那么只需要将msg_prio指定为0即可。
    • 完全支持消息到达的异步通知,当新消息到达且当前队列为空时会通知之前注册过表示接受通知的进程。在任何一个时刻都只有一个进程能够向一个特定的消息队列注册接收通知。如果一个消息队列上已经存在注册进程了,那么后续在该队列上的注册请求将会失败。可以给进程发送信号或者另起一个线程调用通知函数完成通知。当通知完成时,注册即被撤销,进程需要继续接受通知则必须重新注册。
    • 用于阻塞发送与接收操作的超时机制,可以指定阻塞的最长时间,超时自动返回

        参考:Linux进程间通信之POSIX消息队列

    五、Socket套接字

          套接字(Socket)是由Berkeley在BSD系统中引入的一种基于网络连接的IPC,是对网络接口(硬件)和网络协议(软件)的抽象。它既解决了无名管道只能在相关进程间单向通信的问题,又解决了网络上不同主机之间无法通信的问题。

      套接字有三个属性:域(domain)、类型(type)和协议(protocol),对应于不同的域,套接字还有一个地址(address)来作为它的名字。域(domain)指定了套接字通信所用到的协议族,最常用的域是AF_INET,代表网络套接字,底层协议是IP协议。对于网络套接字,由于服务器端有可能会提供多种服务,客户端需要使用IP端口号来指定特定的服务。AF_UNIX代表本地套接字,使用Unix/Linux文件系统实现。

      IP协议提供了两种通信手段:流(streams)和数据报(datagrams),对应的套接字类型(type)分别为流式套接字和数据报套接字。流式套接字(SOCK_STREAM)用于提供面向连接、可靠的数据传输服务。该服务保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字使用TCP协议。数据报套接字(SOCK_DGRAM)提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议。

      参考:Linux编程---套接字

                   linux OSI七层模型、TCP-IP协议栈及每层结构大揭秘

                   TCP/IP协议集详解

          

    展开全文
  • Linux进程通信基本概念 从原理上来看,进程通信的关键技术就是在进程间建立某种共享区,利用进程都可以访问共享区的特点来建立一些通信通道。如下图所示: 其实,以前设计程序时使用的全局变量,就是一种可以在...
  • Linux进程通信之消息队列

    千次阅读 2020-11-08 14:17:30
    Linux进程通信之消息队列 消息队列主要由下面四个函数组成   1、msgget   2、msgctl   3、msgsnd   3、msgrcv 1、msgget 得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符 函数原型 #...
  • Linux进程通信_概述

    2014-08-30 17:11:42
    Linux进程通信_概述:进程之间通信是嵌入式Linux应用开发中很重要的高级议题。一、出现在我们脑海的第一反应可能是:什么是进程?二、进而而来的第二个问题可能是:进程为什么要通信,或者说进程通信的目的是什么?1...
  • Linux进程通信 FIFO

    千次阅读 2014-02-13 18:55:21
    Linux进程通信 FIFO FIFO也称为有名管道,它是一种文件类型,在文件系统中可以看到。程序中可以查看文件stat结构中st_mode成员的值来判断文件是否是FIFO文件。创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件...
  • linux进程通信———有名管道FIFO

    千次阅读 2018-04-01 15:42:34
    linux进程通信———有名管道FIFO 引言:无名管道的一个重大限制是它没有名字,通信范围限定在具有血缘关系的进程间。有名管道以FIFO文件形式存在于文件系统中。这样即使与FIFO创建进程不存在血缘关系的进程,只要...
  • Linux进程通信编程

    2018-05-03 20:14:51
    Linux系统编程—-进程通信:共享内存 Linux系统编程—-进程通信:信号中断处理 Linux系统编程—-进程通信:管道(pipe) Linux系统编程—-进程通信:消息队列 Linux系统编程—-进程同步与互斥:信号量 ...
  • 前言:Linux进程通信的方式什么是进程通信?进程通信是指进程之间交换信息 进程通信方式共有6种: 管道(pipe),包括流管道(s_pipe)和有名管道(named pipe) 信号(signal) 消息队列 共享内存 信号量 套接字...
  • 实现linux进程通信的方式有6种: --内存共享 --信号(Singal) --管道(Pipe) --消息队列(Message) --信号量(Semaphore) --socket 消息队列通信 请关注:h
  • 欢迎转载请注明出处:海漩涡http://blog.csdn.net/tanhuifang520linux 进程通信之共享内存机制C++代码实例一、使用说明只需包含share_memory.h使用类通过名称和内存大小参数与其他进程共同使用一段内存二、代码实例1...
  • Linux进程通信学习笔记

    千次阅读 2015-12-09 23:07:46
    1.为什么需要进程通信 1)数据传输 一个进程需要把它的数据发送给另一个进程。 2)资源共享 多个进程之间共享同样的资源。 3)通知事件 一个进程向另外一个进程发送消息,通知它发生了某事件。 4)进程控制 ...
  • linux进程通信———Posix消息队列简介及基础库函数 引言:消息队列可认为是一个消息链表,有足够写权限的线程可向队列中放置消息,有足够读权限的线程可从队列中取走消息。本篇笔记将简要介绍Posix消息队列特性、...
  • Linux进程通信之信号

    2015-06-17 12:11:17
    Linux应用程序学习之进程通信之信号  前面我们学习了Linux进程间管道的通信方式,主要是把管道看做文件来进行相应操作,这一章就来了解一些进程间的信号通信方式。 在Linux系统中,信号是一种最为古老的进程间通信...
  • 信号灯通信,和一般意义的通信不大一样,通信一般是用来收发数据,而信号灯却是用来控制多进程访问共享资源的,利用这一功能,信号量也就...) 形参key、semflg不再赘述,和我的另一篇文章《linux进程通信之消息...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,012
精华内容 9,204
关键字:

linux进程通信

linux 订阅