linux线程间通讯----管道、信号

 

进程间通讯机制:

unix继承: 管道、信号

system V IPC对象:共享内存、消息队列、信号灯集

1.管道

管道分为无名管道和有名管道--区别在于创建的管道是否在文件系统中不可见,无名不可见,有名可见。

(1)无名管道

特点:

1.在创建之后再文件系统中不可见

2.以半双工的方式进行通信,两根管道

3.拥有固定的读段和写段

4.只能用于具有亲缘关系的进程间通讯

(2)无名管道的功能函数

1.无名管道的创建--pipe()

#include <unistd.h>

int pipe(int pipefd[2]);

参数:
    pipefd:存放无名管道读段和写段的数组首地址
    pipefd[0]---读段
    pipefd[1]---写段
    
返回值:
    成功返回0,失败返回-1
    
练习:在一个进程中,创建一个子进程,子进程从键盘获取数据,父进程打印出数据

 

/*===============================================
*   文件名称:pipe.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/types.h>
#include <wait.h>

int main(int argc, char *argv[])
{ 
    int pipefd[2]={0};	//文件描述符的空间
    char buf[64]={0};
    int ret=pipe(pipefd);	//创建无名管道
    if(ret<0)
    {
        perror("pipe");
        exit(-1);
    }
    pid_t pid=fork();	//创建子进程
    if(pid<0)
    {
        perror("fork");
    }
    if(pid==0)		//子进程
    {
        
        printf("请输入一串数据\n");
        scanf("%s",buf);
        write(pipefd[1],buf,strlen(buf));//写入无名管道
        memset(buf,0,64);		//清空缓冲区
        
    }
    else	//父进程
    {	
        sleep(1);		//等待子进程输完
        read(pipefd[0],buf,64);	//从无名管道读取数据
        printf("父:%s\n",buf);	//打印数据
        wait(NULL);    		//回收子进程
    }	

    return 0;
} 

2.无名管道的读写特性

读特性:

                写端存在:

                                管道有数据:返回读到得到字节数

                                管道无数据:read阻塞

                写段不存在:

                                管道有数据:返回读到的字节数

                                管道无数据:返回0

写特性:

                读段存在:

                                管道有空间:返回写入的字节数

                        管道无空间:write阻塞,直到有空间为止,有一个空间写一个字符

                读段不存在:

                        无论管道是否有空间,管道破裂,系统会发送SIGPIPE给当前进程,进程会死亡

练习:1.计算无名管道空间大小

                2.验证管道破裂

/*===============================================
*   文件名称:length_pipe.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{ 
    int pipefd[2];
    pipe(pipefd);
    close(pipefd[0]);//关闭无名管道的读端
    pid_t pid=fork();
    if(pid<0)
    {
        perror("");
    }
    if(pid==0)
    {
        write(pipefd[1],"hello",5);
        sleep(3);//测试是否会正常退出
        
        /*int count=1,num=0;
        char buf[1024]={0}; 
        while(1)
        {
            count=write(pipefd[1],buf,1024);
            num+=count;
            printf("%d\n",num);   //测试管道大小
        }
        */
    }
    else
    {
        int status;
        wait(&status);//捕获信息
        printf("%d %d %d %d\n",WIFEXITED(status),WEXITSTATUS(status),WIFSIGNALED(status),WTERMSIG(status));
    }

    return 0;
} 

(3)有名管道

有名管道创建之后,会在文件系统中以管道文件的形式存在。

有名管道可以用于任意两个进程间通讯,没有固定的读端,写端

目录

1.管道

(1)无名管道

(2)无名管道的功能函数

1.无名管道的创建--pipe()

2.无名管道的读写特性

(3)有名管道

(4)有名管道的功能函数

2.信号

1.常用信号

2.信号相关指令

3.信号相关接口函数

4.定时器

5.捕获信号--signal


(4)有名管道的功能函数

1.有名管道的创建 -mkfifo()

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
参数:
    pathname:创建管道文件的文件名
    mode:表示创建的管道文件的权限,0664
    
返回值:
    成功返回0,失败返回1

练习:创建一个有名管道,一个进程向管道输入数据,另外一个向管道输出程序

输入程序:

/*===============================================
*   文件名称:mkfifo.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    /*
    int ret1=mkfifo("fifo",0664);//创建管道文件,已存在会保存
    if(ret1<0)
    {
        perror("mkfifo");
        exit(-1);
    }
    */
    int fp=open("fifo",O_RDWR);
    printf("fifo open\n");
    char buf[64]={0};
    int n=5;
    while(n--)
    {
        scanf("%s",buf);
        write(fp,buf,64);
    }


    close(fp);
    return 0;
} 

输出程序:

/*===============================================
*   文件名称:getfifo.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{ 
    int fd=open("fifo",O_RDWR);
    if(fd<0)
    {
        perror("open");
        exit(-1);
    }
    printf("fifo open \n");
    char buf[64]={0};
    int n=5;
    while(n--)
    {
        read(fd,buf,64);
        puts(buf);
        memset(buf,0,64);
    }
    close(fd);
    return 0;
} 

2.信号

信号:是终端在软件层次上的一种模拟

kill -l 常用信号 前32种是从unix继承而来。

型号的处理方式:

        默认处理:

        忽略处理:

        捕获信号:

1.常用信号

SIGHUP: (1) 终端的控制进程结束时

SIGINT: (2) (ctrl+c)结束前台进程

SIGQUIT: (ctrl +\ )

SIGLL: 进程企图执行一条非法指令时(可执行文件本身出错,堆栈溢出)段错误

SIGFPE:致命的算术运算错误,除数为0

SIGKILL:用来结束程序的运行,不能被阻塞、处理、忽略

SIGALRM:(14)定时器到时发出

SIGSTOP:用来暂停一个进程,不能被阻塞、处理、忽略

SIGTSTP:暂停交互过程(ctrl+z)

SIGCHLD:子进程改变状态时,父进程收到此信号

SIGABORT:用于结束进程

2.信号相关指令

kill -l:查看看单签系统中的所有信号

kill -信号编号 进程号 :向指定进程发送对应边好的信号

        eg:   kill -9 1234 向进程号为1234的进程发送信号9

                kill -9 -1234 (-负号)向进程组1234发送信号9

                kill -9 -1 向除了init以外的所有进程发送信号9

                kill -9 -0 信号被发送到所有和当前进程在同一进程组的进程

3.信号相关接口函数

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

参数:
    pid:指定的进程号
    sig:指定的信号,编号也可以
    
返回值:
    成功返回0,失败返回-1;

#include <signal.h>

int raise(int sig);

向当前进程发送信号
参数:
    sig:指定信号

返回值:
    成功返回0值,失败返回非0值

4.定时器

定时时间到,当前进程会接收到编号为14的SIGARLM信号,默认终止当前进程、

一个进程中,最多只能存在一个定时器。

定时器不改变原本函数的执行过程,只是计时,当程序运行时间超过定时器的时间,进程接收到SIGAELM信号

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

参数:
    seconds:定时的秒数
        
返回值:
    成功,如果之前没有定时器,返回0。如果之前有定时器,返回其删一个定时器剩余时间
        
       eg:
        alarm(10);    返回值为0
        sleep(3);
        alarm(5);    返回值为10-3=7
            
        总定时时间为3+5=8,第二次调用ararm,刷新为5.
            
            
#include <unistd.h>
int pause(void);
功能:阻塞函数,等待定时结束

/*===============================================
*   文件名称:alarm.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int ret = alarm(5);
    printf("ret=%d\n",ret);
    sleep(3);
    
    ret=alarm(3);
    printf("ret=%d\n",ret);	//打印上次定时器的剩余时间
    sleep(6);

    return 0;
} 

5.捕获信号--signal

#include <signal.h>

typedef void (*sighandler_t)(int);

//等同于typedef void (*sighandler_t)(int) sighandler;

sighandler_t  signal(int signum, sighandler_t handler);

捕捉到信号就调用函数,而不是采用默认方式
参数:
    signum:指定信号,可填编号
    handler:信号处理函数或者SIG_IGN(选择以忽略方式处理指定信号)、SIG_DFL(选择以默认方式指定信号)
        信号处理函数不传参,参数为捕获到的型号的编号
返回值:


eg:
    signal(14,time_out)
    void time_out(int num)
    {
            printf("time_out %d",num);//num默认为捕获到的信号编号
    }
执行完函数之后,继续向下执行程序

练习:

创建一个子进程,子进程结束时,父进程提示子进程退出信息

SIGCHLD:子进程改变状态时,父进程收到此信号

/*===============================================
*   文件名称:signal.c
*   创 建 者:     
*   创建日期:2022年08月10日
*   描    述:
================================================*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>



void func(int num);
int main(int argc, char *argv[])
{ 
    pid_t pid = fork();//创建子进程
    if(pid<0)
    {
        perror("fork");
    }
    if(pid==0)//子进程
    {
        alarm(2);//定时2秒
        while(1)
        {

        	signal(14,func);//接收到信号,则执行func
        }
        //exit(0);
    }
    else
    {
        //kill(pid,9);
        int wstatus;
        signal(SIGINT,func);	
        signal(17,func);
        wait(&wstatus);	//回收子进程
        printf("%d %d %d %d\n",WIFEXITED(wstatus),WEXITSTATUS(wstatus),WIFSIGNALED(wstatus),WTERMSIG(wstatus));
    }
    return 0;
} 

void func(int num)
{
    printf("signal = %d\nchile is dead \n",num);
}

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值