进程间通讯(IPC)(有信号捕捉函数)
1、IPC(Internal Processes Communication)
进程间通讯--->实际上数据的交换。
通过全局变量的方式实现不了进程间通讯
因为进程内存空间,都是相互独立的,不能通过这种方式去互相访问。
进程间通讯的方式:
1、内存映射,内存共享。
2、通过读写文件的方式。这种效率比较低。
3、信号。
4、消息队列。
5、信号量。
6、管道。无名管道(这个管道没有节点,不存在与文件系统里面,它存在于内核内,只限与有亲缘关系的进程间实现通讯)
有名管道(有一个节点存在于文件系统当中,通过这个节点作为一个桥梁,实现通讯)
无名管道
无名管道的建立:
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
返回值:成功返回0,失败返回-1,并且可以通过perror把错误码打印出来。
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
1) 创建管道 pipe。
2)关闭无关的端口。(本身是全双工的,但是我们两个进程用一个管道通讯,就要按某种同步方式去读写数据,否则造成数据的错误,所以我们人为把其中一端关闭 close(fd))
3) int pipefd[2]这里代表的是两个文件描述符,pipefd[0]--->读 read(pipefd[0], buf, len );
pipefd[1]--->写 write(pipefd[1], buf, len);
4) 使用结束后关闭对应的端口。
无名管道间的双向通信
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int pipefd[2];
int pipefd2[2];
pid_t pid;
int r;
char buf_w[128],buf_r[128];
memset(buf_w,0,128);
memset(buf_r,0,128);
if(pipe(pipefd)<0)
{
perror("creat pipe faile");
exit(-1);
}
if(pipe(pipefd2)<0)
{
perror("creat pipe faile");
exit(-1);
}
pid=fork();
if(pid<0)
{
perror("fork error");
exit(-1);
}
if(pid==0)
{
printf("i am chile process\n");
printf("please input:\n");
close(pipefd[0]);
scanf("%s",buf_w);
write(pipefd[1],buf_w,strlen(buf_w));
memset(buf_w,0,128);
close(pipefd[1]);
close(pipefd2[1]);
read(pipefd2[0],buf_r,128);
printf("read from parent %s\n",buf_r);
memset(buf_r,0,128);
close(pipefd2[0]);
exit(0);
}
else
{
close(pipefd[1]);
read(pipefd[0],buf_r,128);
printf("i am parent process\n");
printf("read from child %s\n",buf_r);
memset(buf_r,0,128);
close(pipefd[0]);
close(pipefd2[0]);
printf("plese input:\n");
scanf("%s",buf_w);
write(pipefd2[1],buf_w,strlen(buf_w));
memset(buf_w,0,128);
close(pipefd2[1]);
waitpid(pid,NULL,0);
exit(0);
}
return 0;
}
有名管道:
在文件系统种,有个管道的节点。然后两个不同进程,可以通过系统IO所提供的接口函数,去实现通讯。
open
read/write
close
NAME
mkfifo - make FIFOs (named pipes) 首先创建一个有名管道的节点。对于节点的操作就是系统IO的操作
mkfifo(char *name,mode_t mode); 第一个参数为创建节点的名字,第二个参数为创建这个节点的访问权限,通常为0644;//第一个参数必须是个文件名,不能是目录,创建后能在相应的目录里面找到这个节点。
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
int nfds, 这个是我们需要监听的fd最大值加1,
fd_set *readfds这个参数是监听可读fd
fd_set *writefds这个是监听可写的fd。
fd_set *exceptfds监听出错的fd。
struct timeval *timeout这个是超时的时间。
使用步骤:
先声明需要监听的集合,然后 先把这个监听的集合清空,FD_ZERO(fd_set *set);,然后把我们需要监听的fd添加到刚才清空的这个集合里面,FD_SET(int fd, fd_set *set);
返回值:
返回值小于0,说明有出错。
返回值等于0,说明超时。
如果是大于,说明有可读的fd,然后去判断是那个fd可读。FD_ISSET(int fd, fd_set *set);如果是这个fd可读,就返回真。接下来就去读该fd。
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#include <sys/select.h>
SYNOPSIS
mkfifo [OPTION]... NAME...
DESCRIPTION
Create named pipes (FIFOs) with the given NAMEs.
Mandatory arguments to long options are mandatory for short options
too.
-m, --mode=MODE
set file permission bits to MODE, not a=rw - umask
-Z, --context=CTX
set the SELinux security context of each NAME to CTX
--help display this help and exit
--version
output version information and exit
实现QQ间通信:
Caobo1.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int rfd,wfd,ret;
char str[32];
fd_set read_fd;
struct timeval time;
mkfifo("fifo1",0644);
mkfifo("fifo2",0644);
rfd=open("fifo1",O_RDONLY);
wfd=open("fifo2",O_WRONLY);
if(rfd<0||wfd<0)
{
perror("fail to open");
return -1;
}
while(1)
{
FD_ZERO(&read_fd);
FD_SET(rfd,&read_fd);
FD_SET(fileno(stdin),&read_fd);
time.tv_sec=5;
time.tv_usec=0;
memset(str,0,32);
ret=select(rfd+1,&read_fd,NULL,NULL,&time);
if(ret<=0)
continue;
if(FD_ISSET(rfd,&read_fd))//监听到rfd里面有东西的时候,把它从管道里面读出来
{
read(rfd,str,32);
printf("----------------\n");
printf("曹博:%s\n",str);
}
if(FD_ISSET(fileno(stdin),&read_fd))//监听到键盘有输入时,把它写到管道里面去
{
printf("please input:");
fgets(str,33,stdin);
write(wfd,str,32);
}
}
close(wfd);
close(rfd);
return 0;
}
Chensu.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int rfd,wfd,ret;
char str[32];
fd_set read_fd;
struct timeval time;
mkfifo("fifo1",0644);
mkfifo("fifo2",0644);
wfd=open("fifo1",O_WRONLY);
rfd=open("fifo2",O_RDONLY);//与上个程序变化的两点;
if(rfd<0||wfd<0)
{
perror("fail to open");
return -1;
}
while(1)
{
FD_ZERO(&read_fd);
FD_SET(rfd,&read_fd);
FD_SET(fileno(stdin),&read_fd);
time.tv_sec=5;
time.tv_usec=0;
memset(str,0,32);
ret=select(rfd+1,&read_fd,NULL,NULL,&time);
if(ret<=0)
continue;
if(FD_ISSET(rfd,&read_fd))//监听到rfd里面有东西的时候,把它从管道里面读出来
{
read(rfd,str,32);
printf("----------------\n");
printf("陈素:%s\n",str);
}
if(FD_ISSET(fileno(stdin),&read_fd))//监听到键盘有输入时,把它写到管道里面去
{
printf("plseae input:\n");
fgets(str,33,stdin);
write(wfd,str,32);
}
}
close(wfd);
close(rfd);
return 0;
}
2、信号
信号是Linux系统种用于进程间通讯或者操作的一种机制。这个信号可以在任何时候发送给一个进程,无需制定该进程的状态。如果说这个进程么有在执行的状态,内核可以先把这个信号保存下来。
当进程恢复运行再把这个信号发送给它。信号也是可以设置成阻塞的状态,这个信号的传递就会延迟,当阻塞的状态取消之后,才可以被进程接收到。
信号他是软件层面对中断机制的一种模拟,是一个异步通讯的过程。
信号的来源:
1)硬件来源,例如我们按下了ctrl+c,就产生了一个中断的信号。
2)软件的来源,使用系统调用,或者通过命令发送信号,常见的发送信号的系统函数:kill
如何处理我们收到的信号:
1)执行默认的操作,Linux系统对每一种信号都有默认的操作方式。
2)捕获这个信号,定义信号的处理函数,当接收到这个信号,我们怎么处理?你想怎么处理就怎么处理。
3)忽略这个信号,就是不对这个信号做处理。
有两个信号是应用程序无法捕获和忽略的信号,SIGKILL SEGSTOP.
系统到底有哪些信号呢? 定义在这个地方:/usr/include/i386-linux-gnu/bits/signum.h
/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). 控制中断挂起的时候 */
#define SIGINT 2 /* Interrupt (ANSI).从键盘上接受到的中断信号,ctrl+c */
#define SIGQUIT 3 /* Quit (POSIX). ctrl+d */
#define SIGILL 4 /* Illegal instruction (ANSI). 非法指令 */
#define SIGTRAP 5 /* Trace trap (POSIX). 调试程序,跟踪 */
#define SIGABRT 6 /* Abort (ANSI). 放弃 */
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
#define SIGBUS 7 /* BUS error (4.2 BSD). 总线错误 */
#define SIGFPE 8 /* Floating-point exception (ANSI). 浮点运算错误 */
#define SIGKILL 9 /* Kill, unblockable (POSIX). 杀死一个进程,该信号不能被阻塞、处理或者忽略 */
#define SIGUSR1 10 /* User-defined signal 1 (POSIX) . */
#define SIGSEGV 11 /* Segmentation violation (ANSI). */
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). 用户定义的信号 */
#define SIGPIPE 13 /* Broken pipe (POSIX). 管段出错 */
#define SIGALRM 14 /* Alarm clock (POSIX). 闹钟时间 */
#define SIGTERM 15 /* Termination (ANSI). 中止 */
#define SIGSTKFLT 16 /* Stack fault. 栈出错 */
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). 子进程状态发送改变 */
#define SIGCHLD 17 /* Child status has changed (POSIX). 子进程的状态发生改变 ,当子进程退出的时候,向父进程发送此信号,如果父进程正在运行wait函数,就会被唤醒,如果没有wait函数,父进程就不会捕获这个信号,此时子进程就会变成僵尸进程 */
#define SIGCONT 18 /* Continue (POSIX). 继续运行信号 */
#define SIGSTOP 19 /* Stop, unblockable (POSIX). 停止信号 该信号不能被阻塞、处理或者忽略*/
#define SIGTSTP 20 /* Keyboard stop (POSIX). 键盘停止 */
#define SIGTTIN 21 /* Background read from tty (POSIX). 后台读取tty */
#define SIGTTOU 22 /* Background write to tty (POSIX). 后台写tty */
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). 窗口大小发生改变 */
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
#define SIGIO 29 /* I/O now possible (4.2 BSD). */
#define SIGPWR 30 /* Power failure restart (System V). */
#define SIGSYS 31 /* Bad system call. */
#define SIGUNUSED 31
信号的处理函数
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void fun_ctrl_c(int sig)
{
if(sig==SIGINT)//中断信号
{
printf("NOW YOU PRESS CTRL+C\n");
}
if(sig==SIGQUIT)//ctrl+d
{
printf("NOW YOU PRESS CTRL+D\n");
}
signal(SIGINT,SIG_DFL);//让信号恢复默认处理方式;
}
int main()
{
signal(SIGINT,fun_ctrl_c);
signal(SIGQUIT,fun_ctrl_c);
while(1)
{
sleep(3);
}
return 0;
}
3、信号的发送
通过系统提供的一些函数,可以发送一些信号:
kill ,发送一个SIGKILL给进程。
raise 发送信号给进程或者给自己
alarm 定时器的事件到,向进程发送一个SIGALRM
pause 没有捕获到信号之前一直是挂起
signal() 当捕获到某一种信号之后,绑定对应的处理函数,然后自定义去处理这个信号。
#include <signal.h>
sigset_t *set先定义信号的集合,
int sigemptyset(sigset_t *set); 清空该集合
int sigfillset(sigset_t *set); 初始化信号集合为所有信号
int sigaddset(sigset_t *set, int signum); 将指定的信号,加到对应的信号集合里面
int sigdelset(sigset_t *set, int signum); 把指定的信号从这个集合里面去掉
int sigismember(const sigset_t *set, int signum); 查询指定的信号,在不在这个集合里面。
以上函数,成功就返回0,失败返回-1,并且对应的错误码会被设置。
include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int ret;
pid=fork();
if(pid<0)
{
perror("fork fail");
exit(-1);
}
else if(pid==0)
{
printf("this is child process before raise\n");
// raise(SIGSTOP);//发送信号给自己;
printf("this is child process after raise\n");
sleep(3);
exit(0);
}
else
{
printf("THE child pid:%d\n",pid);
ret=waitpid(pid,NULL,WNOHANG);//0: 表示阻塞等待进程退出,如果成功,直接返回pid;
//WNOHANG: 非阻塞,假如没有子进程退出,则立即返回,所以一般加循环控制,没有等到子进程退出时会返回0,等到了直接返回pid;//可以结合上个进程笔记一起理解;
if(ret==0)
{
ret=kill(pid,SIGKILL);
if(ret==0)
{
printf("kill the child process ret=%d\n",ret);
}
else
printf("kill error");
}
}
}
4、信号的处理
可以使用调用signal这个函数,将我们需要捕获的信号,和我们自定义的这个处理函数绑定起来,当捕获到绑定的信号的时候
处理函数就会被调用。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
5、信号的阻塞
当我们不想将收到的信号,立刻处理的时候,但是我们也不想把这个信号忽略掉,希望延时一段时间再去处理它,这个时候,就需要用到信号的阻塞来处理。
#include <signal.h>
sigset_t *set先定义信号的集合,
int sigemptyset(sigset_t *set); 清空该集合
int sigfillset(sigset_t *set); 初始化信号集合为所有信号
int sigaddset(sigset_t *set, int signum); 将指定的信号,加到对应的信号集合里面
int sigdelset(sigset_t *set, int signum); 把指定的信号从这个集合里面去掉
int sigismember(const sigset_t *set, int signum); 查询指定的信号,在不在这个集合里面。
以上函数,成功就返回0,失败返回-1,并且对应的错误码会被设置。
将信号变成阻塞型的信号:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
阻塞:SIG_BLOCK
不阻塞:SIG_UNBLOCK
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void fun_ctrl_c(int sig)
{
if(sig==SIGINT)
{
printf("NOW YOU PRESS CTRL+C\n");
}
if(sig==SIGQUIT)
{
printf("NOW YOU PRESS CTRL+D\n");
}
signal(SIGINT,SIG_DFL);
}
int main()
{
sigset_t set;
signal(SIGINT,fun_ctrl_c);
signal(SIGQUIT,fun_ctrl_c);
if(sigemptyset(&set)<0)//清空该集合
{
perror("sigempty error");
exit(-1);
}
if(sigaddset(&set,SIGINT)<0)//将指定的信号,加到对应的信号集合里面
{
perror("sigaddset fail");
exit(-1);
}
if(sigprocmask(SIG_BLOCK,&set,NULL)<0)//设置阻塞信号
{
perror("set block fail");
exit(-1);
}
int i;
for(i=0;i<3;i++)
{
printf("now is block the signal\n");
sleep(3);
}
if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)//将信号变成非阻塞的信号
{
perror("set unblock fail");
exit(-1);
}
while(1)
{
sleep(3);
}
return 0;
}
有阻塞信号函数现象,按ctrl c发送信号没反应
没阻塞信号函数时时的现象
6、作业 写一个程序,去控制一个播放器的播放,实现一些基本的功能
比如说:播放、暂停
快进、快退
音量增,音量减
下一曲、上一曲。。。。。。。。。。。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
int main()
{
pid_t pid;
int pipefd[2];
int wfd,rfd;
char buf[128];
mkfifo("fifo",0644);//创建有名管道
pid=fork();
if(pid<0)
{
perror("fork error");
exit(-1);
}
if(pipe(pipefd)<0)//创建无名管道
{
perror("pipe error");
exit(-1);
}
if(pid==0)
{
close(pipefd[0]);//关掉无名管道的读端
rfd=open("fifo",O_RDONLY);//以读的方式打开有名管道
if(rfd<0)
{
perror("child open fifo fail");
exit(-1);
}
execlp("mplayer","","-ac","mad","-slave"," -quiet","-input","file=fifo","video.avi",NULL);//注意-quiet前面有空格,这条命令启动mplayer,并且把fifo管道里面的命令读到mplayer里面并且运行,注意当是要求mplayer有返回的命名,这个函数会把返回结果直接写到标准输出上面去,其实可以不用无名管道去读,如果要用无名管道进行读写,那么要进行重定向,把输出到标准输出的定向到无名管道的写端,pipefd[1];
}
else if(pid>0)
{
wfd=open("fifo",O_WRONLY);
if(wfd<0)
{
perror("parent open fifo fail");
waitpid(pid,NULL,0);
exit(-1);
}
close(pipefd[1]);
while(1)
{
char ch;
ch=getchar();
if(ch=='q')
break;
switch(ch)
{
case 'p':
write(wfd,"pause\n",strlen("pause\n"));
break;
case 'k':
write(wfd,"seek 3\n",strlen("seek 3\n"));
break;
case 'g':
write(wfd,"get_time_pos\n",strlen("get_time_pos\n"));
memset(buf,0,128);
// read(pipefd[0],buf,128);
// printf("read return :%s\n",buf);
break;
}
}
kill(pid);
waitpid(pid,NULL,0);
close(pipefd[0]);
exit(0);
}
return 0;
}
dup2(pipefd[1],1); //重定向,1表示标准输出;
// sprintf(song,"%s%s","./song/",song_list[0]);//得到整个歌曲路径
//execlp("./mplayer","","-ac","mad","-slave","-quiet ","-input","file=fifo",song,NULL);}
通过程序控制mplayer要用有名管道传送命令,通过无名管道读取mplayer返回的信息,因为mplayer默认是把信息发送到标准输出上,所以要用dup2()中定向标准输出到无名管道的写端:dup2(pipedes[1],1);。
进程间通讯(IPC)(有信号捕捉函数)相关推荐
- QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开 本文地址:h ...
- Linux 进程间通讯方式 pipe()函数
Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...
- linux进程间通讯的几种方式的特点和优缺点,和适用场合。
http://blog.csdn.net/kakaka2011/article/details/6636661 1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有 ...
- linux open 头文件_linux下通过共享内存和mmap实现进程间通讯
前言 最近在学习GNU/Linux内核,看到mmap的时候书上说: mmap/munmap接口函数是用户最常用的两个系统调用接口,无论是在用户程序中分配内存.读写大文件.链接动态库文件,还是多进程间共 ...
- linux mmap实例_Linux下通过共享内存和mmap实现进程间通讯(含实例)
前言 最近在学习GNU/Linux内核,看到mmap的时候书上说: mmap/munmap接口函数是用户最常用的两个系统调用接口,无论是在用户程序中分配内存.读写大文件.链接动态库文件,还是多进程间共 ...
- Android(IPC)进程间通讯1:详解Binder由来?
完整原文:http://tryenough.com/android-... Android开发的进程间通讯,整个Android的应用都依赖于binder做底层通信机制.而Linux中提供的进程间通讯方 ...
- Linux 进程间通讯(IPC)方式 ------- 共享内存
Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...
- IPC 进程间通讯,排队输出--day33
""" IPC 进程间通讯 由于进程之间内存是相互独立的 所以需要对应积极而方案 能够使得进程之间可以相互传递数据 1.使用共享文件,多个进程同时读写同一个文件 IO速度 ...
- Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8425736.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
最新文章
- rust怎么放篝火_如何为你的露营活动搭建一个持久温暖的篝火
- Visual Studio原生开发的20条调试技巧(下)
- 登录样式:log4j 2,上下文,自动清除…全部不附加任何字符串!
- 单机最大负载_分布式高可靠之负载均衡,今天看了你肯定会
- Linux select 实现并发服务器和客户端
- python append函数_让你python代码更快的3个小技巧!速度提高了一倍还多
- 计算机主机爆炸,刚装的PC:开机使用突然爆炸!竟是电源惹的祸
- LCDM--商品潜在互补性发现模型
- java读取txt写入excel,java中对txt和excel的读取和写入
- 5月第3周安全回顾 思科路由器Rootkit现身 企业需漏洞管理
- 配置vue,vue脚手架的应用(老版本)
- 截止11月5日,30日内累计跌幅最大的200只股票
- 一个程序员未来5年的自我规划
- 第五周:Raptor:三色球问题
- 王家林Spark笔记
- 计算机专业英语第12章在线测试答案,计算机专业英语第章在线测试.doc
- win7虚拟机_win7系统如何安装virtual pc虚拟机 安装virtual pc虚拟机方法【介绍】
- ubuntu 16.04.7通过get-pip.py安装pip 20.3.4
- 无责任猜想#冰桶挑战#大佬们相爱相杀众生相
- 函数前后带两个下划线