热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[linux实验]管道及管道间的通信信号

#includeintpipe(intfiledes[2])pipe()会建立管道。并将文件描述词由filedes数组返回。filedes[0]为管道的读取端&

#include int pipe(int filedes[2])

pipe() 会建立管道。并将文件描述词由 filedes 数组返回。
filedes[0] 为管道的读取端,filedes[1] 为管道的写入端。
该函数若执行成功,则返回 0,否则返回-1, 错误原因存于 errno 中


#include int lockf(int fd,int cmd,off_t len)

lockf() 用于对一个已经打开的文件,施加、检测和移除 POSIX 标准的软锁。
fd 是 目标文件的文件描述符,用户进程必须对 fd 所对应的文件具有 O_WRONLY 或是 O_RDWR 的 权限。
cmd 是指lockf()所采取的行动,其合法值定义于unistd.h内,其值为以下几种:
F_ULOCK 释放锁定,也可用整数 0 代替;
F_LOCK 将指定的范围上锁,也可用整数 1 代替; 如果被上锁会阻塞
F_TLOCK 先测试再上锁,也可用整数 2 代替; 如果被上锁不会阻塞
F_TEST 测试指定的范围是否上锁。
len 是指上锁的解锁的范围,以字节为单位。0表示往后全部
lockf()函数计算上锁范围是以文件读 写指针加上 len,因此 lockf()通常与 lseek()搭配使用。
在该实验程序中,使用 lockf(fd[1],1,0) 来实现对管道写入端的加锁,以防止写冲突,
使用 lockf(fd[1],0,0) 来实现对管道写入端的解锁。


#include
void perror(const char *s)

perror() 用来将上一个函数发生错误的原因输出的标准错误(stderr)。
参数 s 所指 的字符串会先打印出来,后面再加上错误的原因。
上一函数的错误原因将依照全局变量 errno 的值来决定输出什么字符串。


#include
ssize_t read(int fd,void * buf ,size_t count)

read()会把参数 fd 所指的文件传送 count 个字节到 buf 指针所指的内存中。
若参数 count为0,则read()不会有作用并返回0。
返回值为实际读取到的字节数,如果返回0, 表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。
如果顺利 read()会返回实际读到的字节数,最好能将返回值与参数 count 作比 较,若返回的字节数比要求读取的字节数少,则有可能读到了文件尾、从管道(pipe)或 终端机读取,或者是 read()被信号中断了读取动作。
当有错误发生时则返回-1,错误代 码存入 errno 中,而文件读写位置则无法预期。


#include ssize_t write (int fd,const void * buf,size_t count)

write()会把参数 buf 所指的内存写入 count 个字节到参数 fd所指的文件内。同时, 文件读写位置也会随之移动。
返回值如果顺利 write()会返回实际写入的字节数。
当有错 误发生时则返回-1,错误代码存入 errno 中。


简单的管道通信
这里需要注意一个逻辑关系,因为需要先向管道中写消息,所以需要把从管道中读取消息的这个操作阻塞掉。当完成向管道中写消息的操作后再对读操作解锁。

另外一个需要注意的是
操作系统会自动帮我们管理管道的释放、上锁和解锁等操作,所以这里我们可以把lockf去掉。

#include
#include
#include #define SIZE 50
int main(){int fd[2], p;char s[50];pipe(fd);lockf(fd[0], F_LOCK, 0);p=fork();if (p){//parentstrcpy(s, "This is a message");write(fd[1], s, SIZE);lockf(fd[0], F_ULOCK, 0);}else{//childread(fd[0], s, SIZE);printf("%s\n", s);}return 0;
}


代码设计
三个进程,一个进程负责从终端输入并写入fda管道,一个进程负责从管道读出内容写到另一个管道,一个进程负责从管道中读出写到终端。每次只允许读出或写入一个字符。
由于父进程应该是最后才被释放的,所以这里用父进程来负责最后把读出的字符写回到终端。
这里由于操作系统自动帮我们管理管道,所以不对管道进行lockf操作。

#include
#include
int main(){pid_t p1, p2;int fda[2], fdb[2];char ch;pipe(fda);pipe(fdb);p1&#61;fork();if (p1){//parentp2&#61;fork();if (p2){//parentdo{read(fdb[0], &ch, sizeof(char));putchar(ch);}while (ch!&#61;&#39;\n&#39;);}else{//child2do{read(fda[0], &ch, sizeof(char));if (ch>&#61;&#39;a&#39; && ch<&#61;&#39;z&#39;)ch&#61;ch-&#39;a&#39;&#43;&#39;A&#39;;else if (ch>&#61;&#39;A&#39; && ch<&#61;&#39;Z&#39;)ch&#61;ch-&#39;A&#39;&#43;&#39;a&#39;;write(fdb[1], &ch, sizeof(char));}while (ch!&#61;&#39;\n&#39;);}}else{//child1do{ch&#61;getchar();write(fda[1], &ch, sizeof(char));}while (ch!&#61;&#39;\n&#39;);}return 0;
}


#include
void (*signal(int signum,void (*handler)(int)))(int)

在这里插入图片描述
signal() 会依照参数 signum 指定的信号编号来设置该信号的处理函数。
当指定的 信号到达的时候&#xff0c;就会跳转到参数 handler 指定的函数执行。
如果参数 handler 不是 函数指针&#xff0c;则必须是下列两个常数之一&#xff1a;
SIG_IGN 忽略参数 signum 指定的信号&#xff1b;
SIG_DFL 将参数 signum 指定的信号重设为核心预设的信号处理方式。

在使用 signal()函数设置信号处理方式之后&#xff0c;如果规定的信号到来&#xff0c;则在跳转到自 定义的 handler 处理函数执行后&#xff0c;系统会自动将此信号的处理函数换回原来系统预先设 定的处理方式。
即&#xff1a;signal()的作用是宣告性的&#xff0c;仅仅是修改信号处理表格的某一项&#xff0c; 而不是立即执行函数 handler。当程序执行过 signal()以后&#xff0c;表示自此参数 1 的信号 &#xff08;signum&#xff09;将受到参数 2 的函数&#xff08;handler&#xff09;的管制。并非是执行到该行就立即会对信号 做什么操作。
信号被接受后&#xff0c;进程对该信号的处理是先重设信号的处理方式为默认状态&#xff0c;再执行所指定的信号处理函数。所以&#xff0c;如果每次都要用指定的函数来接受特定信 号&#xff0c;就必须在函数里再设定一次接受信号的操作。该函数若执行成功,则返回先前的信号 处理函数指针,否则返回-1(SIG_ERR)。


linux信号列表
在这里插入图片描述
在这里插入图片描述
其中操作符号所代表的意义&#xff1a;
A: 默认为终止进程&#xff1b;
B: 默认为忽略此信号&#xff1b;
C: 默认为内存倾卸&#xff08;core dump&#xff09;&#xff1b;
D: 默认为暂停进程执行&#xff1b;
E: 此信号不可拦截&#xff1b;
F: 此信号不可忽略&#xff1b;
G: 此信号非 POSIX 标准。

说明&#xff1a;进程对信号的是否忽略的结果是通过信号的掩码来实现的


#include
#include
#include
int main(){int i;alarm(4);for (i&#61;1; i<&#61;8; i&#43;&#43;){printf("%d\n", i);sleep(1);}return 0;
}

在这里插入图片描述


#include
#include
#include
void func(){printf("GET SIGLRM\n");
}
int main(){int i;alarm(4);signal(SIGALRM, func);for (i&#61;1; i<&#61;8; i&#43;&#43;){printf("%d\n", i);sleep(1);}return 0;
}

在这里插入图片描述
这里signal的作用是把接收到SIGALRM信号时本来对应的应该终止程序的过程调用的地址 修改为func函数的地址。所以当进程接受到sigalrm信号时就会调用func函数。


#include
#include
#include
void func(){printf("GET SIGLRM\n");
}
int main(){int i;alarm(4);alarm(6);signal(SIGALRM, func);for (i&#61;1; i<&#61;8; i&#43;&#43;){printf("%d\n", i);sleep(1);}return 0;
}

在这里插入图片描述

同一个进程只能一次给自己定一个闹钟。
对同一个信号定义好多次&#xff0c;在最新的信号来之前的信号都无效&#xff0c;只有最新的信号有效。

可以修改代码&#xff0c;当第一个alarm信号来后&#xff0c;在设一个alarm

#include
#include
#include
void func(){printf("GET SIGLRM\n");
}
int main(){int i;alarm(4);alarm(6);signal(SIGALRM, func);for (i&#61;1; i<&#61;8; i&#43;&#43;){printf("%d\n", i);if (i&#61;&#61;5)alarm(1);sleep(1);}return 0;
}


注意&#xff1a;在fork之前&#xff0c;调用signal&#xff0c;子进程会继承父进程的信号


#include #include int kill(pid_t pid,int sig)

kill() 可以用来送参数 sig 所指定的信号给参数 pid 所指定的进程&#xff0c;
参 pid 有几 种情况&#xff1a;
pid>0 将信号传送给进程识别码为 pid 的进程&#xff1b;
pid&#61;0 将信号传送给和目前进程相同进程组的所有进程&#xff1b;
pid&#61;-1 将信号像广播般传送给系统内所有的进程&#xff1b;
pid<0 将信号传送给进程组识别码为 pid 绝对值的所有进程。
该函数若执行成功。则返回 0&#xff0c;否则返回-1。


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
阿日小子很顽强_756
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有