Linux进程控制

1.pipe管道通信

int pipe (int __pipedes[2]);

pipefd读写文件描述符,0:代表读, 1:代表写

返回值:失败返回-1,成功返回0 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>


int main(int argc, char* argv[])
{
	
	int fd[2];
	pipe(fd);
	pid_t pid = fork();
	if (pid == 0)	//son
	{
		sleep(3);
		char buf[] = "hello word\n";
		write(fd[1], buf, sizeof(buf));
	}
	else if (pid > 0)//father
	{
		char buf2[100] = {0};
		int ret = 0;
		ret = read(fd[0], buf2, sizeof(buf2));// 阻塞
		if (ret > 0)
		{
			write(STDOUT_FILENO, buf2, sizeof(buf2));
		}
	}

	exit(0);
}
lee@lenovo:~/code/unix$ ./app 
hello word

父子进程实现pipe通信,实现ps aux|grep bash功能

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

int main(int argc, char* argv[])
{
	int fd[2];
	int ret = 0;
	pipe(fd);
	pid_t pid = fork();
	if (pid == 0)	//son
	{
		// 关闭pipe读端
		close(fd[0]);
		// 1.重定向
		ret = dup2(fd[1], STDOUT_FILENO); // STDOUT_FILENO 重定向到 fd[1]
		if (-1 == ret)
		{
			perror("dup2");
			exit(1);
		}

		// 2.execlp
		ret = execlp("ps", "ps", "aux", NULL);
		if (-1 == ret)
		{
			perror("execlp");
			exit(1);			
		}
	}
	else if (pid > 0)//father
	{	
		// 关闭pipe写端
		close(fd[1]);
		// 1.重定向 标准输入重定向像到管道读端
		dup2(fd[0], STDIN_FILENO);
		// 2.execlp
		execlp("grep", "grep", "bash", "--color=auto", NULL);

	}

	exit(0);
}

读管道:

        写端全部关闭:read读到0, 相当于读到文件末尾

        写端没有全部关闭:

                有数据:read读到数据

                 没有数据:read阻塞 fcntl函数可以更改非阻塞

写管道:

        读端全部关闭:产生一个信号SIGPIPE,程序异常终止

        读端没有全部关闭:

                管道已满:write阻塞

                管道没有满:write正常写入

int main(int argc, char* argv[])
{
	
	int fd[2];
	pipe(fd);
	pid_t pid = fork();
	if (0 == pid)	//son
	{
		sleep(3);
		close(fd[0]);	// 关闭读端
		char buf[] = "hello word\n";
		write(fd[1], buf, sizeof(buf));
		close(fd[1]);	// 关闭写端
		while (1)
		{
			sleep(1);
		}
	}
	else if (0 < pid)//father
	{
		char buf2[100] = {0};
		int ret = 0;
		close(fd[1]);	// 关闭写端
		while (1)
		{
			ret = read(fd[0], buf2, sizeof(buf2));// 阻塞
			if (0 < ret)
			{
				write(STDOUT_FILENO, buf2, sizeof(buf2));
			}
			else if (0 == ret)
			{
				printf("read over\n");
				break;
			}
		}
	}

	exit(0);
}

写端全部关闭:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>


int main(int argc, char* argv[])
{
	
	int fd[2];
	pipe(fd);
	pid_t pid = fork();
	if (0 == pid)	//son
	{
		sleep(3);
		close(fd[0]);	// 关闭读端
		char buf[] = "hello word\n";
		write(fd[1], buf, sizeof(buf));
		close(fd[1]);	// 关闭写端
		while (1)
		{
			sleep(1);
		}
	}
	else if (0 < pid)//father
	{
		close(fd[0]);	// 关闭读端
		close(fd[1]);	// 关闭写端
		int status = 0;
		wait(&status);
		if (WIFSIGNALED(status))
		{
			printf("killed by %d\n", WTERMSIG(status));
		}
	}

	exit(0);
}

读端全部关闭,写端如果写入会产生SIGPIPE信号,程序异常终止: 

管道大小:8*512byte 

 

int CheckPIPESize(void)
{
	int fd[2];
	pipe(fd);
	long size = 0;
	size = fpathconf(fd[0], _PC_PIPE_BUF);
	printf("fd[0] pipe size: %d\n", size);
	size = fpathconf(fd[1], _PC_PIPE_BUF);
	printf("fd[1] pipe size: %d\n", size);
	return 0;	
}

 

 管道的优劣:

        优点:简单,相比信号、套接字实现进程间通信,简单很多;

        缺点:1.只能单向通信,双向通信需要建立两个管道;

                   2.只能用于父子、兄弟进程(有共同祖先)间通信,该问题后来使用fifo有名管道解决;

兄弟进程通信

int main(int argc, char* argv[])
{
	int fd[2];
	int ret = 0;
	pipe(fd);
	int n = 2;
	pid_t pid = 0;
	size_t i = 0;

	for (i = 0; i < n; i++)
	{
		pid = fork();
		if (0 > pid)
		{
			perror("fork");
			exit(1);
		}
		else if (0 == pid)
		{
			// printf("son i = %d, pid = %d, ppid = %d\n", i, getpid(), getppid());
			break;
		}
		// else if (0 < pid)
		// {
		// 	printf("father i = %d, pid = %d, ppid = %d\n", i, getpid(), getppid());
		// }
	}

	if (2 == i)
	{
		printf("[父] father pid: %d, son pid: %d\n", getppid(), getpid());
		// 关闭pipe读端
		close(fd[0]);	
		// 关闭pipe写端
		close(fd[1]);
		wait(NULL);		
	}
	else if (0 == i)
	{
		sleep(3);
		printf("[兄弟发送] father pid: %d, son pid: %d\n", getppid(), getpid());
		// 关闭pipe读端
		close(fd[0]);
		// 1.重定向
		ret = dup2(fd[1], STDOUT_FILENO); // STDOUT_FILENO 重定向到 fd[1]
		if (-1 == ret)
		{
			perror("dup2");
			exit(1);
		}

		// 2.execlp
		ret = execlp("ps", "ps", "aux", NULL);
		if (-1 == ret)
		{
			perror("execlp");
			exit(1);			
		}		
	}
	else if (1 == i)
	{
		printf("[兄弟接收] father pid: %d, son pid: %d\n", getppid(), getpid());
		// 关闭pipe写端
		close(fd[1]);
		// 1.重定向 标准输入重定向像到管道读端
		dup2(fd[0], STDIN_FILENO);
		// 2.execlp
		execlp("grep", "grep", "bash", "--color=auto", NULL);
	}

	exit(0);
}

2.FIFO通信 

FIFO有名管道,实现无血缘关系进程通信;

        创建一个管道的伪文件

                mkfifo FIFO 命令创建

                也可以用函数 int mkfifo(const char* pathname, mode_t mode);

        内核会针对fifo文件开辟一个缓冲区,操作fifo文件,可以操作缓冲区,实现进程间通信---实际上就是文件读写

open注意事项:打开fifo文件的时候,read端会阻塞等待write端open,write端同理,也会阻塞等待另外一端打开;

man 7 fifo 查看详细信息

mkfifo FIFO

3.mmap共享映射区 

 

 

void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

        addr: 传NULL

        length: 映射区的长度

        prot:

               PROT_EXEC  Pages may be executed.  可执行

               PROT_READ  Pages may be read. 可读

               PROT_WRITE Pages may be written. 可写

               PROT_NONE  Pages may not be accessed. 不可访问

        flags:

                MAP_SHARED: 共享的,对内存的修改会影响到源文件

                MAP_PRIVATE: 私有的

        fd:文件描述符,open打开一个文件

        offset: 偏移量

        返回值:

                成功返回 可用的内存首地址

                失败返回 MAP_FAILED

释放映射区:

int munmap(void *addr, size_t length);

        addr 传mmap地址

        length: mmap创建的长度

        返回值:成功返回0 失败返回-1

int main(int argc, char* argv[])
{

	int fd = open("./file", O_RDWR);

	// 创建映射区
	char *mem = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (mem == MAP_FAILED)
	{
		perror("mmap");
		exit(1);
	}

	// 拷贝数据
	strncpy(mem, "hello\n", 6);

	// 释放mmap
	munmap(mem, 8);
	close(fd);
	exit(0);
}

 

 

int main(int argc, char* argv[])
{

	int fd = open("./file", O_RDWR);

	// 创建映射区
	// char *mem = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	char *mem = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
	if (mem == MAP_FAILED)
	{
		perror("mmap");
		exit(1);
	}

	// 拷贝数据
	strncpy(mem, "world\n", 6);

	// 释放mmap
	munmap(mem, 8);
	close(fd);
	exit(0);
}

 关于mmap的9个问题:

1.如果更改mem变量的地址,释放的时候munmap,传入mem还能成功吗?

        不能

2.如果对mem越界操作会怎么样?

        文件的大小对映射区操作区有影响,尽量避免

3.如果文件偏移量随便填一个数会怎么样?

        offset必须是4k的整数倍

4.如果文件描述符先关闭,对mmap映射有没有影响?

        没有影响

5.open的时候,可以新创建一个文件来创建映射区吗

        不可以用大小为0的文件

6.open文件选择O_WRONLY,可以吗?

        不可以,没有权限(隐含一次读操作)

7.当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot可以选择PROT_READ|PROT_WRITE吗?

        不可以,SHARED的时候,映射区的权限<=open文件的权限

8.mmap什么情况下会报错?

        很多情况

9.如果不判断返回值会怎么样?

        必须判断

匿名映射:

MAP_ANON, ANONYMOUS这两个宏在有些unix系统没有

/dev/zero 聚宝盆,可以随意映射

/dev/null 无底洞,一般错误信息重定向到这个文件中

用mmap支持无血缘关系进程通信

如果进程要通信,flags必须设为MAP_SHARED

 4.信号

 信号的特点:简单,不能带戴昂信息,满足特定条件发生

信号的机制:进程B发送给进程A,内核产生信,内核处理

信号的产生:

        按键产生 ctrl+c ctrl+z ctrl+\

        调用函数 kill raise abort

        定时器 alarm, setitimer

        命令产生kill

        硬件异常 段错误,浮点型错误,总线错误,SIGPIPE

信号的状态:

        产生

        递达 信号到达并且处理完

        未决  信号被阻塞了

信号的默认处理方式:

        忽略

        执行默认动作

        捕获

信号的4要素:

        编号

        事件

        名称

        默认处理动作

                忽略

                终止

                终止+core

                暂停

                继续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值