uar通信有哪些通信方式_最古老的通信方式Unix进程间通信【超级详细】

1e951df2c51be1c5d02bd1d4b32273fe.png

我们都知道Linux是从Unix发展来的,早期的Unix进程通过无名管道(pipe)、有名管道(fifo)、信号(signal)的方式实现。在system V中进程间通信又加入了消息队列(message queue)、共享内存(share memory)、信号量(semphore)。在BSD Unix中通过socket实现。以上几种方式,Linux全部继承下来了。

什么是进程间通信

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)

599d9869b9b1c8e9ef6fa43681b3f444.png

无名管道(pipe)

无名的管道的特点

  • 只能用于有血缘关系(父子进程、兄弟进程)之间的通信;

  • 管道是单工通信模式,只能一端写入,从另一端读取;

  • 单独构成一个独立的文件系统:管道独立于两端的进程,就是一个文件;但又不是一个普通文件,不属于某种文件系统,并且只存在于内存中;

函数原型

#include int pipe(int pipefd[2]);
  • pipefd[0]用于读取管道数据,pipefd[1]用于写入管道数据;

  • 成功返回0,失败返回-1。

注意:管道不是一个普通的文件,不存在文件名,仅仅存在于内存中,所以使用 ls 命令看不见。无名管道是由进程创建的,因为没有名字,所以其他进程想要使用它,只能通过继承的方式打开,这也是为什么无名管道只能用于有关系的两个进程。

7630636f4217133ed24ffe381934a7f4.png

上图表示了父子进程通过无名管道进行进程间通信,父进程通过fd[1]写入数据,子进程通过fd[0]读取数据。

示例:

子进程写入数据,父进程读取数据。

#include #include #include #include #include int main(){    int fd[2] = {0};    //创建无名管道    int ret = pipe(fd);    if (-1 == ret)    {           perror("pipe");        exit(1);    }    int num = 0;    //无名管道只能用于父子进程之间的通信    pid_t pid = fork();    if (-1 == pid)    {        perror("fork");        exit(1);    }    else if (0 == pid)   //子进程    {        num++;        printf("child num = %d\n", num);        ret = write(fd[1], &num, sizeof(num));   //把变量写入管道        if (-1 == ret)        {            perror("write");            exit(1);        }    }    else                 //父进程    {        usleep(100000);        ret = read(fd[0], &num, sizeof(num));        if (-1 == ret)        {            perror("read");            exit(1);        }        num++;        printf("parent num = %d\n", num);        int status;        wait(&status);    }    return 0;}

无名管道读写特性

  • 写端存在且管道中有数据时,read返回实际读取的字节数。当管道没有数据时,读进程阻塞(直到管道中有数据)。
  • 写端不存在且管道中有数据时,read返回实际读取的字节数。当管道无数据时,read返回0。
  • 读端存在且管道有空间时,write返回实际写入的字节数。当管道无空间时,写进程阻塞。
  • 读端不存在不论管道是否有空间进程都会异常结束,会导致管道断裂。系统不允许写一个读端不存在的管道。

有名管道

有名管道不同于无名管道,它提供了一个路径名与之关联,所以有名管道是以文件的形式存在与文件系统中,任意两个进程都可以通过有名管道通信。

有名管道严格遵循先进先出,读取数据总是从开始处读,写入数据总是添加到末尾。

函数原型

#include #include int mkfifo(const char *pathname, mode_t mode);
  • pathname 管道文件名字;

  • mode 管道文件权限,如0666

  • 成功返回0,失败返回-1。

示例

进程A创建管道,通过键盘往管道写入数据;

进程B从管道读取数据,读到“bye”退出。

fifo_write.c

#include #include #include #include #include #include #include #include #include int main(){    //创建有名管道(文件)    int ret = mkfifo("fifo.tmp", 00400 | 00200);    if (-1 == ret)    {           perror("mkfifo");        exit(1);    }    //默认是阻塞的    int fd = open("fifo.tmp", O_WRONLY);    if (-1 == fd)    {        perror("open");        exit(1);    }    char buf[32] = {0};    while (1)    {        scanf("%s", buf);        ret = write(fd, buf, strlen(buf));        if (-1 == ret)        {            perror("write");            exit(1);        }        if (!strcmp(buf, "bye"))        {            break;        }        memset(buf, 0, sizeof(buf));    }    close(fd);    unlink("fifo.tmp");     //删除管道文件    return 0;}

fifo_read.c

#include #include #include #include #include #include #include int main(){    //直接打开有名管道    int fd = open("fifo.tmp", O_RDONLY);    if (-1 == fd)     {           perror("open");        exit(1);    }    int ret;    char buf[32] = {0};    while (1)     {           ret = read(fd, buf, sizeof(buf));        if (-1 == ret)        {            perror("read");            exit(1);        }        if (!strcmp(buf, "bye"))        {            break;        }        printf("%s\n", buf);        memset(buf, 0, sizeof(buf));    }    close(fd);    return 0;}

信号

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件。Linux中信号是对早期的unix信号机制进行了扩展。

Linux所有信号

d61fc9d38ea1108a059718d973f6c503.png

常用的信号有

信号名含义默认操作
SIGHUB用户终端关闭时产生,发给和该终端关联的所有进程。终止
SIGINT用户输入CTRL+C时产生,发给当前终端所有前台进程。终止
SIGSEV该信号在非法访问内存时产生,如野指针、缓冲区溢出终止
SIGPIPE当进程向一个没有读端的管道中写书数据时产生,代表管道断裂终止
SIGKILL该进程用来结束进程,并且不能被捕捉和忽略终止
SIGSTOP该信号用于暂停进程,并且不能被捕捉和忽略暂停
SIGCONT该信号让进程进入运行态继续
SIGALRM该信号用于通知进程定时器时间已到终止

发送信号的命令

kill [-signal] pid
  • -signal 用于指定信号

  • pid 用于指定接收信号的进程

killall [-u user | prog
  • user 用于指定用户名

  • prog 用于指定进程名

信号相关的API

#include #include #include int kill(pid_t pid, int sig);      //给指定进程发送信号int raise(int sig);                //给进程本身发送信号unsigned int alarm(unsigned int seconds);  //指定时间后给进程本身发送SIGALRM信号typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);  //设置信号处理函数

函数signal()非常重要,可以指定收到信号后的响应动作。

  • signum 要设置的信号种类;

  • handler 信号处理函数(收到信号后调用)。

示例1

输入进程号,杀死对应的进程。

#include #include #include int main(){    pid_t pid;    scanf("%d", &pid);    //kill(pid, 2);    kill(pid, SIGINT);   //给pid进程发送SIGINT信号    return 0;}

示例2

每隔固定时间发送SIGALRM信号,收到信号打印。

#include #include #include void handler(int sig){    printf("收到信号 %d\n", sig);}void show(int sig){    printf("helloworld\n");    alarm(2);}int main(){    //signal(SIGINT, SIG_IGN);    //忽略SIGINT信号    //signal(SIGKILL, SIG_IGN);     //SIGKILL和SIGSTOP不能被忽略    signal(SIGINT, handler);    signal(SIGALRM, show);    alarm(2);                     //2秒后给系统发送SIGALRM信号,只发一次    while (1);    return 0;}

dcb738010926525ebd02e13200211022.png

8bdaf09a2abffce15f83365e2dc3bdcd.png

15cd7e33de81d8735d86116738a3c922.png

ca5e60655619c38fdcc512e9be71e45d.png

084cc63379737264bd4c7c4bd92ab56f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值