Linux系统编程:进程

Linux进程基本概念

  • 进程是Linux任务的执行单元,也是Linux系统资源的分配单元
  • 每个Linux应用程序运行后由一个或多个进程构成
  • 每个Linux进程可以执行一个或多个程序
  • Linux进程有多种不同状态(即:Linux进程有不同的活法)
  • ps显示当前目录下的进程
    • ps ax显示全部进程
  • 每个进程都有一个唯一的标识(PID)
  • 每个进程都是由另一个进程创建而来(即:父进程)
    • pid_t getpid(void)获取当前进程PID
    • pid_t getppid(void)获取当前进程的父进程PID

进程的种类

  • 进程的种类一共分为三种
    • 交互进程:它是由shell脚本控制,用于控制计算机跟用户进行交互功能功能
    • 批处理进程:本质上维护了一个队列,被放入队列中的进程会被统一处理
    • 守护进程:脱离终端而独立存在,随着系统的启动而运行,随着系统的关闭而结束,例如:服务进程

特殊进程(0、1、2)

  • 0号进程(idel)
    • 它是由linux操作系统启动时运行的第一个进程,该进程也称为空闲进程,操作系统中当其他进程都不运行时会运行0号进程
  • 1号进程(Init进程)
    • 1号进程在0号进程中通过kernel_thread创建出来的第一个进程,该进程是完成对一些硬件的初始化工作,是其他进程的父进程,当某个进程的父进程死亡后,1号进程会收养该进程
  • 2号进程(kthread)
    • 称为调度进程,该进程也是由0号进程产生,主要工作就是完成任务的调度:某个进程的时间片到了,则通过2号进程完成时间片分配并执行该进程

Linux进程生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34QkS2XM-1692431722537)(C:\Users\无实的谎言花\Desktop\上海\Linux系统编程\图片\进程间状态切换.png)]

  • 就绪/运行 状态(R):TASK_RUNNING
  • 阻塞状态
    • 可中断(S):TASK_INTERRUPTIBLE
    • 不可中断(D):TASK_UNINTERRUPTIBLE
  • 停止状态(T):TASK_STOPPED
  • 退出状态:TASK_DEAD
    • 僵尸(Z):EXIT_ZOMBIE
    • 死亡(X):EXIT_DEAD

进程相关指令

ps -ef查看当前进程的关系

ps -ajx查看当前进程状态

ps -aux可以显示进程占CPU的百分比

top动态查看进程的运行情况

htop带颜色动态查看进程运行状态

bg 作业号将停止的进程进入后台运行,作业号jobs -l查看停止进程作业号

fg 作业号将后台运行程序设置前台运行

kill指令

 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
前30个是稳定的信号,下面是不稳定的信号
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	
 
常用的信号:
9) SIGKILL:杀死进程
2) SIGINT:中断进程,就是ctrl+c
19) SIGSTOP:暂停进程,就是ctrl+z
18) SIGCONT:继续执行程序或让进程进入后台运行

Linux的进程树

  • pstree查看进程树

  • 整个Linux系统的所有进程构成一个树状结构

  • 树根由内核自动创建,即:IDLE(pid : 0)

  • 系统中的第一个进程是init / systemd (pid : 1)

    • 0号进程创建1号进程,1号进程负责完成内核部分初始化工作
    • 1号进程加载执行初始化程序,演变为用户态1号进程

Linux进程创建

  • pid_t fork(void) 不是函数,本质为系统调用
    • 通过当前进程创建新进程,当前进程为父进程,新进程为子进程
  • int execve(const char *pathname, char *const argv[], char *const envp[]); 本质也是系统调用
    • 在当前进程中执行pathname指定的程序代码
  • 先创建进程,才能执行程序代码

fork()的工作方式

  • 为子进程申请内存空间,并将父进程数据完全复制到子进程空间中
  • 两个进程中的程序执行位置完全一致**(fork()函数调用位置)**
  • 不同之处:通过fork()返回值判断父子进程,执行不同代码
    • 父进程:fork()返回子进程pid
    • 子进程:fork()返回 0
//fork()的工作方式体现
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <unistd.h>

int g_global = 0;

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

	printf("Hello World!\n");
	printf("current = %d\n", getpid());

	if((pid = fork()) > 0)
	{
		g_global = 1;
		usleep(100);
		printf("%d g_global = %d\n", getpid(), g_global);
	}

	else
	{
		g_global = 10;
		printf("parent = %d\n", getppid());
		printf("%d g_global = %d\n", getpid(), g_global);
	}
	return 0;
}

/*输出结果:
Hello World!
current = 2703
parent = 2703
2704 g_global = 10
2703 g_global = 1
*/

execve()的工作方式

  • 根据参数路径加载可执行程序
  • 通过可执行程序信息构建进程数据,并写入当前进程空间
  • 将程序执行位置重置到入口地址处(即:main())
  • execve(…)将重置当前进程空间(代码&数据)而不会创建新进程
#include <unistd.h>
int execve(const char *pathname, char *const argv[], char *const envp[]);

//参数1	应用程序路径
//参数2	启动参数(进程参数)
//参数3	环境变量
  • main函数(默认进程入口)
    • int main(int argc, char* argv[])
      • argc-命令行参数个数
      • argv-命令行参数数组
//parent.c
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <unistd.h>

#define EXE "child.out"

int create_process(char *path, char *args[])
{
    int ret = fork();
    if(ret == 0)
    {
        execve(path, args, NULL);
    }
    return ret;
}

void zero_str(char *s)
{
    while(s && *s)
        *s++ = 0;
}

int main(int argc, const char *argv[])
{
    char path[] = EXE;
    char arg1[] = "hello";
    char arg2[] = "world";
    char *args[] = {path, arg1, arg2, NULL};
    
    printf("%d begin\n", getpid());
    printf("%d child = %d\n", getpid(), create_process(EXE, args));
    
    zero_str(path);
    zero_str(arg1);
    zero_str(arg2);
    
    printf("%d path = %s\n", getpid(), path);
    printf("%d arg1 = %s\n", getpid(), arg1);
    printf("%d arg2 = %s\n", getpid(), arg2);
    
    printf("%d end\n", getpid());
    
    return 0;
}
//child.c
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    int i = 0;
    sleep(3);
    
    for(i = 0; i < argc; i++)
    {
        printf("exec = %d, %s\n", getpid(), argv[i]);
    }
    return 0;
}
//运行结果
ubuntu@ubuntu:sysio$ ./parent.out 
2924 begin
2924 child = 2925
2924 path = 
2924 arg1 = 
2924 arg2 = 
2924 end
ubuntu@ubuntu:sysio$ exec = 2925, child.out
exec = 2925, hello
exec = 2925, world

进程在内存中的分配

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

static int g_init = 255;
static float g_uninit;

static void text()
{
    
}

int main(int argc, const char *argv[])
{
    static double s_init = 0.255;
    static double s_uninit;
    int i = 0;
    int* p = malloc(4);
    
    printf("argv[0] = %p\n", argv[0]);
    printf("&i = %p\n", &i);
    printf("p = %p\n", p);
    printf("&g_uninit = %p\n", &g_uninit);
    printf("&s_uninit = %p\n", &s_uninit);
    printf("&g_init = %p\n", &g_init);
    printf("&s_init = %p\n", &s_init);
    printf("text = %p\n", text);
    
    free(p);
    
    return 0;
}

/*输出结果
argv[0] = 0x7fff3ff00192	进程入口
&i = 0x7fff3feffb6c			栈区
p = 0x558eb65b9260			堆区
&g_uninit = 0x558eb54f2028	.bass区
&s_uninit = 0x558eb54f2030	.bass区
&g_init = 0x558eb54f2010	.data区
&s_init = 0x558eb54f2018	.data区
text = 0x558eb52f173a		代码段区
*/

进程拷贝文件,父进程拷贝前一半,子进程拷贝后一半

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>

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

	//创建一个读文件描述符
	int fd_r = open(argv[1], O_RDONLY);
	if(fd_r < 0)
	{
		perror("open");
		return -1;
	}

	//创建一个写文件描述符
	int fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664);
	if(fd_w < 0)
	{
		perror("open");
		return -1;
	}

	off_t file_size = lseek(fd_r, 0, SEEK_END);
	printf("文件的大小是%ld字节\n", file_size);

	pid_t cpid = fork();
	if(cpid > 0)
	{
		wait(NULL);

		//父进程拷贝前半部分
		lseek(fd_r, 0, SEEK_SET);
		lseek(fd_w, 0, SEEK_SET);

		char buf;
		for(int i = 0; i < file_size/2; i++)
		{
			if(read(fd_r, &buf, 1) < 0)
			{
				perror("read");
				return -1;
			}

			if(write(fd_w, &buf, 1) < 0)
			{
				perror("write");
				return -1;
			}
		}

	}
	else if(cpid == 0)
	{
		lseek(fd_r, file_size/2, SEEK_SET);
		lseek(fd_w, file_size/2, SEEK_SET);

		char buf;
		for(int i = file_size/2; i < file_size; i++)
		{
			if(read(fd_r, &buf, 1) < 0)
			{
				perror("read");
				return -1;
			}

			if(write(fd_w, &buf, 1) < 0)
			{
				perror("write");
				return -1;
			}
		}
	}
	else
	{
		perror("fork");
		return -1;
	}

	close(fd_r);
	close(fd_w);

    return 0;
}


进程拷贝文件,父进程拷贝三分之一,剩下的兄弟进程各自拷贝三分之一

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>

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

	//创建一个读文件描述符
	int fd_r = open(argv[1], O_RDONLY);
	if(fd_r < 0)
	{
		perror("open");
		return -1;
	}

	//创建一个写文件描述符
	int fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664);
	if(fd_w < 0)
	{
		perror("open");
		return -1;
	}

	off_t file_size = lseek(fd_r, 0, SEEK_END);
	printf("文件的大小是%ld字节\n", file_size);

	pid_t cpid = fork();
	if(cpid > 0)
	{
		wait(NULL);
		pid_t c2pid = fork();
		if(c2pid > 0)
		{
			wait(NULL);
		//	lseek(fd_r, -(file_size/3), SEEK_END);
		//	lseek(fd_w, -(file_size/3), SEEK_END);

			char buf;
			for(int i = file_size - file_size/3; i < file_size; i++)
			{
				if(read(fd_r, &buf, 1) < 0)
				{
					perror("read");
					return -1;
				}
				if(write(fd_w, &buf, 1) < 0)
				{
					perror("write");
					return -1;
				}
			}


		}
		else if(c2pid == 0)
		{
		//	lseek(fd_r, file_size/3, SEEK_SET);
		//	lseek(fd_w, file_size/3, SEEK_SET);

			char buf;
			for(int i = file_size/3; i < file_size - file_size/3; i++)
			{
				if(read(fd_r, &buf, 1) < 0)
				{
					perror("read");
					return -1;
				}
				if(write(fd_w, &buf, 1) < 0)
				{
					perror("read");
					return -1;
				}
			}
			
		}
		else
		{
			perror("fork");
			return -1;
		}

	}
	else if(cpid == 0)
	{
		lseek(fd_r, 0, SEEK_SET);
		lseek(fd_w, 0, SEEK_SET);

		char buf;
		for(int i = 0; i < file_size/3; i++)
		{
			if(read(fd_r, &buf, 1) < 0)
			{
				perror("read");
				return -1;
			}

			if(write(fd_w, &buf, 1) < 0)
			{
				perror("write");
				return -1;
			}
		}
	}
	else
	{
		perror("fork");
		return -1;
	}

	close(fd_r);
	close(fd_w);

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值