首页 > 系统相关 >进程线程(2)

进程线程(2)

时间:2024-08-13 19:52:27浏览次数:17  
标签:const 函数 int char 线程 进程 NULL

父子进程的关系

  • 子进程是父进程的副本。
  • 子进程获得父进程数据段,堆,栈,正文段共享。

        在fork之后,一般情况哪个会先运行,是不确定的。如果非要确定那个要先运行,需要IPC机制。  

    区别:

  1. fork的返回值
  2. pid不同

进程的创建

  1. 创建之后,父子进程各自拥有4g独立的内存空间 
  2. 各自拥有自己的相关的程序的各个段数据段,所以,各自之间对数据的改变,不会相互影响。 
  3. 子进程会继承父进程已打开的文件描述符,若fork之前打开文件,父子进程操作同一个文件,相互间有影响。fork之后打开文件,父子进程操作同一个文件,但因为各自拥有自己的 "文件表项",所以,各自按照自己的逻辑改变文件。

进程的执行

exec函数族

        用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),
子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的
用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建
新进程,所以调用exec前后该进程的id并未改变。有六种以exec开头的函数,统称exec函数:

        exec函数族用于执行一个文件,用新进程的镜像替换调用进程的镜像。主要包括以下几种函数:

1)int execl (const char *path, const char *arg, ... /* (char  *) NULL */);
2)int execlp (const char *file, const char *arg, ... /* (char  *) NULL */);

3)int execle (const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
4)int execv (const char *path, char *const argv[]);
5)int execvp (const char *file, char *const argv[]);
6)int execvpe (const char *file, char *const argv[], char *const envp[]);

区别:

(1)前4个使用路径名作为参数,后面两个使用文件名做参数。当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。
(2)相关的参数表传递

  •     l表示list,v表示vector
  •     execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。
  •     execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。

(3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。其他未指定环境变量,使用父进程继承过来的。execve 是真正的系统调用
        这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错
则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

(1)execl 函数和 execv 函数

          参数传递方式:

                 1)带 l的函数(如execl)参数逐个列举,以NULL结尾。

                  2)带 v的函数(如execv)参数组织成指针数组的形式。

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

int main(int argc, const char *argv[])
{
	printf("---begin---\n");
	
//  execl("/bin/ls","ls","-l",".",NULL);

	char * const arg[] ={"ls","-l",".",NULL}; 
	execv("/bin/ls",arg);
	
	execl("/home/linux/linux_prog/01-prog/mycp","mycp","1.c","2.c",NULL);

	printf("---end---\n");
	return 0;
}

(2)execvp 函数和execle函数

        文件寻找方式

             1)带p的函数(如execvp)表示可执行文件的寻找方式是从系统的环境变量PATH中的路径下面去找。

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

int main(int argc, const char *argv[])
{
	printf("---begin---\n");
	
	//if (execlp("mycp","mycp","1.c","2.c",NULL) < 0)

	char *const arg[] = {"mycp","1.c","2.c",NULL};
	if (execvp("mycp",arg) < 0)
	{
		perror("execl fail");
		return -1;
	}

	printf("---end---\n");	
	return 0;
}

              2)带e的函数(如execle)表示可以给要执行的新程序传递需要的环境变量。

#include <stdio.h>
#include <unistd.h>
extern char **environ;

int main(int argc, const char *argv[])
{
	char *menv[] = {"USER=linux","PASSWD=123456",NULL};

	//execle("./myenv","myenv",NULL,environ);
	//execle("./myenv","myenv",NULL,menv);
	execle("./myenv","myenv",NULL,menv);
	
	return 0;
}
在fork函数中的应用
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int execvpe(const char *file, char *const argv[],
		char *const envp[]);
int main(int argc, const char *argv[])
{

	char *env[5] = {NULL};
	char username[20];
	char passwd[20];

	while (1)
	{
		printf("Input username and passwd:");
		fgets(username,sizeof(username),stdin);
		username[strlen(username)-1] = '\0';
		fgets(passwd,sizeof(passwd),stdin);
		passwd[strlen(passwd)-1] = '\0';

		env[0] = username;
		env[1] = passwd;

		pid_t pid = fork();

		if (pid < 0)
		{
			perror("fork fail");
			return -1;
		}

		if (pid == 0)
		{
			char *const arg[] = {"check",NULL};
			if (execvpe("check",arg,env) < 0)
			{
				perror("execvpe fail");
				return -1;
			}
		}

	}
	return 0;
}

进程的终止

        八种情况:

   (1)正常结束:

  1. main 中 return
  2. exit() //库函数 。c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件注册清理函数(atexit)。
  3. _exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。 //系统调用   
  4. 主线程退出  
  5. 主线程调用pthread_exit。 

  (2)异常终止:

  1. abort()
  2.  signal   kill pid
  3. 最后一个线程被pthread_cancle开辟堆区空间

进程的退出

(1)孤儿进程:

子进程还在,但父进程已经结束。此时,避免子进程将来没有人收尸,由init进程来收养子进程。init(1) --- child(xxx)

(2)僵尸进程:

父进程还在,子进程先结束了。父进程没有做收尸操作。此时,子进程进入僵尸态。(wait/waitpid --- 查看子进程的退出状态)进程执行结束但空间未被回收变成僵尸进程。

  (1)exit     库函数

void exit(int status);

  •     退出状态,终止的进程会通知父进程,自己使如何终止的。    
  •     如果是正常结束(终止),则由exit传入的参数。
  •     如果是异常终止,则有内核通知异常终止原因的状态。
  •     任何情况下,父进程都能使用wait,waitpid获得这个状态,以及资源的回收。

            (1)功能:让进程退出,并刷新缓存区

            (2)参数:status:进程退出的状态

            (3)返回值:缺省

(2)_exit    系统调用

void _exit(int status);

        (1)功能:让进程退出,不刷新缓存区

        (2)参数:status:进程退出状态

        (3)返回值: 缺省

 (3)atexit                回调函数

int atexit(void (*function)(void));

        (1)功能:注册进程退出前执行的函数

        (2)参数:function:函数指针,指向void返回值void参数的函数指针

        (3)返回值: 成功返回0,失败返回非0

         当程序调用exit或者由main函数执行return时,所有用atexit。 注册的退出函数,将会由注册时顺序倒序被调用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int *q = NULL;
void cleanup1(void)
{
	free(q);
	printf("----cleanup 1---\n");
}
void cleanup2(void)
{
	printf("----cleanup 2---\n");
}

int main(int argc, const char *argv[])
{
	atexit(cleanup1);
	atexit(cleanup2);
    int *p = malloc(4);
	q = p;
	*p = 4;
	printf("*p = %d\n",*p);
	printf("hello world!\n");

	
	//_exit(0);
	//_exit(0);
	return 0;
}
注意:
  1. exit函数调用时,会调atexit函数 。_exit函数调用时,不会调到atexit
  2. atexit 程序正常结束。(1).main 返回 //exit 。(2).exit()        
  3. atexit函数 可以多次注册 
  4. 最后"退出清理函数"的调用顺序,与注册顺序相反。

    

进程空间的回收

        wait/waitpid函数

pid_t wait(int *wstatus);

pid_t waitpid(pid_t pid, int *wstatus, int options);

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

        (1)功能:该函数可以阻塞等待任意子进程退出并回收该进程的状态。一般用于父进程回收子进程状态。

        (2)参数:status 进程退出时候的状态如果不关心其退出状态一般用NULL表示。如果要回收进程退出状态,则用WEXITSTATUS回收。

        (3)返回值:成功 回收的子进程pid。 失败 -1;

        

    

标签:const,函数,int,char,线程,进程,NULL
From: https://blog.csdn.net/qq_69639971/article/details/141171218

相关文章

  • 【JVM】jvm 线程栈的一些设置 Thread Size
    1.概述下面是jvm线程栈的一些设置,简单的了解一下。2.XssXss和ThreadStackSize是等价的参数,用于设置Java线程栈的大小,单位为Kb。在命令行中,-Xss100K和-XX:ThreadStackSize=100是表示相同意义的参数。Xss参数的实现:Xss参数是通过设置ThreadStackSize数据......
  • windows核心编程 第三章,跨越进程边界共享内核对象,对象句柄的继承性,改变句柄的标志,命名
    windows核心编程3.3跨越进程边界共享内核对象3.3.1对象句柄的继承性3.3.2改变句柄的标志3.3.3命名对象3.3.4终端服务器的名字空间3.3.5复制对象句柄文章目录windows核心编程3.3跨越进程边界共享内核对象3.3.1对象句柄的继承性3.3.2改变句柄的标志3.3.3命名......
  • Java线程池学习
    Java线程池学习一、线程池基础1定义2优点3基本组件二、Java线程池实现1Executor接口2ExecutorService接口3ThreadPoolExecutor类3.1创建线程池实例三、执行策略1直接提交策略(DirectSubmissionExecutor)2固定线程数策略(FixedThreadPool)3缓存线程池策略......
  • 进程常用api
    进程常用apifork该api用于创建子进程,创建出的子进程与父进程共享内存和数据。pid_tfork(void)return:在子进程返回0;在父进程中返回子进程的PID;发生错误时返回EOF例:pid_tPID=fork();if(PID<0){perror("fork");exit(EXIT_FAILURE);}elseif(PID==0){......
  • TapData 信创数据源 | 国产信创数据库 PolarDB MySQL 数据同步指南,加速国产化进程,推进
    随着国家对自主可控的日益重视,目前在各个行业和区域中面临越来越多的国产化,采用有自主知识产权的国产数据库正在成为主流。长期以来,作为拥有纯国产自研背景的TapData,自是非常重视对于更多国产信创数据库的数据连接器支持,旗下产品已陆续与**阿里云、华为云、麒麟软件、优炫数据库......
  • C 多线程
    C多线程C程序中经常同时执行多项任务。例如,一个程序可能:(1)在执行程序过程中通过完成并行任务来提高性能。(2)在处理用户输入的同时,在后台进行耗时的数据通信和实时操作。通过并行执行(concurrentexecution)程序中的部分代码,可以实现不同任务同时进行。特别是在多处理器系......
  • 游戏安全入门-扫雷分析&远程线程注入
    前言无论学习什么,首先,我们应该有个目标,那么入门windows游戏安全,脑海中浮现出来的一个游戏--扫雷,一款家喻户晓的游戏,虽然已经被大家分析的不能再透了,但是我觉得自己去分析一下还是极好的,把它作为一个小目标再好不过了。我们编写一个妙妙小工具,工具要求实现以下功能:时间暂停、修......
  • Socket 可以填IP 域名也行 防止进程修改 计算 脚本跟踪调试 用带编号的日志
    网址带http 域名不带http 实例:  www.dududu.compp.com人家这域名就这样的带斜杠的是目录php的网址域名后面是php的协议,访问的是php的阿帕奇服务器哪一个是域名是能认出来的  注册表API可以修改操作系统,这玩仍得对照书和应用程序接口网站上面写的函数然后开......
  • Linux进程和计划任务管理
    目录一、进程基本概念1.进程2.程序和进程的关系 二、查看进程信息1.ps命令1.1 psaux命令1.2ps-elf命令 2.top命令 3.pgrep命令 4.jobs命令 三、查看进程树 四、进程的启动方式1.手动启动2.调度启动五、终止进程的运行1.Ctrl+C组合键2.kill......
  • 千万别从系统中创建线程, 看看从线程池中调用的线程的效率(实践篇)
    本篇会加入个人的所谓鱼式疯言❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言而是理解过并总结出来通俗易懂的大白话,小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.......