Linux多进程

Linux多进程


1.进程的介绍

进程描述是一个程序执行过程。当程序执行后,执行过程开始,则进程产生;执行过程结束,则进程也就结束。进程是一个独立的可调度的活动,由操作系统进行统一调度,相应的任务会被调度到 cpu 中进行执行。进程一旦产生,则需要分配相关资源,同时进程是资源分配的最小单位。
程序是静态的,它是保存在磁盘上的指令的有序集合,没有任何执行的概念。
进程是一个动态的概念,它是程序执行的过程,包括了动态创建、调度和销毁的整个过程。
2.进程的相关命令
(1)ps命令
显示进程状态
ps -aux 显示进程所有信息
在这里插入图片描述
(2)top命令
显示进程实时信息
不显示任何闲置或无用的进程 top -i
更数指定次数后,退出top命令top -n数字
在这里插入图片描述
(3)kill命令
kill 命令是用于结束进程的命令或者用于显示相关信号
在这里插入图片描述3.创建进程
进程的创建需要fork()函数;
创建多任务时子进程由父进程统一创建,统一管理,不能递归创建。

int main()
{
	pid_t pid = fork();
	if(pid == -1)
	{
		perror("fork");
		return -1;
	}
	else if(pid==0)
	{
		printf("child process <%d> is running.....\n",getpid());
		sleep(2);
		printf("child process <%d> ready to exit.\n",getpid());
		exit(EXIT_SUCCESS);
	}
	else
	{
		// 父进程中继续创建子进程
		pid = fork();
		if(pid == -1)
		{
			perror("fork");
			return -1;
		}
		else if(pid == 0)
		{
			printf("child process <%d> is
running.....\n",getpid());
			sleep(4);
			printf("child process <%d> ready to
exit.\n",getpid());
			exit(EXIT_SUCCESS);
		}
		printf("parent process task.\n");
	}
	printf("parent and child task.\n");
	return 0;
}

其中getpid是获取当前进程的ID,getppid是获取当前进程的父进程的ID。
4.进程的等待
在子进程运行结束后,进程进入僵死状态,并释放资源,子进程在内核中的数据结构依然保留。
父进程需要通过wait()或waitpid()函数等待子进程退出,释放子进程遗留资源。
(1)wait()函数
当父进程调用wait函数时,它会阻塞自己的执行,直到它所等待的一个子进程结束为止。在等待子进程结束的同时,父进程会准备运行,直到子进程结束并返回状态码后才继续执行。
如果不关心状态值,子进程退出的状态值为NULL。
(2)waitpid()函数
waitpid函数的功能与wait函数一样,但比wait()函数功能更强大,可以理解成 wait() 底层调用waitpid()函
数。
当参数options为WNOHANG时,可以在不阻塞父进程的条件下轮询检测子进程是否结束。
当参数options为0时此函数就会阻塞父进程,等待子进程结束。
5.进程间的通信
(1)无名管道
1.1 无名管道无名管道属于单向通讯
无名管道只能用于父子进程通讯
无名管道发送端叫做写端,接收端叫做读端
无名管道将读端与写端抽象成两个文件进行操作,在无名管道创建成功之后,则会返回将读端与写端的文件描述符存入数组的特点。
1.2 通过pipe创建无名管道并向管道中写入内容并打印

int main()
{
	pid_t pid;
	int pipefd[2]; // 存放读端与写端描述符的数组
	//创建管道
	int ret = pipe(pipefd);
	if(ret == -1)
	{
		perror("pipe");
		exit(EXIT_FAILURE);
	}
	// 创建子进程
	pid = fork();
	if(pid==-1)
	{
		perror("fork");
		exit(EXIT_FAILURE);
	}
	else if(pid==0)	
	{
		char buff[256]={0};
		close(pipefd[1]); // 关闭子进程写端文件描述符
		// read操作时,当管道中没有数据时,会阻塞
		ssize_t rbytes=read(pipefd[0],buff,sizeof(buff));
		if(rbytes == -1)
		{
			perror("read");
			close(pipefd[0]);
			exit(EXIT_FAILURE);
		}
		printf("content:%s\n",buff);
		close(pipefd[0]);
		exit(EXIT_SUCCESS);
	}
	else
	{
		// 关闭父进程读端文件描述符
		close(pipefd[0]);
		// 向管道中写内容
		char buff[]="Hellp pipe.";
		ssize_t wbytes = write(pipefd[1],buff,strlen(buff));
		if(wbytes == -1)
		{
			perror("write");
			close(pipefd[1]);
			exit(EXIT_FAILURE);
		}
		close(pipefd[1]);
		wait(NULL);
	}
	return 0;
}

当管道为空时,读进程会阻塞。
当管道的写端被关闭,从管道中读取剩余数据后,read 函数返回0。
(2)有名管道
2.1 有名管道是文件系统中可见的文件,但是不占用磁盘空间,仍然在内存中。可以通过 mkfifo命令创建有名管道。
有名管道与无名管道一样,在应用层是基于文件接口进行操作。
有名管道用于任意进程之间的通讯,当管道为空时,读进程会阻塞。
2.2 有名管道的创建
读端

#define PIPE_NAME "/home/linux/pipe"
int main()
{
	int fd = open(PIPE_NAME,O_RDONLY);
	if(fd == -1)
	{
		perror("read");
		exit(EXIT_FAILURE);
	}
	char buf[256]={0};
	ssize_t rbytes = read(fd,buf,sizeof(buf));
	if(rbytes > 0)
	{
		printf("content:%s\n",buf);
	}
	close(fd);
	return 0;
}

写端

#define PIPE_NAME "/home/linux/pipe2"
int main()
{
	//检测管道是否存在不存在则创建管道
	int ret = access(PIPE_NAME,F_OK);
	if(ret == -1)
	{
		mkfifo(PIPE_NAME,0644);
	}
	int fd = open(PIPE_NAME,O_WRONLY);
	if(fd == -1)
	{
		perror("open");
		exit(EXIT_FAILURE);
	}
	char buf[256]={"Hello pipe."};
	ssize_t wbytes = write(fd,buf,sizeof(buf));
	if(wbytes == -1)
	{
		perror("write");
		close(fd);
		exit(EXIT_FAILURE);
	}

	close(fd);
	return 0;
}

2.3 有名管道的优缺点
如果有名管道的一端以只读方式打开,它会阻塞到另一端以写的方式(只写,读写)。
如果有名管道的一端以只写方式打开,它会阻塞到另一端以读的方式(只读,读写)。
6.进程间的通信-信号
1.信号的定义和常见信号
信号是在软件层面上是一种通知机制,对中断机制的一种模拟,是一种异步通信方式。一般具有如下特
点:
进程在运行过程中,随时可能被各种信号打断。
进程可以忽略或者去调用相应的函数去处理信号。
进程无法预测信号到达的精准时间。
常见的信号:
SIGKILL 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。
SIGALRM 该信号当一个定时器到时的时候发出。
SIGSTOP 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。
进程退出
SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,S
IGUSR2,SIGVTALRM
进程忽略
SIGCHLD,SIGPWR,SIGURG,SIGWINCH
进程暂停
SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
2.信号的发送和等待
当由进程来发送信号时,则可以调用 kill() 函数与 raise () 函数,raise是向自己发送信号。
在进程没有结束时,进程在任何时间点都可以接受到信号,由pause()函数阻塞进程等待信号。
3.信号的处理
忽略 : 不进行处理。
默认 : 按照信号的默认方式处理。
用户自定义 : 通过用户实现自定义处理函数来处理,由内核来进行调用。用户自定义信号是通过signal 函数设置信号,用户自处理通常使用以下形式:
typedef void (sighandler_t)(int);
typedef void (
)(int) sighandler_t;
7.进程间的通信-消息队列
System V IPC 对象共有三种: 消息队列、共享内存、信号量
System V IPC 是由内核维护的若干个对象,通过 ipcs 命名查询
在这里插入图片描述
1.创建消息队列
通过msgget()系统调用来完成。msgget()函数返回一个消息队列的标识符,后续的操作都将基于这个标识符进行。创建消息队列时,需要指定一个键值(通常通过ftok()函数生成)和标志位(用于指定创建消息队列时的权限和行为)。
2.发送消息
使用msgsnd()系统调用向队列中发送消息。发送消息时需要指定消息队列的标识符、指向要发送的消息的指针、消息数据部分的大小以及标志位。
3.接收消息
使用msgrcv()系统调用从消息队列中接收消息。接收消息时需要指定消息队列的标识符、指向接收消息的缓冲区的指针、消息缓冲区的大小、消息类型(用于指定接收特定类型的消息)以及标志位。
4.删除消息队列
使用msgctl()系统调用来删除消息队列。
5.消息队列的优缺点
优点:
消息队列提供了一种灵活且高效的进程间通信方式。它允许异步通信,即发送方不必等待接收方检查它所收到的消息就可以继续工作下去,而接收方如果没有收到消息也不需等待。消息队列是随内核持续的,并与进程相关,只有在内核重启或显式删除一个消息队列时,该消息队列才会真正被删除。
缺点:
每个消息的最大长度和每个消息队列的总字节数都有上限。系统上消息队列的总数也有一个上限。
8.进程间的通信-共享内存
共享内存是将分配的物理空间直接映射到进程的用户虚拟地址空间中,减少数据在内核空间缓存,共享内存是一种效率较高的进程间通讯的方式,在 Linux 系统中通过 ipcs -m 查看所有的共享内存。
1.创建共享内存
shmget:用于创建或获取一个共享内存段。其参数包括一个唯一键值(通常通过ftok函数生成)、共享内存的大小(一般为系统页面大小的整数倍)以及一组标志(用于指定创建方式、权限等)。
2.共享内存的映射
shmat:将共享内存段连接到当前进程的地址空间中。其参数包括共享内存标识符、希望连接的进程虚拟地址空间的地址(如果使用NULL,则由系统自动选择一个合适的地址)以及连接选项(如SHM_RND、SHM_RDONLY等)。
3.解除共享内存的映射
shmdt:将共享内存段从当前进程的地址空间中断开连接。其参数为要断开连接的共享内存的地址(由shmat返回)。
4.删除共享内存
shmctl:删除共享内存通常使用shmctl系统调用,并指定IPC_RMID命令。
9.进程间的通信-信号量集合信号量: 由内核维护的整数,其值被限制为大于或等于0
信号量可以执行如下操作:
将信号量设置成一个具体的值。
在信号量当前值的基础上加上一个数值。
在信号量当前值的基础上减上一个数值。
等待信号量的值为 0。
1.创建信号量集合
semget:用于创建或获取一个信号量集合。其原型为int semget(key_t key, int nsems, int semflg);。其中,key是用于标识信号量集合的唯一标识符,nsems是信号量集合中包含的信号量数量,semflg是一些标志参数,用于指定信号量的权限和行为。
2.初始化信号量/删除信号量
semctl:用于控制信号量集合中的信号量。其原型为int semctl(int semid, int semnum, int cmd, …);。其中,semid是信号量集合的标识符,semnum是要操作的信号量的索引,cmd是要执行的操作(如设置信号量的初始值、删除信号量集合cmd=IPC_RMID等),根据cmd的不同而有所不同。
semop:用于对信号量集合中的信号量进行操作,如阻塞信号量和释放信号量等。其原型为int semop(int semid, struct sembuf *sops, size_t nsops);。其中,semid是信号量集合的标识符,sops是一个包含操作信息的结构体数组,nsops是struct sembuf结构体数组的大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值