作者: 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。
在运行读进程,打印出了success,说明在以阻塞模式打开的时候,只有读写进程都打开才会跳出阻塞。任何单一的打开都会阻塞。
最后输入看看,通讯完成:
然后就没得了,关闭写,读也就关闭了。
共享内存。详细的介绍可以查看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函数,因为毕竟....还是觉得新的总有它出现的意义。