博客文章

Linux进程间通信

作者: andy.      时间: 2016-08-20 10:23:22

可以先看一片文章:深刻理解Linux进程间通信(IPC)。文章介绍一下无名管道、有名管道、共享内存、信号的使用方法。

先看无名管道。无名管道的特点,简单来说,只能用于有关系进程间进程通讯,比如父子间。数据只能一个进程写,另外一个进程读。如果要相互通讯的话,要建立两个无名管道。man一下pipe:

PIPE(2)                        Linux Programmer's Manual                       PIPE(2)

NAME
       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);

下面也有示例代码,自己按照给的代码自己写一个:

int main(void) {
        int fd[2];//管道描述符
        char buf[1024];
        int length;

        pipe(fd);
        memset(buf, 0, 1024);
        int pid = fork();

        if( pid < 0 )
                exit(-1);

        if(pid == 0){
                close(fd[1]);//文件描述符要在父进程和子进程中都关闭才能真正的关闭。先将不用的文件描述符关闭,下同。
                while((length = read(fd[0], buf, 1024))){
                        write(STDOUT_FILENO, buf, length);
                        write(STDOUT_FILENO, "\n", 1);
                }
                close(fd[0]);
        }else{
                sleep(5);

                close(fd[0]);
                write(fd[1], "XOXO", 4);
                close(fd[1]);
                waitpid(pid, NULL, 0);
        }
}

看运行结果:

[[email protected] up]# gcc 1.c 
[[email protected] up]# ./a.out 
XOXO
[[email protected] up]#

有名管道。简单来说可以应用与两个无关的进程,遵守应该的先进先出。创建使用mkfifo,shell命令和C都是一样的。

看写进程的代码:

int main(){
        if(mkfifo("pipe", 0666) == -1){
                perror("");
        }
        int len = 0;
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        int fd = open("pipe", O_WRONLY);
        printf("open success\n");
        while(1){
                scanf("%s", buf);
                write(fd, buf, strlen(buf));
                memset(buf, 0, sizeof(buf));
        }
        close(fd);
}

读进程的代码:

int main(void){
        int len = 0;
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        int fd = open("pipe", O_RDONLY);
        printf("open success\n");
        while((len = read(fd, buffer, 1024)) > 0){
                printf("%s\n", buffer);
        }
        close(fd);
}

运行,先运行写进程,没有打印success。

blob.png

在运行读进程,打印出了success,说明在以阻塞模式打开的时候,只有读写进程都打开才会跳出阻塞。任何单一的打开都会阻塞。

blob.png

最后输入看看,通讯完成:

blob.png

然后就没得了,关闭写,读也就关闭了。


共享内存。详细的介绍可以查看IBM developerWorks的UNIX 共享内存应用中的问题及解决方法和百度百科的Linux进程通信共享内存


shmget函数用于创建(或者获取)一个由key键值指定的共享内存对象,返回该对象的系统标识符:shmid;
shmat函数用于建立调用进程与由标识符shmid指定的共享内存对象之间的连接;
shmdt函数用于断开调用进程与共享内存对象之间的连接;
shmctl函数用于对已创建的共享内存对象进行查询、设值、删除等操作;


这里给出两个示例代码,创建共享内存并写入:

int main(){
        char * shmbuf;
        int shmid = shmget(1234, 1024, IPC_CREAT);

        shmbuf = shmat(shmid, 0, 0);
        memcpy(shmbuf, "hello, world", strlen("hello, world") + 1);
        shmdt("shmbuf");
}

读取共享内存并删除的代码:

int main(){
        char * shmbuf;
        int shmid = shmget(1234, 1024, IPC_CREAT);

        shmbuf = shmat(shmid, 0, 0);
        printf("%s", shmbuf);
        shmdt("shmbuf");
        shmctl(shmid, IPC_RMID, NULL);
}

运行结果:

}
[[email protected] ipcs]# ./1
163841
[[email protected] ipcs]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x6c6c6536 0          root       600        4096       0                       
0x000004d2 163841     root       0          1024       0                       

[[email protected] ipcs]# ./2
hello, world[[email protected] ipcs]#

严格来说key是由ftok产生,访问共享内存的时候应该加锁,但是这里为了方便演示,使代码不至于太复杂,都没有使用。


信号,说到信号就有pause,windows中的暂停命令,但是Linux C中有这么一个函数,在信号来之前是暂停状态,当信号来了,就会继续向下运行。看代码吧:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
 
void Proccess_Signal(int sign){
        switch (sign) {
                case SIGHUP:
                        printf("It's SIGHUP\n");
                        break;
                case SIGINT:
                        printf("It's SIGINT\n");
                        break;
                case SIGQUIT:
                        printf("It's SIGQUIT\n");
                        exit(1);
        }
}
 
int main(){
        signal(SIGHUP, Proccess_Signal);
        signal(SIGINT, Proccess_Signal);
        signal(SIGQUIT, Proccess_Signal);
        while(1){
                sleep(1);
        }
}

用signal注册了一个信号,当该信号到来的时候,会执行Process_Signal这个回调函数。用kill 像进程发送信号。看man结果:

KILL(1)                              User Commands                             KILL(1)

NAME
       kill - terminate a process

SYNOPSIS
       kill [-signal|-s signal|-p] [-q value] [-a] [--] pid|name...
       kill -l [number] | -L

可选项。kill -s  信号 进程号。在C中还有raise函数和kill函数实现相同的功能,这儿就不做演示了。alarm函数就是时间到了给进程发一个SIGALRM信号。

每个进程能够决定响应SIGSTOP和SIGKILL之外的其他所有信号。但是一般建议用户使用SIGUSR1用户信号。

注册信号的执行函数的时候,建议使用sigaction函数,因为毕竟....还是觉得新的总有它出现的意义。