目录
1.匿名管道
匿名管道没有与缓冲区绑定的名字,主要用于有亲缘关系的进程间通信,
通过调用pipe函数创建管道
#include <unistd.h>
int pipe(int filedes[2]);
pipe函数在内核开辟一块用于通信的缓冲区(缓冲区没有名字,故称匿名管道)
管道有一个读端一个写端,通过参数传出给两个文件描述符,分别对应管道的读写端:filedes[0]指向管道的读端;filedes[1]指向管道的写端
用户程序通过read(filedes[0]);或者write(filedes[1]),向该文件读写数据其实就是读写内核缓冲区。
pipe函数调用成功返回0,调用失败返回-1。
例5-20:匿名管道的使用
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int pipe_fd[2];
if(pipe(pipe_fd)<0){
printf("pipe create error\n");
return -1;
}//if
printf("pipe create success\n");
if (fork()>0) { //father
int r;
char buf[15+1];
printf("***Father want to read from son\n");
r=read(pipe_fd[0],buf,15); //从管道读端读取数据到buf
buf[r]=0;
printf("***Father got strings: %s\n",buf);
}
else { //son
const char *test="a test string from son ";
printf("Son sleep:\n");
sleep(5);//父亲要读,必须等儿子写完
printf("Son write after sleep: %s\n",test);
write(pipe_fd[1],test,strlen(test)); //从管道写端写入test
}
close(pipe_fd[0]);
close(pipe_fd[1]);
}//main
由于儿子里的sleep(5),父亲要读管道里的内容,必须等儿子沉睡后醒来,然后儿子写入内容
能观察到在输出Son sleep:后停顿5秒
例5-21:匿名管道的大小
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int pfd[2],i=0;
pid_t pid;
if(pipe(pfd)<0){
printf("pipe create error\n");
return -1;
}
printf("pipe create success\n");
pid=fork();
if(pid<0){
printf("fork create error\n");
return -1;
}
if(pid>0){//father
close(pfd[0]);//关闭读
char buf[]="a";
while(1){
write(pfd[1],buf,sizeof(buf[0]));//把buf写入管道
printf("write pipe %d, total size %dB\n", i+1, (i+1)*sizeof(buf[0]));
i++;
}
}
else{//son
close(pfd[1]);//关闭写
pause();
return 0;
}
}//main
Linux默认的PIPE缓冲区大小为64KB
2.命名管道
通信的多进程间不存在亲缘关系时,通过提供一个有路径名的管道文件对应的缓冲区实现通信
如何创建?(通信各方要有权访问管道文件)
- 利用mkfifo命令
- 利用代码建管道
shell中对应命名管道操作的命令:mkfifo
特点:
- 允许无亲缘关系进程间的通信。
- FIFO以文件形式存在于文件系统中,但不通信的话文件内没有数据。
- 文件操作方式基于“先进先出”原理
- 提供灵活多样的同步机制
例5-22命名管道的使用
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
//读管道
int main(int argc,char *argv[]){
if (argc !=2) {
printf("not enough params,give pipefile name\n");
exit(1);}
char *pipefile;
pipefile=argv[1];
char buf[100];
int fd,i;
printf("read open namedpipe!!\n");
fd=open(pipefile,O_RDONLY,0);
if (fd<0) {
printf("no such namedpipe file !!\n");
exit(-1); }
printf("OK!namedpipe opened for read!\n");
i=read(fd,buf,100);
buf[i]=0;
printf("OK!readed from namedpipe: %s!\n",buf);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
//写管道
int main(int argc,char *argv[]){
if (argc !=2){
printf("not enough params,give pipefile name\n");
exit(1);}
char *pipefile;
pipefile=argv[1];
int fd;
char *teststr="test strings!";
printf("OPEN namedpipe--%s for write!\n",pipefile);
fd=open(pipefile,O_WRONLY,0);
printf("OK!namedpipe--%s opened for write!\n",pipefile);
sleep(5);
write(fd,teststr,strlen(teststr));
printf("OK!namedpipe write successfully!\n");
exit(0);
}
(1)用命令创建管道
也可以用代码创建管道,在命令行输入管道名和路径,不输入路径默认在当前路径下建立
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
int main (int argc, char *argv[] ){
if (argc != 2) {
printf("USEMSG: create_fifo {fifoname}\n");
exit (1);
} //if
char *pipefile;
pipefile=argv[1]; //命令行传入参数做文件名
if ((mkfifo (pipefile, 0666 )) < 0) {//权限是rwx,6对应rw-
perror("mkfifo failed");
exit(1);
}//if
printf("mkfifo successed,name is %s\n",pipefile);
return 0;
}
(2)打开读端:读端等待写端
(3)打开写端:写入
(4)读端完成读
例5-23命名管道非阻塞模式通信
注意权限,最好su root
注意创建管道的路径
//读
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/home/chenqiqi/文档/code/book/myfifo"
void main(int argc,char** argv){
char buf_r[100];
int fd, nread;
// 创建管道
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));
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0); //不要阻塞,没数据也往下执行
if(fd==-1){ //出错警告
perror("fail");
exit(1);
}
while(1) {
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==-1) {//出错
if(errno==EAGAIN) //出错号
printf("no data yet\n");
}else{
printf("read %s from FIFO\n",buf_r);//读数据
}
sleep(1);
} //while
pause(); //暂停,等待信号
unlink(FIFO); //删除文件
}
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//写
#define FIFO "/home/chenqiqi/文档/code/book/myfifo"
void main(int argc,char** argv){
int fd,j;
char w_buf[100];//用于缓存参数传递的信息
int nwrite;
//打开命名管道FIFO
fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);
if(argc==1){//判断有没有输入
printf("Please send something\n");
exit(-1);
}//if
strcpy(w_buf,argv[1]); //从agrv复制字符串到w_buf
//连续10次向管道写入数据
for(j=0;j<10;j++){
if((nwrite=write(fd,w_buf,strlen(w_buf)))==-1){
// 将w_buf所指内存写入strlen(w_buf)个字节,到参数fd所指的文件
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);
}//for
}
因为读程序要创建管道,所以先执行读,此时读不到数据
再执行写,写入数据
例5-24多方读管道同步处理
父亲创建2个孩子,2个孩子各写10句,父亲读20句
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int r,i,p1,p2,fd[2];
char buf[10],s[50];
pipe(fd); //创建管道
while((p1=fork())==-1); //创建子程序失败时,循环]
if(p1==0){ //孩子1
//lockf(fd[1],1,0);
sprintf(buf, "AAAAAAAAAA\n");//把字符串放入buf
printf("child process p1 WRITE!\n");
for(i=1;i<=10;i++){
write(fd[1],buf,10); //把buf中的字符写入管道
sleep(1);
}
//lockf(fd[1],0,0);
exit(0);
}
else{//父亲又建立一个孩子
while((p2=fork())==-1);
//创建子程序失败时,循环
}
if(p2==0){ //孩子2
//lockf(fd[1],1,0);
sprintf(buf, "BBBBBBBBBB\n");///把字符串放入buf
printf("child process p2 WRITE!\n");
for(i=1;i<=10;i++){
write(fd[1],buf,10); //把buf中的字符写入管道
sleep(1);
}
//lockf(fd[1],0,0);
exit(0);
}
//父亲读20次
printf("father read:\n");
for(i=1;i<=20;i++) {
if((r=read(fd[0],s,10))==-1)//从fd读到s中
printf("can’t read pipe\n");
else
printf("%s",s);
}
printf("father read end.\n");
wait(0);
wait(0);
exit(0);
}
输出上图结果后因为sleep(1)停顿,然后再次输出父亲读的20次:
孩子1写了一两句就被孩子2打断了,为了避免这种情况,加上管道自身的锁,运行结果如下:
孩子1先写了10句A,孩子2又写了10句B,然后父亲一起读出这20句
3.软中断
软中断是对硬件中断的一种模拟,发送软中断就是向接收进程发送一个信号。
接收进程收到软中断信号后,执行一个事先关联的处理程序。软中断处理程序必须等接收进程执行时才生效。进程可向自身发送软中断信号,以便在特殊情况下,进程能转入规定好的处理程序。
(1)信号接收方进程对软中断信号预置处理程序
软中断信号预置函数:signal (sig, function)
(2)发送信号的进程向指定进程发送某种类型的软中断信号;
发送软中断信号的函数:int kill (pid, sig)
(3)信号动作触发信号处理
例5-25软中断
#include<signal.h>
#include<sys/types.h>
int k; //定义循环变量
//定义软中断处理函数
void mysigfunc(int sig) {
k=0; //修改循环变量k的值为0
printf("get signal!\n");
}
int main(){
//预置信号处理函数,接收到SIGHUP信号即触发mysigfunc函数
signal(SIGHUP, mysigfunc);
k=1;
//软中断处理函数通过修改循环变量使循环结束,
while(k==1)
printf("Hello! %d\n",getpid());
printf("OK!\n");
//结束后输出提示OK
exit(0);
}
启动进程后,在另一个终端改为root用户,执行以下命令