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

 #include<unistd.h> 
 int pipe(int filedes[2]) 

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


 #include<sys/file.h> 
 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<stdio.h> 
void perror(const char *s) 

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


#include<unistd.h> 
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<unistd.h> 
 ssize_t write (int fd,const void * buf,size_t count)

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


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

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

#include <stdio.h>
#include <unistd.h>
#include <string.h>

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

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

#include <stdio.h>
#include <unistd.h>
int main(){
	pid_t p1, p2;
	int fda[2], fdb[2];
	char ch;
	pipe(fda);
	pipe(fdb);
	p1=fork();
	if (p1){
		//parent
		p2=fork();
		if (p2){
			//parent
			do{
				read(fdb[0], &ch, sizeof(char));
				putchar(ch);
			}while (ch!='\n');
		}else{
			//child2
			do{
				read(fda[0], &ch, sizeof(char));
				if (ch>='a' && ch<='z')
					ch=ch-'a'+'A';
				else if (ch>='A' && ch<='Z')
					ch=ch-'A'+'a';
				write(fdb[1], &ch, sizeof(char));
			}while (ch!='\n');
		}
	}else{
		//child1
		do{
			ch=getchar();
			write(fda[1], &ch, sizeof(char));
		}while (ch!='\n');
	}

	return 0;
}

#include<signal.h> 
void (*signal(int signum,void (*handler)(int)))(int) 

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

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


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

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


#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(){
	int i;
	alarm(4);
	for (i=1; i<=8; i++){
		printf("%d\n", i);
		sleep(1);
	}
	return 0;
}

在这里插入图片描述


#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void func(){
	printf("GET SIGLRM\n");
}
int main(){
	int i;
	alarm(4);
	signal(SIGALRM, func);
	for (i=1; i<=8; i++){
		printf("%d\n", i);
		sleep(1);
	}
	return 0;
}

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


#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void func(){
	printf("GET SIGLRM\n");
}
int main(){
	int i;
	alarm(4);
	alarm(6);
	signal(SIGALRM, func);
	for (i=1; i<=8; i++){
		printf("%d\n", i);
		sleep(1);
	}
	return 0;
}

在这里插入图片描述

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

可以修改代码,当第一个alarm信号来后,在设一个alarm

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void func(){
	printf("GET SIGLRM\n");
}
int main(){
	int i;
	alarm(4);
	alarm(6);
	signal(SIGALRM, func);
	for (i=1; i<=8; i++){
		printf("%d\n", i);
		if (i==5)
			alarm(1);
		sleep(1);
	}
	return 0;
}

注意:在fork之前,调用signal,子进程会继承父进程的信号


 #include<sys/types.h> 
 #include<signal.h> 
 int kill(pid_t pid,int sig) 

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H4ppyD0g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值