假如A进程和B进程想要通信,按照之间讲解的进程的原理是无法完成的,A和B进程的内存空间是独立的0-3G。如果想要实现通信就必须借助内核空间才能完成,因为内核空间是共用的。在linux系统中进程间通信的方式有7种。
(1)传统进程间通信(3种)
1.无名管道
2.有名管道
3.信号
(2)system V版本引入IPC进程间通信(3种)
1.消息队列
2.共享内存
3.信号灯集
(3)BSD(伯克利分校)版本中引入的socket通信
1.套接字通信(通过网络实现进程间通信)
(4)Android系统中
1.binder机制
无名管道只能由于亲缘关系的进程间通信,无名管道是在内核中实现的。如果A和B进程想要通过无名管道通信,A向管道的一端发送消息,B进程从管道的另外一端读取消息即可。无名管道的通信方式不支持使用lseek函数。无名管道的大小是64K。如果A一直往管道中写数据,B不读取。A写满的时候就会阻塞。如果A没有写,B取读取数据,B进程阻塞。无名管道是半双工的通信方式。
A和B的通信方式:
单工: A------------->B
半双工:在同一时刻内只能一边发一边收
A------>B A<-------B
全双工:在同一时刻两边都可以收发
A<-------->B
int pipe(int pipefd[2]);
功能:Pipe()创建一个管道,这是一个可以用于进程间通信的单向数据通道。数组pipefd用于返回引用管道两端的两个文件描述符。Pipefd [0]是指管道的读端。Pipefd[1]指管道的写端。写入到管道写端的数据由内核进行缓冲,直到从管道读端读取。
参数:@pipefd :返回管道两端的文件描述符pipefd[0]读端pipefd[1]写端
返回值:成功返回0,失败返回-1置位错误码
#include int main(int argc, const char* argv[])
{pid_t pid;int pipefd[2];char buf[128] = { 0 };// 1.创建无名管道if (pipe(pipefd))PRINT_ERR("pipe error");// 2.创建进程pid = fork();if (pid == -1) {PRINT_ERR("fork error");} else if (pid == 0) {close(pipefd[0]); // 关闭子进程的读端// 子进程向管道中写数据 pipefd[1]while (1) {fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';write(pipefd[1], buf, strlen(buf));if (strcmp(buf, "quit") == 0)break;}close(pipefd[1]);exit(0);} else {close(pipefd[1]); // 关闭父进程的写端// 从管道中读数据 pipefd[0]while (1) {memset(buf, 0, sizeof(buf));read(pipefd[0], buf, sizeof(buf));if (strcmp(buf, "quit") == 0)break;printf("buf = %s\n", buf);}close(pipefd[0]);wait(NULL);}return 0;
}
1.只能用于亲缘关系的进程的通信
2.无名管道是半双工的通信方式
3.不支持使用lseek函数
4.无名管道的大小是64K
#include int main(int argc, const char* argv[])
{pid_t pid;int pipefd[2];char buf[128] = { 0 };// 1.创建无名管道if (pipe(pipefd))PRINT_ERR("pipe error");for(int i=0;i<65537;i++){write(pipefd[1],"a",1); //写第65537个字符的时候,因为管道满了,写阻塞printf("i = %d\n",i);}printf("********************\n"); //这句话不打印return 0;
}
#include int main(int argc, const char* argv[])
{pid_t pid;int pipefd[2];char buf[128] = { 0 };// 1.创建无名管道if (pipe(pipefd))PRINT_ERR("pipe error");for(int i=0;i<65537;i++){write(pipefd[1],"a",1);printf("i = %d\n",i);}printf("********************\n");return 0;
}
#include int main(int argc, const char* argv[])
{pid_t pid;int pipefd[2];char buf[128] = { 0 };pid = fork();if (pid == -1) {PRINT_ERR("fork error");} else if (pid == 0) {if (pipe(pipefd))PRINT_ERR("pipe error");close(pipefd[0]); // 关闭读端write(pipefd[1], "a", 1); // 写管道while (1);}else{int wstatus;wait(&wstatus);if (WIFSIGNALED(wstatus)) { // 信号结束的子进程printf("signal = %d\n", WTERMSIG(wstatus)); //打印让子进程退出的信号号//子进程收到了管道破裂的信号,被杀死了。父进程收到了它的退出状态。// signal=13 ,13就是管道破裂的信号}}return 0;
}
#include int main(int argc, const char* argv[])
{int pipefd[2];char buf[128] = { 0 };if (pipe(pipefd))PRINT_ERR("pipe error");write(pipefd[1], "hello world", 11); // 写管道while (1){read(pipefd[0],buf,sizeof(buf));printf("buf = %s\n",buf);}return 0;
}
#include int main(int argc, const char* argv[])
{int pipefd[2];char buf[128] = { 0 };if (pipe(pipefd))PRINT_ERR("pipe error");write(pipefd[1], "hello world", 11); // 写管道close(pipefd[1]);while (1){memset(buf,0,sizeof(buf));read(pipefd[0],buf,sizeof(buf));printf("buf = %s\n",buf);sleep(1);}return 0;
}
有名管道可以在任意进程间进程通信,因为在创建有名管道的时候在用户空间会产生一个有名管道的文件,如果A和B进程想要通信,只需要让A和B进程打开这个文件即可得到文件描述符,此时两者就可以通信的。有名管道的文件在内存存储并没有在硬盘存储(掉电丢失)。有名管道的大小也是64K。有名管道也是不支持使用lseek。
int mkfifo(const char *pathname, mode_t mode);
功能:创建有名管道
参数:@pathname:管道文件的路径及名字@mode:管道文件的权限(mode & (~(umask)))
返回值:成功返回0,失败返回-1置位错误码
01mkfifo.c
#include
#define FIFO_NAME "./myfifo"int main(int argc,const char * argv[])
{if(mkfifo(FIFO_NAME,0666))PRINT_ERR("mkfifo error");//阻塞等待getchar();//删除管道文件char buf[128] = {0};snprintf(buf,sizeof(buf),"rm -rf %s",FIFO_NAME);system(buf); return 0;
}
02write.c
#include
#define FIFO_NAME "./myfifo"
int main(int argc, const char* argv[])
{int fd;char buf[128] = {0};if ((fd = open(FIFO_NAME, O_WRONLY)) == -1)PRINT_ERR("open error");while (1) {printf("input > ");fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';write(fd, buf, strlen(buf));if (strcmp(buf, "quit") == 0)break;}close(fd);return 0;
}
03read.c
#include
#define FIFO_NAME "./myfifo"
int main(int argc, const char* argv[])
{int fd;char buf[128] = { 0 };if ((fd = open(FIFO_NAME, O_RDONLY)) == -1)PRINT_ERR("open error");while (1) {memset(buf, 0, sizeof(buf));read(fd, buf, sizeof(buf));if (strcmp(buf, "quit") == 0)break;printf("buf = %s\n", buf);}close(fd);return 0;
}
1.有名管道可以用于任意进程间的通信
2.有名管道半双工的通信方式
3.有名管道的大小也是64K
4.有名管道也是不支持使用lseek
5.有名管道读写特点
读端存在,写管道:有多少写多少,直到写满位置(64K),写阻塞。
读端不存在,写管道
读端没有打开过,写管道:此时写端在open的位置阻塞
读端先打开后关闭,写管道:管道破裂,收到SIGPIPE信号,杀死进程
写端存在,读管道:有多少读多少,没有数据可读的时候读阻塞。
写端不存在,读管道
写端没有打开过,读管道:此时读端在open的位置阻塞
写端先打开后关闭,读管道:有多少读多少,如果没有数据可读的时候立即返回。
在进程执行的时候,用户可以给进程发送信号,进程收到信号后,对信号的处理有三种方式,分别是:捕捉,忽略,默认,大部分进程信号的默认是杀死进程,信号的发送和执行是异步过程
1-31为稳定信号,发出就一定会处理,34-64为不稳定信号,内核不使用
kill -信号号 PID
SIGCHLD:当子进程退出的时候,父进程会收到这个信号
注:在所有的信号中,只有SIGKILL/SIGSTOP两个信号不能被捕捉,也不能被忽略。只能执行默认的动作。
#include typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:注册一个信号处理函数
参数:@signum:信号号@handler:信号处理的方式SIG_IGN:忽略SIG_DFL:默认自己填写信号处理函数:捕捉void signal_handle(int signo){}
返回值:成功返回handler(handler是一个函数指针,返回它的地址),失败返回值SIG_ERR,并置位错误码
#include void signal_handle(int signo)
{if (signo == SIGINT) {printf("我收到了一个ctrl+c的信号\n");}
}
int main(int argc, const char* argv[])
{// 1.对SIGINT进行捕捉,如果安下ctrl+c,会执行信号处理函数// if(signal(SIGINT,signal_handle)==SIG_ERR)// PRINT_ERR("signal error");// 2.对SIGINT进行忽略// if (signal(SIGINT, SIG_IGN) == SIG_ERR)// PRINT_ERR("signal error");// 3.对SIGINT进行默认处理if (signal(SIGINT, SIG_DFL) == SIG_ERR)PRINT_ERR("signal error");while (1);return 0;
}
#include void signal_handle(int signo)
{if (signo == SIGUSR1) {printf("我收到了一个SIGUSR1的信号\n");}
}
int main(int argc, const char* argv[])
{// 1.对SIGUSR1进行捕捉,// if(signal(SIGUSR1,signal_handle)==SIG_ERR)// PRINT_ERR("signal error");// 2.对SIGUSR1进行忽略// if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)// PRINT_ERR("signal error");// 3.对SIGUSR1进行默认处理if (signal(SIGUSR1, SIG_DFL) == SIG_ERR)PRINT_ERR("signal error");while (1);return 0;
}
//发信号 kill -10 pid
#include void handle(int signo)
{if(signo == SIGPIPE){printf("我收到了管道破裂的信号\n");}if(signo == SIGINT){printf("我收到了一个ctrl+c的信号\n");}}
int main(int argc, const char* argv[])
{int pipefd[2];char buf[128] = { 0 };if(signal(SIGPIPE,handle)==SIG_ERR)PRINT_ERR("signal error");if(signal(SIGINT,handle)==SIG_ERR)PRINT_ERR("signal error");if (pipe(pipefd))PRINT_ERR("pipe error");close(pipefd[0]); // 关闭读端write(pipefd[1], "a", 1); // 写管道while (1);return 0;
}
#include
void handle(int signo)
{waitpid(-1, NULL,WNOHANG);
}
int main(int argc, const char* argv[])
{pid_t pid;pid = fork();if (pid == -1) {PRINT_ERR("fork error");} else if (pid == 0) {sleep(1);printf("child1 :pid = %d\n", getpid());} else {if(signal(SIGCHLD,handle)==SIG_ERR)PRINT_ERR("signal error");while(1);}return 0;
}
int raise(int sig);
功能:给自己发信号
参数:@sig:信号号
返回值:成功返回0,失败返回非0int kill(pid_t pid, int sig);
功能:给指定pid的进程发送信号
参数:@pid:进程号pid > 0 :给pid号的进程发信号pid = 0 :给同组的进程发送信号pid = -1:给所有有权限的进程发送信号pid <-1:首先会对pid取绝对值,给和这个绝对值相同的组的进程发送信号@信号号
返回值:成功返回0,失败返回-1置位错误码unsigned int alarm(unsigned int seconds);
功能:当seconds倒计时为0的时候发送SIGALRM信号
参数:@seconds:秒钟数,如果填写为0,取消挂起的信号
返回值:如果alarm是第一次调用,返回0.如果alarm不是第一次调用,返回上一次调用的剩余秒钟数
#include
void handle(int signo)
{//回收子进程的资源waitpid(-1, NULL,WNOHANG); //给自己发送杀死的信号raise(SIGKILL);
}
int main(int argc, const char* argv[])
{pid_t pid;pid = fork();if (pid == -1) {PRINT_ERR("fork error");} else if (pid == 0) {sleep(1);printf("child1 :pid = %d\n", getpid());} else {if(signal(SIGCHLD,handle)==SIG_ERR)PRINT_ERR("signal error");while(1);}return 0;
}
test.c
#include int main(int argc,const char * argv[])
{while(1);return 0;
}
kill.c
#include
//./a.out pid
int main(int argc, const char* argv[])
{if(kill(atoi(argv[1]),SIGKILL))PRINT_ERR("kill error");return 0;
}
#include void handle(int signo)
{printf("我收到了一个闹钟信号\n");
}
int main(int argc,const char * argv[])
{if(signal(SIGALRM,handle)==SIG_ERR)PRINT_ERR("signal error");//第一次调用alarm返回值是上一次剩余的秒钟数,但是之前没有调用过alarm,//所以返回值就是0printf("alarm ret = %d\n",alarm(5));//等待2秒sleep(2);//第二调用alarm函数返回的上一次剩余的秒钟数3.它会把上一次剩余的秒钟数刷新为//5秒.5秒之后执行闹钟的信号处理函数printf("alarm ret = %d\n",alarm(5));while(1);return 0;
}
#include void handle(int signo)
{printf("系统自动出牌了\n");alarm(4);
}
int main(int argc, const char* argv[])
{if (signal(SIGALRM, handle) == SIG_ERR)PRINT_ERR("signal error");alarm(4);char ch;while (1) {ch = getchar();getchar();printf("用户手动出牌=%c\n", ch);alarm(4);}return 0;
}