linux 进程学习

文章详细介绍了Linux中的进程控制,包括wait函数的使用,用于等待并回收子进程,以及execl函数来执行新的程序。同时,讨论了进程通信的基础,如管道(匿名管道和命名管道FIFO)的创建和使用,以及信号和共享内存的概念。这些是多进程编程中的关键概念。
摘要由CSDN通过智能技术生成

根据B站视频整理

进程控制

1.wait函数

函数原型: pid_t wait(int *status)

  • 函数作用:阻塞并等待子进程退出;回收子进程残留资源;获取子进程结束状态(退出原因)
  • 返回值:成功(清理掉的子进程id),失败(-1,没有子进程)
  • Status参数:子进程的退出状态,是传出参数
    WIFEXITED(status):为非0 进程正常结束;获取进程退出状态
    WIFSIGNALED(status):为非0 进程异常终止;取得进程终止的信号编号
//wait函数 父进程对子进程进行回收
#include <sys/wait.h>    //wait函数必须的头文件
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
	pid_t pid=fork();
	if(pid<0){ perror("fork error"); return -1;}
	else if(pid>0){
		printf("father:pid=%d",getpid());
		pid_t wpid=wait(NULL);
		printf("wpid=%d",wpid);
	}else printf("child:pid=%d",getpid());
	return 0;
}
  • 结果截图
    wait函数运行截图

对于wait函数参数的说明及代码验证,可在linux 通过man [2] wait查询
WIFEXITED(wstatus)正常退出
returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().
WEXITSTATUS(wstatus)
returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should be employed only if WIFEXITED returned true.
WIFSIGNALED(wstatus)信号杀死
returns true if the child process was terminated by a signal.
WTERMSIG(wstatus)
returns the number of the signal that caused the child process to terminate. This macro should be employed only if WIFSIGNALED returned true.

//在父进程更改
int status;
pid_t wpid=wait(&status);
if(WIFEXITED(status)){//正常退出
	printf("child normal exit,status=%d",WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){//被信号杀死
	printf("child killed by signal,signo=%d",WTERMSIG(status));
}
  • 结果截图
    信号杀死子进程
    正常退出

waitpid函数
pid_t waitpid(pid_t pid,int *status,int options);

  • 参数说明:
    pid=-1 等待任意子进程,与wait等效
    pid>0 等待进程id与pid相等的子进程
    pid=0 等待进程组id与目前进程相同的任何子进程,即和调用waitpid()函数的进程在同一个进程组的进程
    pid<-1 等待其组id等于pid的绝对值的任意子进程,适用于子进程在其他组的情况
    status:子进程的退出状态,用法同wait函数
    options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞
  • 函数返回值
    >0:返回回收的子进程id
    -1:无子进程
    =0,且第三个参数为WNOHANG,意味着子进程正在运行
while(1){
	pid_t  wpid=waitpid(-1,&status,WNOHANG);
	if(wpid>0){//有子进程退出
		if(WIFEXITED(status)) printf("child normal exit,status=%d",WEXITSTATUS(status));
		else if(WIFSIGNALED(status)) printf("child killed by signal,signo=%d",WTERMSIG(status));
	}else if(wpid==-1)//没有子进程
	{printf("no child is living,wpid=%d",wpid); break;}
}

总结:
调用一次waitpid或者wait函数,只能回收一个子进程

2.execl函数

函数原型: int execl(const char *path,const char arg,…/(char ) NULL/);

  • 参数:
    path:要执行的程序的路径
    变参arg:要执行的程序需要的参数
    arg:仅是占位
    arg后面的:命令参数
    参数写完之后,加上NULL
  • 返回值:
    若成功,则不返回,不会执行exec函数后面的代码;若失败,会执行execl后面的代码,可用perror打印出错信息

execl函数一般执行自己写的代码

1.c编译成1.在子进程调用时,通过execl(./1,”占位符的随便的名字”,这里没参数,NULL);执行。
出错返回perror(“execl error”);

进程通信

IPC:进程间通信(interprocess communication)
两个进程要想完成数据交换,必须通过内核。
方式有:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。

1.管道

匿名管道pipe

  • 管道的实质是内核缓冲区,内部使用环形队列实现。
  • 管道有读写两端,且是阻塞的,读写两端为两个文件描述符
  • 数据被读走,就不会存在于管道中。
  • 数据流向是单向的
  • 实现有血缘关系的进程间的通信

函数原型: int pipe(int fd[2]);

  • 参数:fd[0]存放管道的读端,fd[1]存放管道的写端。
  • 返回值:成功返回0;失败返回-1,并设置errno值。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
/*
1.父进程创建pipe
2.父进程调用fork函数创建子进程
3.父进程关闭一端,子进程关闭一端
4.父进程分别执行read和write通道
*/

int main(){
	//创建管道
	int fd[2];
	int ret=pipe(fd);
	if(ret<0){
		perror("pipe error");
		return -1;
	}
	pid_t pid=fork();
	if(pid<0){perror("fork error"); return -1;}
	else if(pid>0){
		close(fd[0]);//关闭读端
		write(fd[1],"hello world",strlen("hello world"));
	}else{
		close(fd[1]);
		char buf[64];
		memset(buf,0x00,sizeof(buf));
		int n=read(fd[0],buf,sizeof(buf));//没有数据会阻塞
		printf("read over,n=%d,buf=%s\n",n,buf);
	}
	return 0;
}
  • 代码截图
    pipe1
//实现ps aux|grep bash
else if(pid>0){
	close(fd[0]);
	dup2(fd[1],STDOUT_FILENO);
	execlp("ps","ps","aux",NULL);
}else if(pid==0){
	close(fd[1]);
	dup2(fd[0],STDIN_FILENO);
	execlp("grep","grep","bash",NULL);
}
  • 结果截图
    父进程写,子进程读

管道的读写行为
读:有数据read正常读,返回读出的字节数;无数据,若写端全部关闭,read解除阻塞,返回0;若没有全部关闭,read会阻塞。
写:读端全部关闭,管道破裂,进程终止,内核给当前进程发SIGPIPE信号;读端没有全部关闭,缓冲区没满,继续write,否则write阻塞。

FIFO 命名管道
不相关进程也能交换数据。

FIFO是LInux基础文件类型中的一种(文件类型为p,可通过ls -l查看文件类型)。但FIFO文件在磁盘上没有数据块,文件大小为0,仅仅用来标识内核中的一条通道。使用读写,实际操作的是内核缓冲区。

int mkfifo(const char *pathname,mode_t mode);

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
//进程A
int main(){
	//创建fifo文件
	int ret=mkfifo("./myfifo",0777);
	if(ret<0){perror("mkfifo error");return -1;}
	//打开文件
	int fd=open("./myfifo",O_RDWR);
	if(fd<0){
		perror("open error");
		return -1;
	}
	//写FIFO文件
	write(fd,"hello world",strlen("hello world"));
	//close(fd);这里不能关闭
	return 1;
}
//进程B
int main(){
	int fd=open("./myfifo",O_RDWR);
	if(fd<0){perror("open error");return -1;}
	char buf[64];
	memset(buf,0x00,sizeof(buf));
	int n=read(fd,buf,sizeof(buf));
	printf("n=%d,buf=%s",n,buf);
	close(fd);
	return 0;
}

2.信号(开销小)

3.共享映射区

4.共享内存

可以支持结构体等多种数据类型
读完之后,数据依旧存在
主要函数:

  • shmget 创建/获取共享内存
  • shmat(attach) 关联到当前进程地址空间
  • 通过指针读写共享内存
  • shmdt(detach) 进程分离共享内存
  • shmctl 删除共享内存
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值