Linux 进程间通讯

一、Linux 下进程间通讯方式

  1)管道(Pipe)及有名管道(named pipe):

  管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

  2)无名信号量semaphore)级有名信号量(named semaphore

  主要作为进程间以及同一进程不同线程之间的同步手段。

  3)信号(Signal

  信号是比较复杂的通信方式,用于通知接受进程有某种事件生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期 信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上, 该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,sigaction函数重新实现了signal函数);

  4)报文(Message)队列(消息队列)

  消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

 5)共享内存

  使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针其他通信机制运行效率较低设计的。往往与其它通信机制,如信号量结合使用, 来达到进程间的同步及互斥。

 6)套接字(Socket)

  更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字。

二、进程间通讯特点说明

1. socket
        1、使用socket通信的方式实现起来简单,可以使用因特网域和UNIX域来实现,使用因特网域可以实现不同主机之间的进出通信。
        2、该方式自身携带同步机制,不需要额外的方式来辅助实现同步。
        3、随进程持续。
2. 共享内存
        1、最快的一种通信方式,多个进程可同时访问同一片内存空间,相对其他方式来说具有更少的数据拷贝,效率较高。
        2、需要结合信号灯或其他方式来实现多个进程间同步,自身不具备同步机制。
        3、随内核持续,相比于随进程持续生命力更强。
3. 管道
        1、较早的一种通信方式,缺点明显:只能用于有亲缘关系进程之间的通信;只支持单向数据流,如果要双向通信需要多创建一个管道来实现。
        2、自身具备同步机制。
        3、随进程持续。
4. FIFO
        1、是有名管道,所以支持没有亲缘关系的进程通信。和共享内存类似,提供一个路径名字将各个无亲缘关系的进程关联起来。但是也需要创建两个描述符来实现双向通信。
        2、自身具备同步机制。
        3、随进程持续。
5. 信号
        1、这种通信可携带的信息极少。不适合需要经常携带数据的通信。
        2、不具备同步机制,类似于中断,什么时候产生信号,进程是不知道的。
6. 消息队列
        1、与共享内存和FIFO类似,使用一个路径名来实现各个无亲缘关系进程之间的通信。消息队列相比于其他方式有很多优点:它提供有格式的字节流,减少了开发人员的工作量;消息具有类型(system V)或优先级(posix)。其他方式都没有这些优点。
        1、具备同步机制。
        2、随内核持续。

三、进程间通讯实现方式

  1、有名管道(不相关的没有血缘关系的进程也可以相互通信)

1、管道创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);

filname是指文件名,而mode是指定文件的读写权限。mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议用mkfifo。

open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4
1、写入端代码
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h> 

int main()
{
    const char *fifo_name = "/tmp/my_fifo";
    int pipe_fd = -1;
    int data_fd = -1;
    int res = 0;
    const int open_mode = O_WRONLY;
    int bytes_sent = 0;
    char buffer[PIPE_BUF + 1];
    int bytes_read = 0;

    if(access(fifo_name, F_OK) == -1)
    {
        printf ("Create the fifo pipe.
");
        res = mkfifo(fifo_name, 0777);

        if(res != 0)
        {
            fprintf(stderr, "Could not create fifo %s
", fifo_name);
            exit(EXIT_FAILURE);
        }
    }

    printf("Process %d opening FIFO O_WRONLY
", getpid());
    pipe_fd = open(fifo_name, open_mode);
    printf("Process %d result %d
", getpid(), pipe_fd);

    if(pipe_fd != -1)
    {
        bytes_read = 0;
        data_fd = open("Data.txt", O_RDONLY);
        if (data_fd == -1)
        {
            close(pipe_fd);
            fprintf (stderr, "Open file[Data.txt] failed
");
            return -1;
        }

        bytes_read = read(data_fd, buffer, PIPE_BUF);
        buffer[bytes_read] = '';
        while(bytes_read > 0)
        {

            res = write(pipe_fd, buffer, bytes_read);
            if(res == -1)
            {
                fprintf(stderr, "Write error on pipe
");
                exit(EXIT_FAILURE);
            }

            bytes_sent += res;
            bytes_read = read(data_fd, buffer, PIPE_BUF);
            buffer[bytes_read] = '';
        }

        close(pipe_fd);
        close(data_fd);
    }
    else
        exit(EXIT_FAILURE);

    printf("Process %d finished
", getpid());
    exit(EXIT_SUCCESS);
}
View Code
2、 管道读取端
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>

int main()
{
    const char *fifo_name = "/tmp/my_fifo";
    int pipe_fd = -1;
    int data_fd = -1;
    int res = 0;
    int open_mode = O_RDONLY;
    char buffer[PIPE_BUF + 1];
    int bytes_read = 0;
    int bytes_write = 0;

    memset(buffer, '', sizeof(buffer));

    printf("Process %d opening FIFO O_RDONLY
", getpid());
    pipe_fd = open(fifo_name, open_mode);
    data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
    

    if (data_fd == -1)
    {
        fprintf(stderr, "Open file[DataFormFIFO.txt] failed
");
        close(pipe_fd);
        return -1;
    }

    printf("Process %d result %d
",getpid(), pipe_fd);
    if(pipe_fd != -1)
    {
        do
        {
            res = read(pipe_fd, buffer, PIPE_BUF);
            bytes_write = write(data_fd, buffer, res);
            bytes_read += res;
        }while(res > 0);

        close(pipe_fd);
        close(data_fd);
    }
    else
        exit(EXIT_FAILURE);

     printf("Process %d finished, %d bytes read
", getpid(), bytes_read);
     exit(EXIT_SUCCESS);
}
View Code

 2、无名管道(只能用于有血缘关系的进程之间,例如父子进程之间)

#include<unistd.h> 
int pipe(pipefd[2]);
pipefd[2]//用来返回文件描述符的数组 
pipefd[0]//为读打开,为读端
pipefd[1]//为写打开,为写端 
//通常调用pipe()之后会调用fork进行创建父子进程,进行通信,对于管道数据的流向,要么父进程(写,关闭读端),子进程(读,关闭写端),要么子进程(写,关闭读端),父进程(读,关闭写端),在此之间,双方可以调用read(对于读进程)和write(对于写进程)对未关闭的文件描述符进行读写操作 
//读写规则 1、读一个写端关闭的管道,在所有数据读完之后,read返回0,以指示文件到结尾处 2、如果写一个读端已关闭的管道,则产生SIGPIPE信号,捕捉信号write出错返回 3、互斥与原子性,在写的时候,读端不允许访问管道,并且已写尚未读取的字节数应该小于或等于PIPE_BUF(一般为4096字节)所规定的缓存大小 
//当所有的读端与写端全部关闭后,管道才能被销毁
样例:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    int pipefd[2];//创建之后返回两个文件描述符pipefd[0]--读端    pipefd[2]--写端
    pid_t cpid;
    char buf;
    if (pipe(pipefd) == -1)//创建管道
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    cpid = fork();//创建子进程
    if (cpid<0)
    {
        perror("fork cpid error");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0)//child
    {
        close(pipefd[0]);//关闭文件描述符0,读端 
        int i = 0;
        char* _mesg = NULL;
        while (i<20)
        {
            _mesg = "hello father !  i am  child.
 ";
            write(pipefd[1], _mesg, strlen(_mesg)+1);
            sleep(1);
            i++;
        }
    }
    else//father
    {
        close(pipefd[1]);//父进程将argv[1]写入管道,关闭读端 
        char _mesg_f[100];
        int j = 0;
        while (j < 20)
        {
            memset(_mesg_f, '', sizeof(_mesg_f));
            read(pipefd[0], _mesg_f, sizeof(_mesg_f));
            sleep(3);
            printf("%s
", _mesg_f);
            j++;
        }
     }
    return 0;
}
View Code

 3、有名信号量

#include <fcntl.h> 
#include <sys/stat.h> 
#include <semaphore.h> 
sem_t *sem_open(const char *name, int oflag); //打开一个有名信号量,此时有名信号量是已经存在了的。
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);//创建有名信号量
参数说明:
    name:信号量文件名。
    flags:sem_open() 函数的行为标志。
    mode:文件权限,可用八进制表示,如0777.
    value:信号量初始值。
    
sem_close函数
#include <semaphore.h>
int sem_close(sem_t *sem);//关闭有名信号量
返回值:若成功,返回0;若出错,返回-1
参数:
    sem:指向信号量的指针。
    
sem_unlink函数
#include <semaphore.h>
int sem_unlink(const char *name);//删除有名信号量文件
返回值:若成功,返回0;若出错,返回-1
参数:

    name:有名信号量文件名

sem_open    用于创建一个信号量,并能初始化它的值。
sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。
sem_post   相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。
sem_getvalue 获取信号量的值。
sem_close   sem_unlink 删除信号量
#include <unistd.h>  
#include <stdio.h>  
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "name_sem_file"
int main ()   
{   
    sem_t *sem = NULL; 
    //有名信号量在fork子程序中继承下来
    //跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量  
    sem = sem_open("name_sem", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1 
    if(sem == SEM_FAILED)
     {
        perror("sem_open");  
        exit(-1);  
     }  
     pid_t pid; //pid表示fork函数返回的值  
    int count=0;  
    int fd = open(FILENAME,O_RDWR | O_CREAT,0777);
    if(fd < 0)
    {
        perror("open");
    }
    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0)
    {
        char write_buf[] = "1";
        int i = 0;
        printf("child: pid = %ld,ppid = %ld, fd = %d, count = %d
",(long)getpid(),(long)getppid(),fd,count); 
        for(i = 0;i < 2;i++)
        {
            /*信号量减一,P 操作*/
            sem_wait(sem);
            for(i = 0;i<5;i++)
            {
                if (write(fd,write_buf,sizeof(write_buf)))
                {
                    perror("write");
                }
                count++;
                }
            printf("in child....and count = %d
",count);
            /*信号量加一,V 操作*/
            sem_post(sem);
            sleep(2);
            }
        exit(0);
    }
    else
    {
        char write_buf[] = "2";
        int i = 0;
         printf("parent: pid = %ld,ppid = %ld, fd = %d, count = %d
",(long)getpid(),(long)getppid(),fd,count);
        for(i = 0; i<2; i++)
        {
            /*信号量减一,P 操作*/
            sem_wait(sem);
            for(i = 0;i<5;i++)
            {
                if (write(fd,write_buf,sizeof(write_buf)))
                {
                    perror("write");
                }       
                count++;
                }
            printf("in father.... count = %d
",count); 
            /*信号量加一,V 操作*/
            sem_post(sem);
            sleep(2);
            }  
            printf("Waiting for the child process to exit
");
            //等待子进程退出
            waitpid(pid,NULL,0);
            sem_del("name_sem"); //删除信号量文件 name_sem  
            exit(0);
            
    }
    //return 0;  
}
View Code
#include <unistd.h>  
#include <stdio.h>  
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "sync_name_sem_file"

int main ()   
{   
    sem_t *sem_1 = NULL; 
    sem_t *sem_2 = NULL; 
    //有名信号量在fork子程序中继承下来
    //跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量  
    sem_1 = sem_open("sync_name_sem_1", O_CREAT|O_RDWR, 0666, 0); //信号量值为 0
    sem_2 = sem_open("sync_name_sem_2", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1 
    if( (sem_1 == SEM_FAILED) | (sem_2 == SEM_FAILED))
     {
        perror("sem_open");  
        exit(-1);  
     }  
     pid_t pid; //pid表示fork函数返回的值  
    int count=0;  
    int fd = open(FILENAME,O_RDWR | O_CREAT,0777);
    if(fd < 0)
    {
        perror("open");
    }
    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0)
    {
        char write_buf[] = "1";
        int i = 0;
        printf("child: pid = %ld,ppid = %ld, fd = %d, count = %d
",(long)getpid(),(long)getppid(),fd,count); 
        for(i = 0;i<2;i++)
        {
            /*信号量减一,P 操作*/
            sem_wait(sem_1);
            for(i = 0;i<5;i++)
            {
                if (write(fd,write_buf,sizeof(write_buf)))
                {
                    perror("write");
                }
                count++;
            }
            printf("in child....and count = %d
",count);
            /*信号量加一,V 操作*/
            sem_post(sem_2);
            sleep(2)
        }
        exit(0);
    }
    else
    {
        char write_buf[] = "2";
        int i = 0;
        printf("parent: pid = %ld,ppid = %ld, fd = %d, count = %d
",(long)getpid(),(long)getppid(),fd,count);
        for(i = 0;i<2;i++)
        {
            /*信号量减一,P 操作*/
            sem_wait(sem_2);
            for(i = 0;i<5;i++)
            {
                if (write(fd,write_buf,sizeof(write_buf)))
                {
                    perror("write");
                }       
                count++;
            }
            printf("in father.... count = %d
",count); 
            /*信号量加一,V 操作*/
            sem_post(sem_1);
            sleep(2);
        }  
        printf("Waiting for the child process to exit
");
        //等待子进程退出
        waitpid(pid,NULL,0);
        sem_del("sync_name_sem_1"); //删除信号量文件 sync_name_sem_1 
        sem_del("sync_name_sem_2"); //删除信号量文件 sync_name_sem_2 
        exit(0);
    }
    //return 0;  
}
View Code

4、无名信号量 

1)初始化创建信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
    sem:信号量的地址。
    pshared:等于 0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
    value:信号量的初始值
2)信号量 P 操作(减 1int sem_wait(sem_t *sem);
参数:
    sem:信号量的地址。
int sem_trywait(sem_t *sem); //以非阻塞的方式来对信号量进行减 1 操作。若操作前,信号量的值等于 0,则对信号量的操作失败,函数立即返回。
3)信号量 V 操作(加 1int sem_post(sem_t *sem);
参数:
    sem:信号量的地址。
4)获取信号量的值
int sem_getvalue(sem_t *sem, int *sval);
参数:
    sem:信号量地址。
    sval:保存信号量值的地址。
5)销毁信号量
int sem_destroy(sem_t *sem);
参数:
    sem:信号量地址。
信号量用于互斥实例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
 
sem_t sem; //信号量
 
void printer(char *str)
{
    sem_wait(&sem);//减一
    while(*str)
    {
        putchar(*str);    
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("
");
    
    sem_post(&sem);//加一
}
 
void *thread_fun1(void *arg)
{
    char *str1 = "hello";
    printer(str1);
}
 
void *thread_fun2(void *arg)
{
    char *str2 = "world";
    printer(str2);
}
 
int main(void)
{
    pthread_t tid1, tid2;
    
    sem_init(&sem, 0, 1); //初始化信号量,初始值为 1
    
    //创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);
    
    //等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL); 
    
    sem_destroy(&sem); //销毁信号量
    
    return 0;
}
View Code
信号量用于同步实例:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
sem_t sem_g,sem_p;   //定义两个信号量
char ch = 'a';
 
void *pthread_g(void *arg)  //此线程改变字符ch的值
{
    while(1)
    {
        sem_wait(&sem_g);
        ch++;
        sleep(1);
        sem_post(&sem_p);
    }
}
 
void *pthread_p(void *arg)  //此线程打印ch的值
{
    while(1)
    {
        sem_wait(&sem_p);
        printf("%c",ch);
        fflush(stdout);
        sem_post(&sem_g);
    }
}
 
int main(int argc, char *argv[])
{
    pthread_t tid1,tid2;
    sem_init(&sem_g, 0, 0);   //初始化信号量
    sem_init(&sem_p, 0, 1);
    
    pthread_create(&tid1, NULL, pthread_g, NULL);
    pthread_create(&tid2, NULL, pthread_p, NULL);
    
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    return 0;
}
View Code

 5、信号

SIGHUP 1 A 终端挂起或者控制进程终止

SIGINT 2 A 键盘中断(如break键被按下)

SIGQUIT 3 C 键盘的退出键被按下

SIGILL 4 C 非法指令

SIGABRT 6 C 由abort(3)发出的退出指令

SIGFPE 8 C 浮点异常

SIGKILL 9 AEF Kill信号

SIGSEGV 11 C 无效的内存引用

SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道

SIGALRM 14 A 由alarm(2)发出的信号

SIGTERM 15 A 终止信号

SIGUSR1 30,10,16 A 用户自定义信号1

SIGUSR2 31,12,17 A 用户自定义信号2

SIGCHLD 20,17,18 B 子进程结束信号

SIGCONT 19,18,25 进程继续(曾被停止的进程)

SIGSTOP 17,19,23 DEF 终止进程

SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键

SIGTTIN 21,21,26 D 后台进程企图从控制终端读

SIGTTOU 22,22,27 D 后台进程企图从控制终端写

 
View Code
样例:
void sigroutine(int signo) {

        switch (signo) {

        case SIGALRM:

        printf("Catch a signal -- SIGALRM ");

        break;

        case SIGVTALRM:

        printf("Catch a signal -- SIGVTALRM ");

        break;

        }

        return;

}

 

int main()

{

        struct itimerval value,ovalue,value2;

        sec = 5;

 

        printf("process id is %d ",getpid());

        signal(SIGALRM, sigroutine);

        signal(SIGVTALRM, sigroutine);

 

        value.it_value.tv_sec = 1;

        value.it_value.tv_usec = 0;

        value.it_interval.tv_sec = 1;

        value.it_interval.tv_usec = 0;

        setitimer(ITIMER_REAL, &value, &ovalue);

 

        value2.it_value.tv_sec = 0;

        value2.it_value.tv_usec = 500000;

        value2.it_interval.tv_sec = 0;

        value2.it_interval.tv_usec = 500000;

        setitimer(ITIMER_VIRTUAL, &value2, &ovalue);

 

        for (;;) ;

}
View Code

 6、共享内存

1、shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数说明:得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符。
参数:
    key ftok函数返回的I P C键值
    size 大于0的整数,新建的共享内存大小,以字节为单位,获取已存在的共享内存块标识符时,该参数为0,
    shmflg IPC_CREAT||IPC_EXCL 执行成功,保证返回一个新的共享内存标识符,附加参数指定IPC对象存储权限,如|0666
返回值:成功返回共享内存的标识符,出错返回-1,并设置error错误位。

2、shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void shmaddr, int shmflg);

函数说明:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间
参数:
    shmid: 共享内存标识符
    shmaddr: 指定共享内存出现在进程内存地址的什么位置,通常指定为NULL,让内核自己选择一个合适的地址位置
    shmflg: SHM_RDONLY 为只读模式,其他参数为读写模式
返回值:成功返回附加好的共享内存地址,出错返回-1,并设置error错误位

3、shmdt
#include <sys/types.h>
#include <sys/shm.h>
void *shmdt(const void* shmaddr);

函数说明:与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,需要注意的是,该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程
参数:shmddr 连接共享内存的起始地址
返回值:成功返回0,出错返回-1,并设置error。

4、shmctl
#include <sys/types.h>
#Include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
函数说明:控制共享内存块
参数:
    shmid:共享内存标识符
    cmd:
        IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构赋值到buf所指向的buf中
        IPC_SET:改变共享内存的状态,把buf所指向的shmid_ds结构中的uid、gid、mode赋值到共享内存的shmid_ds结构内
        IPC_RMID:删除这块共享内存
    buf:共享内存管理结构体
返回值:成功返回0,出错返回-1,并设置error错误位。
View Code
comm.h

#ifndef _COMM_H_
#define _COMM_H_

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "." // ftok函数 生成key使用
#define PROJ_ID 66 // ftok 函数生成key使用
int create_shm( int size);//  分配指定大小的共享内存块
int destroy_shm( int shmid); // 释放指定id的共享内存块
int get_shmid(); // 获取已经存在的共享内存块

#endif /*_COMM_H_*/
View Code
#include "comm.h"

// 
static int comm_shm(int size, int shmflag)
{
    key_t key = ftok(PATHNAME, PROJ_ID); // 获取key 
    if(key < 0){
        perror("ftok");
        return -1;
    }
    int shmid = shmget(key,  size, shmflag);
    if(shmid < 0){
        perror("shmget");
        return -2;
    }
    return shmid;
}
int create_shm( int size)
{
    return comm_shm(size, IPC_CREAT|IPC_EXCL|0666);
}
    
int get_shmid()
{
    return comm_shm(0, IPC_CREAT);
}
int destroy_shm(int shmid)
{
    if( shmctl( shmid, IPC_RMID, NULL) < 0)
    {
        perror("shmctl");
        return -1;
    }
    return 0;
}
View Code
server.c

#include "comm.h"

int main()
{
    int shmid = create_shm(4096);// 创建共享内存块
    char *buf;
    int i = 0;
    buf = shmat(shmid,NULL, 0 );
    while( i < 4096)
    {
        buf[i] = 'a'+i ;
        i++;
        sleep(1); 
        if(i == 26)
            break; // 让程序结束,去释放该共享内存
    }
    destroy_shm(shmid);
    return 0;
}
View Code
clinet.c

#include "comm.h"

int main()
{
    int shmid = get_shmid(4096);
    char *buf;
    int index = 0;
    buf = shmat(shmid,NULL, 0 );
    while( index < 4096)
    {
        printf("%s
", buf);
        sleep(1);
        index++;
        if( index == 27)
            break; // 让程序结束
        }
    return 0;
}
View Code

7、消息队列

函数功能介绍
msgget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

功能:创建或取得一个消息队列对象
返回:消息队列对象的id 同一个key得到同一个对象
格式:msgget(key,flag|mode);
flag:可以是0或者IPC_CREAT(不存在就创建)
mode:同文件权限一样

msgsnd函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:将msgp消息写入标识为msgid的消息队列
msgp:
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的类型必须>0
char mtext[1]; /* message data */长度随意
};

msgsz:要发送的消息的大小 不包括消息的类型占用的4个字节
msgflg: 如果是0 当消息队列为满 msgsnd会阻塞
如果是IPC_NOWAIT 当消息队列为满时 不阻塞 立即返回
返回值:成功返回id 失败返回-1

msgrcv函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从标识符为msgid的消息队列里接收一个指定类型的消息 并 存储于msgp中 读取后 把消息从消息队列中删除
msgtyp:为 0 表示无论什么类型 都可以接收
msgp:存放消息的结构体
msgsz:要接收的消息的大小 不包含消息类型占用的4字节
msgflg:如果是0 标识如果没有指定类型的消息 就一直等待
如果是IPC_NOWAIT 则表示不等待

msgctl函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl(msgid,IPC_RMID,NULL);//删除消息队列对象
View Code
接收模块

/*============================================================================= 
#     FileName: msgreceive.c
#         Desc: receice message from message queue 
#       Author: Licaibiao 
#      Version:  
#   LastChange: 2017-01-20
#      History: 
=============================================================================*/
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
 
struct msg_st
{
    long int msg_type;
    char text[128];
};
 
int main()
{
    int running = 1;
    int msgid = -1;
    int len = 0;
    long int msgtype = 5; 
    struct msg_st data;
        
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(-1 == msgid )
    {
        fprintf(stderr, "msgget failed with error: %d
", errno);
        exit(EXIT_FAILURE);
    }
    
    while(running)
    {
        memset(&data.text, 0, 128);
        len = msgrcv(msgid, (void*)&data, 128, msgtype, 0);
        if(-1 == len)
        {
            fprintf(stderr, "msgrcv failed with errno: %d
", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote: %s
",data.text);
 
        if(0 == strncmp(data.text, "end", 3))
            running = 0;
    }
 
    //remove message queue
    if(-1 == msgctl(msgid, IPC_RMID, 0))
    {
        fprintf(stderr, "msgctl(IPC_RMID) failed
");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}
View Code
发送模块
/*=============================================================================
#     FileName: msgsend.c
#         Desc: send data to message queue
#       Author: Licaibiao
#      Version: 
#   LastChange: 2017-01-20 
#      History:
=============================================================================*/
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
 
#define MAX_TEXT 512
struct msg_st
{
    long int msg_type;
    char text[MAX_TEXT];
};
 
int main()
{
    int running = 1;
    struct msg_st data;
    char buffer[BUFSIZ];
    int msgid = -1;
    int len;
 
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
        fprintf(stderr, "msgget failed with error: %d
", errno);
        exit(EXIT_FAILURE);
    }
 
    while(running)
    {
        
        printf("Enter data : ");
        fgets(buffer, BUFSIZ, stdin);
        data.msg_type = 5;    
        strcpy(data.text, buffer);
        len = strlen(data.text);    
        if(msgsnd(msgid, (void*)&data, len-1, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed
");
            exit(EXIT_FAILURE);
        }
        
        if(strncmp(buffer, "end", 3) == 0)
            running = 0;
        usleep(100000);
    }
    exit(EXIT_SUCCESS);
}
View Code

 三、补充

  1、多进程与多线程区别

对比维度 多进程 多线程 总结
数据共享、同步 数据共享复杂,需要用IPC;数据是分开的,同步简单 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 各有优势
内存、CPU 占用内存多,切换复杂,CPU利用率低 占用内存少,切换简单,CPU利用率高 线程占优
创建销毁、切换 创建销毁、切换复杂,速度慢  创建销毁、切换简单,速度很快  线程占优
 编程、调试  编程简单,调试简单  编程复杂,调试复杂  进程占优
 可靠性  进程间不会互相影响  一个线程挂掉将导致整个进程挂掉  进程占优
 分布式  适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单

  

原文地址:https://www.cnblogs.com/jiejunwang/p/10058158.html