1、数据传输
2、资源共享
3、通知事件
4、进程控制
Linux进程间通信(IPC)由几步发展而来:UNIX进程间通信、基于System V进程间通信、POSIX进程间通信
可移植操作系统接口,最初是为了提高UNIX环境下应用程序的可移植性,然而POSIX并不局限于LINUX,其他许多操作系统,例如DEC OpenVMS和Windows都支持POSIX标准
也被称为AT&T System V,是Unix操作系统众多版本中的一支
管道(pipe)和有名管道(FIFO)
信号(signal)
消息队列
共享内存
信号量
套接字
特点:单向、先进先出,一个进程的输出与一个进程的输入连接在一起,一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)在管道头部读出数据
无名管道、有名管道:前者用于父进程和子进程间的通信,后者可以用于同一系统中任意两个进程间的通信
创建:int pipe(int fledis[2]);
当一个管道建立时,它会创建两个文件描述符:filedis[0]用于读管道。filedis[1]用于写管道
关闭:使用close
#include
#include
#include
#include
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
else
printf("pipe create success\n");
close(pipe_fd[0]);
close(pipe_fd[1]);
}
运行结果
[gyy@localhost process_com]$ gcc pipe_creat.c -o pipe_creat
[gyy@localhost process_com]$ ./pipe_creat
pipe create success
读写:管道用于不同进程间通信,通常会先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道(必须在调用fork之前调用pipe(),否则子进程将不会继承文件描述符)
#include
#include
#include
#include
#include
#include
int main()
{
int pipe_fd[2];
pid_t pid;
char buf_r[100];
char *p_wbuf;
int r_num;
memset(buf_r,0,sizeof(buf_r));
//创建管道
if(pipe(pipe_fd)<0)
{
printf("pipe creat error!\n");
return -1;
}
//创建子进程
if((pid=fork())==0)
{
printf("\n");
close(pipe_fd[1]);
sleep(2);
if((r_num=read(pipe_fd[0],buf_r,100))>0)
{
printf("%d numbers read from the pipe is: %s\n",r_num,buf_r);
}
close(pipe_fd[0]);
exit(0);
}
else if(pid>0)
{
close(pipe_fd[0]);
if(write(pipe_fd[1],"Hello",5)!=-1)
printf("parent write1 hello!\n");
if(write(pipe_fd[1],"Pipe",5)!=-1)
printf("parent write2 Pipe!\n");
close(pipe_fd[1]);
sleep(3);
waitpid(pid,NULL,0);
exit(0);
}
}
运行结果:首先创建管道,然后创建子进程,子进程会继承父进程的管道,子进程首先关闭写等待2秒,然后读出管道中的信息并打印,父进程关闭读然后依次写入“Hello”“Pipe”,然后睡眠3秒
[gyy@localhost process_com]$ gcc pipe_rw.c -o pipe_rw
[gyy@localhost process_com]$ ./pipe_rw
parent write1 hello!
parent write2 Pipe!
10 numbers read from the pipe is: HelloPipe
不相关的进程也能交换数据,实质上就是文件
创建:int mkfifo(const char *pathname,mode_t mode)
pathname:FIFO文件名
mode:属性,见文件操作
当打开FIFO时,非阻塞标志(O_NONBLOCK )将对以后产生如下影响
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞
2、使用O_NONBLOCK:访问要求无法满足是不阻塞,立刻出错返回,errno是ENXIO
fifo_write.c
#include
#include
#include
#include
#include
#include
#include
#define FIFO_SERVER "/tmp/myfifo"
main(int argc,char **argv)
{
int fd;
char w_buf[100];
int nwrite;
//打开FIFO
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
if(argc==1)
{
printf("Please send something\n");
exit(-1);
}
if(fd==-1)
printf("OPEN ERROR\n");
strcpy(w_buf,argv[1]);
printf("%s\n",w_buf);
if((nwrite=write(fd,w_buf,100))==-1)
{
if(errno==EAGAIN)
printf("The FIFO has not been read yet.Please try later\n");
}
else
printf("write %s to the FIFO\n",w_buf);
}
fifo_read.c
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO "/tmp/myfifo"
main(int argc,char ** argv)
{
char buf_r[100];
int fd;
int nread;
//创建FIFO
if(mkfifo(FIFO,O_CREAT|O_EXCL)<0&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
printf("Preparing for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
//打开FIFO
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
if(fd==-1)
{
perror("open");
}
while(1)
{
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==-1)
{
if(errno=read(fd,buf_r,100)==-1)
{
if(errno==EAGAIN)
printf("no date yet\n");
}
}
printf("read %s from FIFO\n",buf_r);
sleep(1);
}
//暂停等待信号
pause();
//删除文件
unlink(FIFO);
}
运行结果
[root@localhost process_com]# gcc fifo_write.c -o fifo_write
[root@localhost process_com]# gcc fifo_read.c -o fifo_read
fifo_read.c
[root@localhost process_com]# ./fifo_read
Preparing for reading bytes...
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read FIFO_TEST from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
^C
fifo_write.c
[root@localhost process_com]# ./fifo_write FIFO_TEST
FIFO_TEST
write FIFO_TEST to the FIFO
注意要两个进程同时运行
信号(signal)机制是UNIX系统中最为古老的进程间通信机制
硬件软件都能产生信号
进程用kill函数将信号发送给另一个进程,用户可用kill命令将信号发送给其他进程
几种常见的信号
1、忽略信号
大多数信号都按照这种方式处理,但是SIGKILL、SIGSTOP不能忽略
2、执行用户希望的动作
通知内核在某种信号发生时调用一个用户函数
3、执行系统默认动作
大多数信号默认动作是结束进程
kill和raise
kill既可以向自身发送信号,也可以向其他进程发送信号,raise函数是向进程自身发送信号
int kill(pid_t pid,int signo)
int raise(int signo)
alarm函数用于指定一段时间后产生一个SIGALRM信号,如果不捕捉此信号,默认动作是终止该进程
unsigned int alarm(usigned int seconds)
每个进程只能有一个闹钟时间,如果在调用alarm时已设置过alarm,则时间被替代
设置闹钟时间为0则取消以前的闹钟
pause函数使进程挂起直至捕获到一个信号
int pause(void)
方法:使用简单的signal函数,使用信号集函数组
void (*signal (int signo,viod (*func)(int)))(int)
#include
#include
#include
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);
}
运行结果
[root@localhost process_com]# gcc mysignal.c -o mysignal
[root@localhost process_com]# ./mysignal
Waiting for signal SIGINT or SIGQUIT
I have get SIGQUIT
[root@localhost process_com]# ps au | grep ./mysignal
root 7563 0.0 0.0 4212 356 pts/1 S+ 17:08 0:00 ./mysignal
[root@localhost process_com]# kill -s SIGQUIT 7563
[root@localhost process_com]# ps au | grep ./mysignal
root 7566 0.0 0.0 4212 356 pts/1 S+ 17:09 0:00 ./mysignal
[root@localhost process_com]# kill -s SIGINT 7566
[root@localhost process_com]# ./mysignal
Waiting for signal SIGINT or SIGQUIT
I have get SIGINT
代码分析:my_func为信号的处理函数,参数为某一个信号,可以根据信号的不同有不同的打印值,主程序为SIGINT和SIGQUIT注册,然后pause等待信号。这是打开另外一个中断,先查阅进程PID,然后用kill指令发送信号,然后程序进程就会捕获信号。
被多个进程共享的一部分物理内存,共享内存是进程间共享数据的一种最快的方式,一个进程向共享内存区域写了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容
1、创建共享内存,使用shmget函数
2、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数
int shmget(key_t key,int size,int shmflg)
key标识共享内存的键值:O/IPC_PRIVATEO当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中又设置 IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。
返回值:如果成功,返回共享内存标识符;如果失败,返回-1。
key标识共享内存的键值:O/IPC_PRIVATEO当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中又设置 IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。返回值:如果成功,返回共享内存标识符;如果失败,返回-1。
int shmat(int shmid,char *shmaddr,int flag)
shmid:shmget函数返回的共享存储标识符
flag:决定以什么方式来确定映射的地址(通常为0)
shmaddr写零系统自动指定
返回值:如果成功,则返回共享内存映射到进程中的地址,失败返回-1
int shmdt(char * shmaddr)
不在需要共享内存使需要把它从进程地址空间中脱离
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PERM S_IRUSR|S_IWUSR
int main(int argc,char **argv)
{
int shmid;
char *p_addr,*c_addr;
if(argc!=2)
{
fprintf(stderr,"Usage:%s\n\a",argv[0]);
exit(1);
}
if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)
{
fprintf(stderr,"Create Share Memory Error: %s\n\a",strerror(errno));
exit(1);
}
//创建子进程
if(fork())//父进程写
{
p_addr=shmat(shmid,0,0);
memset(p_addr,'\0',1024);
strncpy(p_addr,argv[1],1024);
wait(NULL);//释放资源,不关心终止情况
exit(0);
}
else //子进程读
{
sleep(1);
c_addr=shmat(shmid,0,0);
printf("Client get: %s\n",c_addr);
exit(0);
}
}
运行结果
[root@localhost process_com]# gcc shmem.c -o shmem
[root@localhost process_com]# ./shmem SHMEM_Test
Client get: SHMEM_Test
代码分析:创建共享内存,创建子进程后父进程在映射后给共享内存区域写‘\0’(结束符),然后把参数写入共享内存区域,子进程先休眠1秒,等待父进程写完毕,然后映射共享内存,读出其中的数据并打印。