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)(有信号捕捉函数)相关推荐

  1. QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QSharedMemory共享内存实现进程间通讯(IPC)及禁止程序多开     本文地址:h ...

  2. Linux 进程间通讯方式 pipe()函数

    Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...

  3. linux进程间通讯的几种方式的特点和优缺点,和适用场合。

    http://blog.csdn.net/kakaka2011/article/details/6636661 1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有 ...

  4. linux open 头文件_linux下通过共享内存和mmap实现进程间通讯

    前言 最近在学习GNU/Linux内核,看到mmap的时候书上说: mmap/munmap接口函数是用户最常用的两个系统调用接口,无论是在用户程序中分配内存.读写大文件.链接动态库文件,还是多进程间共 ...

  5. linux mmap实例_Linux下通过共享内存和mmap实现进程间通讯(含实例)

    前言 最近在学习GNU/Linux内核,看到mmap的时候书上说: mmap/munmap接口函数是用户最常用的两个系统调用接口,无论是在用户程序中分配内存.读写大文件.链接动态库文件,还是多进程间共 ...

  6. Android(IPC)进程间通讯1:详解Binder由来?

    完整原文:http://tryenough.com/android-... Android开发的进程间通讯,整个Android的应用都依赖于binder做底层通信机制.而Linux中提供的进程间通讯方 ...

  7. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  8. IPC 进程间通讯,排队输出--day33

    """ IPC 进程间通讯 由于进程之间内存是相互独立的 所以需要对应积极而方案 能够使得进程之间可以相互传递数据 1.使用共享文件,多个进程同时读写同一个文件 IO速度 ...

  9. Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8425736.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...

最新文章

  1. rust怎么放篝火_如何为你的露营活动搭建一个持久温暖的篝火
  2. Visual Studio原生开发的20条调试技巧(下)
  3. 登录样式:log4j 2,上下文,自动清除…全部不附加任何字符串!
  4. 单机最大负载_分布式高可靠之负载均衡,今天看了你肯定会
  5. Linux select 实现并发服务器和客户端
  6. python append函数_让你python代码更快的3个小技巧!速度提高了一倍还多
  7. 计算机主机爆炸,刚装的PC:开机使用突然爆炸!竟是电源惹的祸
  8. LCDM--商品潜在互补性发现模型
  9. java读取txt写入excel,java中对txt和excel的读取和写入
  10. 5月第3周安全回顾 思科路由器Rootkit现身 企业需漏洞管理
  11. 配置vue,vue脚手架的应用(老版本)
  12. 截止11月5日,30日内累计跌幅最大的200只股票
  13. 一个程序员未来5年的自我规划
  14. 第五周:Raptor:三色球问题
  15. 王家林Spark笔记
  16. 计算机专业英语第12章在线测试答案,计算机专业英语第章在线测试.doc
  17. win7虚拟机_win7系统如何安装virtual pc虚拟机 安装virtual pc虚拟机方法【介绍】
  18. ubuntu 16.04.7通过get-pip.py安装pip 20.3.4
  19. 无责任猜想#冰桶挑战#大佬们相爱相杀众生相
  20. 函数前后带两个下划线

热门文章

  1. 使用PE镜像修改VMware中Windows镜像的方法
  2. Vue实现导入Excel功能
  3. 小红书接口加密参数X-sign
  4. 感恩2020,期待2021
  5. 熊绎:我看软件工程师的职业规划(转载)
  6. 【基于ECharts 数据可视化展示相关配置表全】
  7. MATLAB De_Pcode Mex 求助
  8. 关于组件之间使用provide和inject传值
  9. 工业物联网·能耗监控智慧空调接入华为云解决方案
  10. (DFS)深度优先搜索算法详解