Linux从入门到开发实战(C/C++)Day04-进程

点击前往Day05-管道

1.进程的定义:
        运行中的程序
        操作系统资源调度的基本单位
        资源    CPU时间片、文件描述符号 fd、内存、设备、等等...

2.进程的组成:
        数据:全局、静态、只读变量(类比内存四区)
        代码:进程相关的源代码,存在于全局变量区
        堆栈:进程绑定的堆空间和栈空间
        优先级、时钟、进程id 等等...
                linux系统下每个进程在/proc目录下有进程id及其对应的目录存储进程的一些数据

3.进程模式
        用户模式:应用程序运行模式
        内核模式:程序调用系统调用,触发系统中断切到内核模式,内核模式完毕切回用户模式

4.进程状态
        运行时状态、睡眠状态、挂起状态、等待状态、死亡状态、僵尸状态、等等...
        用ps -aue、top、tree等命令可以查看进程状态

5.创建进程
        1)手动运行
                命令、双击执行、system函数(程序中执行命令)
        2)调度进程
                nice、renice     控制进程是否让步
                kill                     结束进程
                crontab             安装进程
        3)fork vfork 创建子进程
                #include<sys/types.h>
                #include<unistd.h>
                pid_t fork(void);
        4)exec簇

6.父子进程
        getpid                        获取当前进程的id
        getppid                      获取当前进程的父进程(parent)id
        创建进程的进程        父进程
        被创建的进程            子进程

        clone 克隆
        子进程复制父进程的代码,会拷贝进程上下文(当前的运行状态,运行到什么时候,到哪行)
        所以子进程也有fork函数,且子进程一开始就会执行fork函数
        父进程执行fork后,父子进程同时运行
        父进程的fork返回子进程id,子进程fork返回0
        所以可以根据fork返回值区分父子进程代码

    7.进程的结束
        1)程序执行完毕自然结束(main函数的return)
        2)程序自己结束自己(exit函数)
        3)父进程结束不会自动结束其子进程
               但是结束终端会结束终端上的子进程
        4)其他进程可以结束进程(kill命令)
               kill -9 [进程id]

    8.僵尸进程
            父进程先于子进程结束,子进程结束的时候它的一些资源未释放
            持续占用系统资源,直到系统重启才会释放
            要注意避免僵尸进程的产生(类比手动内存分配要注意内存泄漏)
            最简单避免防止:父进程 晚于 子进程结束
                    子进程结束前会向父进程发送SIGCHILD信号,父进程可以使用wait函数等待这个信号
                        #include <sys/types.h>
                        #include <sys/wait.h>
                        pid_t wait(int *status);
                        pid_t waitpid(pid_t pid, int *status, int options);
                        int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

    9.进程的组织形式
        进程
        进程组:多个 进程 组成,进程组的组长(session进程)
        会话:多个 进程组 组成

    10.守护进程
        用来 观察、记录的
        特征:
            能持续运行,不能被其他进程轻易干掉
            属于后台进程,不会被看到

        一般用来用作记录日志文档

        守护进程编程模型:
            第一种:
                1)创建新会话                   setsid
                2)改变当前工作目录        chdir
                3)重设当前文件权限        umask
                4)关闭文件                      close
            第二种:
                1)重设当前文件权限        umask
                2)创建子进程                   fork
                3)让父进程结束            
                4)创建新会话                   setsid
                5)忽略一些信号               主要忽略SIGCHLD、SIGHUP
                6)改变当前工作目录        chdir
                7)重定向文件描述符号    dup2    为了屏蔽终端干扰

进程间通信:
    广义:进程间数据交换(传输)
    借助:
        文件
        文件映射虚拟内存(可以在父子进程之间进行通信)
        管道
        信号
    狭义:IPC
    借助:
        共享内存
        消息队列
        旗语(信号量)

父子进程间通信:
        变量:父子进程都拥有同名变量,但是不共用
        堆内存:父子进程都有堆内存,但是不共用
        mmap:父子进程共享同一个mmap映射出来的内存

相关示例

#include <stdio.h>
#include <unistd.h> //linux操作系统标准头文件
#include <string.h> //memcpy
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> //umask
#include <time.h>
#include <sys/time.h>
#include <dirent.h> //遍历目录
#include <fcntl.h>	//文件映射虚拟内存
#include <sys/mman.h>
#include <wait.h>
#include <signal.h> //SIGCHLD
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <bits/pthreadtypes.h>
#include <aio.h>

// exec进程
void _exec(int argc, char *argv[])
{
	printf("exec启动\n");
	execl(argv[1], "启动进程名%s", argv[1]);
}

// fork创建子进程
void _fork()
{
	printf("当前进程id: %u\n", getpid());
	sleep(3);
	// 创建子进程
	int pid = fork();
	printf("被创建子进程id: %u\n", pid);
	if (pid) // 父进程
	{
		while (1)
			printf("我是父进程:%u %u\n", getpid(), getppid());
		sleep(1);
	}
	else
	{
		while (1)
			printf("---我是子进程: %u %u\n", getpid(), getppid());
		sleep(1);
	}
}

// kill结束进程
void _kill(int argc, char *argv[])
{
	int pid = atoi(argv[1]);
	kill(pid, 9);
	printf("给%d进程发送信号9完毕\n", pid);
}

// 父进程等待子进程结束后再继续
void _wait()
{
	printf("当前进程id: %u\n", getpid());
	sleep(3);
	// 创建子进程
	int pid = fork();
	printf("被创建子进程id: %u\n", pid);
	if (pid) // 父进程
	{
		for (int i = 0; i < 3; i++)
		{
			printf("我是父进程:%u %u\n", getpid(), getppid());
			sleep(1);
		}
		printf("父进程的事情干完了,正在等待子进程\n");
		int status = 114514;
		pid_t r = wait(&status);
		printf("子进程结束,父进程继续 %d %d\n", r, status);
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			printf("---我是子进程: %u %u\n", getpid(), getppid());
			sleep(1);
		}
		printf("子进程结束\n");
		return 123;
	}
}

// 守护进程
void _daemon()
{
	// 1)重设当前文件权限		umask
	umask(0);
	// 2)创建子进程			fork
	int ret = fork();
	if (ret < 0)
	{
		printf("创建进程失败%m\n");
		exit(-1);
	}
	// 3)让父进程结束
	if (ret > 0)
	{
		exit(0);
	}
	printf("pid:%d\n", getpid());
	// 4)创建新会话			setsid
	setsid();
	// 5)忽略一些信号			主要忽略SIGCHLD、SIGHUP
	signal(SIGCHLD, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	// 6)改变当前工作目录		chdir
	chdir("/");
	// 7)重定向文件描述符号	dup2	为了屏蔽终端干扰
	//	/dev/null 是黑洞设备,什么东西往这里一丢就没了
	int fd = open("/dev/null", O_RDWR);
	dup2(fd, 0);
	dup2(fd, 1);

	// 模拟守护进程工作
	while (1)
	{
		sleep(1); // 2852
	}
}

int main(int argc, char *argv[])
{
    // _exec(argc,argv);
	// _fork();
	// _kill(argc,argv);
	// _wait();
	// _daemon();
}

猜你喜欢

转载自blog.csdn.net/qq_55149044/article/details/141003761