Linux C 进程间通信(无名管道、有名管道、信号)

无名管道

无名管道原理

1.通过pipe()创建无名管道,返回管道的两端的文件描述符
2.管道两端分别为读端和写端,且读端和写端固定
3.通信模式:单工
4.通信的进程之间必须具有亲缘关系

无名管道的使用

#include <unistd.h>
int pipe(int pipefd[2]);
//参数 ---- 保存管道两端文件描述符的数组
//返回值 ----- 成功:0,失败:-1

例子:
```
int main(int argc,char **argv)
	{
	int fd[2];
	pid_t pid;
	char buf[100];

	//创建无名管道
	if(pipe(fd) < 0){
		perror("pipe");
		exit(1);
	}

	//创建子进程
	if((pid = fork()) < 0){
		perror("fork");
		exit(1);
	}else if(!pid){  //子进程:从键盘获取字符串,写到管道中
		close(fd[0]);
		while(1){
			memset(buf,0,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = '\0';
			write(fd[1],buf,strlen(buf));
		}

	}else{  //父进程: 从管道读出数据,并打印到屏幕上
		close(fd[1]);
		while(1){
			memset(buf,0,sizeof(buf));
			if(read(fd[0],buf,sizeof(buf)) < 0){
				perror("read");
				exit(1);
			}
			printf("%s\n",buf);
		}
	}

	return 0;
}
```

无名管道读写数据注意事项

读端: 
	管道没有写端的进程,从管道对数据时,read()会返回0
	管道有写端的进程:	
			管道中没有数据,read()会阻塞,等待写数据
			管道中有数据,read()读出数据

写端: 
	管道没有读端进程,向管道写数据时,write()写端的进程会立即结束进程
	管道有读端的进程,向管道写数据时,
					如果管道没有满,则write()写数据到管道中,
					如果管道写满了,则write()阻塞

有名(命名)管道

有名管道原理

1.通过mkfifo()创建有名管道
2.有名管道在文件系统中可见,在目录中可以看到管道文件
3.两个进程通信时,只需要通过open()打开管道,就可以对管道进行读写操作
4.工作模式:半双工
5.可以用于没有亲缘关系的进程之间通信

有名管道创建

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//参数1 ----- 管道名称
//参数2 ----- 管道的权限:   权限 = mode & ~umask
//返回值 -----成功:0,失败:-1
例如:

```
int main(int argc,char **argv)
{
	if(argc != 2){
		fprintf(stderr,"Usage: %s <fifoname>\n",argv[0]);
		exit(1);
	}

	if(mkfifo(argv[1],0666) < 0){
		perror("mkfifo");
		exit(1);
	}

	return 0;
}
运行: 
blob@ubuntu:~/$ ./mkfifo fifo
blob@ubuntu:~/$ ls -l
total 36
prw-rw-r-- 1 blob blob     0 Aug 19 20:27 fifo
```

使用有名管道实现两个进程的通信

输入端:
int main(int argc,char **argv)
{
    int fd;
    char buf[100];

    if(argc != 2){
        fprintf(stderr,"Usage: %s<fifoname>\n",argv[0]);
        exit(1);
    }

    if((access(argv[1],F_OK)<0)){
        if(mkfifo(argv[1],0666)<0){
            perror("mkfifo");
            exit(1);
        }
    }

    if((fd=open(argv[1],O_RDWR))<0){
        perror("open");
        exit(1);
    }

    while(1){
        memset(buf,0,sizeof(buf));
        printf("请输入字符串:");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        if(write(fd,buf,strlen(buf))<0){
            perror("write");
            exit(1);
        }
        if(!strncmp(buf,"quit",4))
            break;
    }
    close(fd);
    return 0;

输出端:
int main(int argc,char **argv)
{
    int fd;
    char buf[100];
    int ret;

    if(argc != 2){
        fprintf(stderr,"Usage: %s<fifoname>\n",argv[0]);
        exit(1);
    }

    if((access(argv[1],F_OK)<0)){
        if(mkfifo(argv[1],0666)<0){
            perror("mkfifo");
            exit(1);
        }
    }

    if((fd=open(argv[1],O_RDWR))<0){
        perror("open");
        exit(1);
    }

    while(1){
        memset(buf,0,sizeof(buf));
        if(ret=read(fd,buf,sizeof(buf))<0){
            perror("read");
            exit(1);
        }
        if(!strncmp(buf,"quit",4))
            break;
        printf("%s\n",buf);
    }
    close(fd);
    return 0;
}

信号

概念

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;
如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程 

进程对信号的响应方式有三种

1.忽略信号:
	对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

2.捕捉信号:
	定义信号处理函数,当信号发生时,执行相应的处理函数。
	
3.执行缺省操作:
	Linux对每种信号都规定了默认操作

linux系统对常见信号的缺省(默认)操作

linux系统中信号的种类:
farsight@ubuntu:~/22071/process/day03_code$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

信号名								含义																			默认操作
SIGHUP	该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一						        终止
		会话内的各个作业与控制终端不再关联。	
SIGINT	该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。			        终止
SIGQUIT	该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。														    终止
SIGILL	该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。		            终止
SIGFPE	该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。	        终止
SIGKILL	该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。													    终止
SIGALRM	该信号当一个定时器到时的时候发出。																			    终止
SIGSTOP	该信号用于暂停一个进程,且不能被阻塞、处理或忽略。															    暂停进程
SIGTSTP	该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。										    暂停进程
SIGCHLD	子进程改变状态时,父进程会收到这个信号																		    忽略
SIGABORT	该信号用于结束进程																						终止

信号相关的api函数

给指定进程发送信号

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);  
//参数1 ---- 指定的进程ID 
				pid > 0 , 表示给指定的进程发送信号
				pid = 0 , 表示给与当前进程在同一个进程组中的所有进程发送信号
				pid = -1, 表示给进程表中所有进程发送信号					
//参数2 ---- 要发送的信号
//返回值 ----- 成功:0,失败:-1
例如:
```
int main(int argc,char **argv)
{
	pid_t pid;
	#if 0
		printf("请输入要杀死的进程的PID:");
		scanf("%d",&pid);
		if(kill(pid,SIGKILL) < 0){
			perror("kill");
			exit(1);
		}
	#else
		int i = 0;
		if((pid = fork()) < 0){
			perror("fork");
			exit(1);
		}else if(!pid){
			while(1){
				printf("我是子进程!\n");
				sleep(1);
				i++;
				if(i == 7)
					//kill(0,SIGKILL);
					kill(-1,SIGKILL);
			}
		}else{
			while(1){
				printf("我是父进程!\n");
				sleep(1);
			}
		}

	#endif
	return 0;
}
```

给当前进程发送信号

#include <signal.h>
int raise(int sig);
//参数 ------ 要送的信号
//返回值-----成功:0,失败:-1
例如:

```
	 int main(int argc,char **argv)
	{
		int i = 0;

			while(1){
				printf("我是一只小小鸟!\n");
				sleep(1);
				i++;
				if(i == 7)
					raise(SIGKILL);
			}
		return 0;
	}

```

在进程中设置定时器(闹钟)

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//参数 ----- 要设置秒数,从调用该函数开始计时,进程运行到seconds秒后,内核就会给当前进程发送SIGALRM信号
//返回值 ----- 如果之前有设置过定时器,则返回剩余的秒数,如果没有设置过,则返回0
例如:
```
int main(int argc,char **argv)
{
	alarm(7);
	#if 1
	while(1){
		printf("我是一只小小鸟!\n");
		sleep(1);
	}
	#endif
	return 0;
}
```

使进程暂停,收到任一信号则返回

#include <unistd.h>
int pause(void);
//返回值 -----成功:进程收到的信息,失败:-1
例如:

```
int main(int argc,char **argv)
{
    alarm(7);
    printf("设置定时器,7秒后时间到!\n");
    pause();
    return 0;
}
```

注册信号,设置信号的响应方式

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//参数1 ----- 要注册的信号
//参数2 ----- 对注册的信号的响应方式:
						SIG_IGN:忽略该信号
						SIG_DFL:采用系统默认方式处理信号
						自定义的信号处理函数指针,信号处理函数类型如下: 
						void xxxx(int signo)
						{
							//处理信号的代码
						}
//返回值-----成功:信号处理函数的地址,失败:SIG_ERR

例如: 
```
void fun(int signo)
{
	int i;

	for(i = 0; i < 10; i++){
		printf("我要出去玩!\n");
		sleep(1);
	}
}

int main(int argc,char **argv)
{
	//signal(SIGALRM,SIG_IGN);
	signal(SIGALRM,fun);
	alarm(7);
#if 1
	while(1){
		printf("我要好好学习...!\n");
		sleep(1);
	}
#endif
	return 0;
}
```
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值