Linux 进程

一、进程线程基础概念

1.程序:可执行文件,可执行指令的集合,是一个文件
2.进程:动态的,运行在内存中,程序执行有一次的过程,伴随着资源的分配和释放;
在这里插入图片描述
3.程序的构成

size a.out // size <程序名>
test  //代码段
rodate //只读数据段
data  //数据段
bss   //未初始化的段

4.进程的构成

test  //代码段
rodate //只读数据段
data  //数据段
bss   //未初始化的段
堆、栈、系统相关的信息(命令行参数、环境变量)

5.进程的表示
task_struct 的结构体
6.进程分类:
(1)交互进程;//输入进程
(2)批处理进程//windows 中.bat
(3)守护进程//开机启动,系统关闭而关闭
7.相关命令

pstree //以树型查看当前进程的信息
ps -ef //进程快照,查看当前进程
ps -ef |grep "a.out"  //查找"a.out"相关进程

ps -aux |grep "a.out" //查看当前进程号

kill -l // 查看控制进程的信号
//常用宏
SIGKILL      9    杀死进程
SIGCHLD     17    子进程结束时,给父进程发送信号
SIGCONT   	18	让信号继续运行
SIGSTOP	    19    让信号停止

kill -9  11962

在这里插入图片描述
//示例:把一个进程放到后台运行 或者放到前台运行

./a.out   //默认前台运行
./a.out & //后台运行

//前台运行一个进程
./a.out 
//查看进程号
ps -aux |grep a.out
//停止进程
kill -19  <pid>
//jobs命令查看job号
jobs

//bg <job号>
bg 1 //把进程放到后台运行
fg 1 //把进程放到前台运行

二、创建进程

1.pid_t fork(void);//成功创建,返回0 ,失败返回-1;

//fork()函数创建成功子进程,在子进程中,pid=0,在父进程中pid为子进程的进程号。
(1)创建子进程,子进程把父进程堆区、栈区、可数据区、可读区、系统相关信息复制了一份,并且父子进程互不影响。
(2)fork之后,子进程会继承父进程打开的文件描述符集合,共享文件状态标志位和问价偏移量。

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

#include<sys/types.h>

int main(int argc,const char * argv[])
{
	pid_t pid;
	fork();//创建子进程;
	printf("welcom to 22091\n");
	while(1);
	return 0;
}

//示例:创建进程,显示子进程、父进程pid、父进程ppid
//在父子进程输出printf()函数末尾必须加换行,或者flush(),刷新缓存,不然不能输出。

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

int main(int argc,const char *argv[])
{
	pid_t pid;
	pid=fork();
	if(pid<0)
	{
		perror("Fail to fork");
		return -1;
	}
	else if(pid==0)
	{
		printf("创建子进程成功,子进程pid:%d\n",getpid());
		printf("父进程pid :%d\n",getppid());
	}
	else
	{
		printf("父进程pid:%d\n",getpid());
		printf("父进程的父进程pid:%d\n",getppid());
		
	}
	while(1);//创建阻塞
	return 0;
}

2.僵尸子进程:子进程结束的时候,父进程没有进行收尸操作(父进程还存在),此时占用资源;

孤儿子进程:父进程结束了,子进程会编程孤儿子进程。


3.常用退出进程的函数

(1)return 结束一个函数的执行,程序不一定结束;
(2)void exit(int status)[库函数] ,需要添加 #include<stdlib.h>//结束一个进程,结束之前会刷新缓冲区;status :进程状态的标志,0表示正常结束,其他表示异常结束。

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

(3)void _exit(int status)[系统调用],需添加 #include<unistd.h> 与exit不同是不刷新缓冲区;

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

void func1()
{
	printf("func1");
	
}
void func2()
{
	printf("func2");
	exit(0);//刷新缓冲区,可以在终端输出内容
	//_exit(0);//当使用该函数时,系统调用,没有缓冲区,故不能在终端输出内容;
}
int main(int argc, const char* argv[])
{
	func1();
	func2();
	return 0;
}

4.回收僵尸态子进程

pid_t wait();//返回值:成功返回僵尸子进程的pid,失败返回-1(没有子进程);当没有子进程时阻塞(可中断等待态s)

//示例:创建僵尸子进程

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>//exit()函数头文件
int main(int argc, const char *argv[])
{
	pid_t pid;

	pid=fork();//创建一个子进程
	if(pid<0)
	{
		perror("Fail to fork");
		return -1;
	}
	else if(pid==0)
	{
		sleep(3);
		printf("child exit!\n");
		exit(EXIT_SUCCESS);
	}
	else
	{
	
	}

	while(1);
	return 0;
}

// 子进程中断的原因
① exit()或_exit();//判断是exit()还是)_exit()使用宏函数 WIFEXITED(status),返回值为ture 则为exit(number),其中number =WEXITSTATUS(status);

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

#include<sys/types.h>
#include<sys/wait.h>

int main(int argc, const char *argv[])
{
	pid_t pid ,pid2;
	int status;

	pid=fork();//创建一个子进程
	if(pid<0)
	{
		perror("Fail to fork");
		return -1;
	}
	else if(pid==0)
	{
		sleep(3);
		printf("child exit!\n");
		exit(100);
		//return 0;
	}
	else
	{
		pid2=wait(&status);	//父进程回收子进程,在子进程未结束时,父进程处于可中断等待态S
		if(pid2<0)
		{
			perror("Fail to wait");
			return -1;
		}

		if(WIFEXITED(status))//判断是exit() ,还是_exit(data)结束的子进程
		{
			printf("true is exit! exit(%d)\n",WEXITSTATUS(status));//子进程结束时的状态值为 data
		}
		printf("child exit pid =%d\n",pid2);//wait()返回值为子进程的pid号
	
	}

	while(1);
	return 0;
}

② return // return 和exit()一样
③信号 //信号结束子进程,WIFSIGNALED(status)判断是否是由信号结束的(status是从wait(&status)获得的),是由信号结束的子进程,用WTERMSIG(status) 的返回值知道结束子进程的信号;

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

#include<sys/types.h>
#include<sys/wait.h>

int main(int argc, const char *argv[])
{
	pid_t pid ,pid2;
	int status;

	pid=fork();//创建一个子进程
	if(pid<0)
	{
		perror("Fail to fork");
		return -1;
	}
	else if(pid==0)
	{
		sleep(100);
		printf("child exit!\n");
		exit(100);
	}
	else
	{
		pid2=wait(&status);	
		if(pid2<0)
		{
			perror("Fail to wait");
			return -1;
		}

		if(WIFEXITED(status))
		{
			printf("true is exit! exit(%d)\n",WEXITSTATUS(status));
		}
		printf("child exit pid =%d\n",pid2);

		if(WIFSIGNALED(status))//判断子进程是否是由信号中断,返回值为ture 则是
		{
			printf("子进程由信号中断,中断号:%d\n",WTERMSIG(status));//获得中断子进程的信号
		}
	
	}

	while(1);
	return 0;
}

5.练习:打开一个文件,父进程(键盘输入)向文件写入一段字符串,子进程读出,在屏幕显示。

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

int main(int argc, const char *argv[])
{
	pid_t pid;

	int fd;
	ssize_t size1,size2;
	
	char buf[1024]={0};

	if(argc!=2)
	{
		fprintf(stderr,"Usage: %s log.txt",argv[0]);
		return -1;
	}
	fd=open(argv[1],O_RDWR  | O_CREAT | O_TRUNC,0666);
	if(fd<0)
	{
		perror("Fail to fopen");
		return -1;
	}
	pid =fork();
	if(pid<0)
	{
		perror("Fail to fork()");
		return -1;
	}
	else if(pid ==0)
	{
		int flag=0;
		while(1)
		{
			usleep(300);
			memset(buf,0,sizeof(buf));
			ssize_t si=read(fd,buf,sizeof(buf));
			if(si>0)
			{
				if(strncmp(buf,"quit",4)==0)
				{
					break;
				}
				printf("c:%s",buf);	
			}			
		}
	}
	else
	{
		char buf2[1024]={0};
		while(1)
		{
			memset(buf,0,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);	
			fflush(stdin);	
			lseek(fd,0,SEEK_SET);
			size2=write(fd,buf,sizeof(buf));//把buf中的字符写入fd
			lseek(fd,0,SEEK_SET);
			if(strncmp(buf,"quit",4)==0)
			{
				wait(NULL);
				close(fd);
				break;
			}
		}
	}
	return 0;
}

6.pid_t waitpid(pid_t pid ,int *status,int options);//可设置非阻塞方式回收子进程

//功能:回收子进程
//返回值:
//参数:
//pid  -1  所有子进程,
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/wait.h>

int main(int argc, const char *argv[])
{
	pid_t pid ,pid2;
	int status;

	pid=fork();//创建一个子进程
	if(pid<0)
	{
		perror("Fail to fork");
		return -1;
	}
	else if(pid==0)
	{
		sleep(100);
		printf("child exit!\n");
		exit(100);
		//return 0;
	}
	else
	{
		while(1)
		{
			pid2=waitpid(-1,&status,WNOHANG);//0 以阻塞的方式调用	
			if(pid2<0)
			{
				perror("Fail to wait");
				return -1;
			}
			else if(pid2==0)
			{
				printf("pid2 =%d\n",pid2);
				continue;
			}
			else
			{
				if(WIFEXITED(status))
				{
					printf("true is exit! exit(%d)\n",WEXITSTATUS(status));
				}
				printf("child exit pid =%d\n",pid2);

				if(WIFSIGNALED(status))
				{
					printf("子进程由信号中断,中断号:%d\n",WTERMSIG(status));

				}
			}
		}
	
	}
	//while(1);
	return 0;
}

7.守护进程

(1)前提:Linux 系统中,从终端运行的进程,当终端结束时,进程也结束。
(2)查看守护进程

ps -axj  //其中TPGID为-1的为守护进程

(3)创建守护进程
//创建守护进程过程:
①创建子进程fork(),父进程退出"exit(EXIT_SUCCESS);
②在子进程中创建新的会话(脱离控制终端)setsid();
③改变进程的工作目录到|“/” :chdir(“/”);
④重设文件掩码 :umask(0);
⑤关掉不需要的文件描述符号:
close(0);//标准输入
close(1);//标准输出
close(2);//标准出错
//示例:守护进程每隔1秒在文件write_file 中打印:I am fine thank you!\n
紧急关闭 :killall -9 a.out
//多文件编程
head.h

#ifndef _HEAD_H_
#define _HEAD_H_

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
extern void demon_func();
#endif

//demo.c

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


void demon_func()
{
	pid_t pid;

	pid=fork();
	if(pid<0)
	{
		perror("Fail to fork");
		return ;
	}
	else if(pid>0)
	{
		exit(EXIT_SUCCESS);
	}
	else
	{
		setsid();
		chdir("/");
		umask(0);

		close(0);
		close(1);
		close(2);//关闭标准输入\输出\标准出错

	}
}

//main.c

//创建守护进程

#include "head.h"

int main(int argc, const char *argv[])
{
	int fd;
	char buf[]={"I am fine, thank you!\n"};

	fd=open("write_file",O_RDWR  | O_CREAT | O_TRUNC,0666);	
	if(fd<0)
	{		
		perror("Fail to open");
		return -1;
	}
		

	demon_func();
	while(1)
	{
		sleep(2);
		write(fd,buf,strlen(buf));

	}
	close(fd);
	return 0;
}

Linux 进程线程目录及链接

1.Linux 进程
2.Linux 线程
3.Linux进程间通信之无名管道通信
4.Linux进程间通信之有名管道通信
5.Linux 进程间通信—信号
6.进程间通信之IPC对象


版权声明:本文为jun8086原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。