【Linux】进程创建和进程终止

进程创建

在Linux中fork函数可以在已经存在的进程中创建出一个新进程。这个新进程称为子进程,而原进程称为父进程。

  • 头文件#include<unistd.h>
  • pid_t fork(void);
  • 返回值:子进程返回0,父进程返回子进程的id,出错返回-1.

当进程创建fork函数,操作系统会进行:

  • 分配新的内存块和内核数据结构(PCB,进程地址空间、页表)给子进程
  • 将父进程的部分数据结构内容拷贝给子进程
  • 添加子进程到系统进程列表中
  • fork返回,开始由调度器调度

当一个寄存调用fork函数后,就有存在两个二进制代码相同的进程,且运行到相同地址。

cpp 复制代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{       
        printf("PID: %d, PPID: %d\n", getpid(), getppid());
        
        fork();

        printf("PID: %d, PPID: %d\n", getpid(), getppid());
        return 0;

} 

写时拷贝

  • 子进程在刚创建出来的时候,父子进程指向的是同一份代码和数据,此时子进程和父进程的代码和数据默认是不可被写入的------进程独立性
  • 父子进程任意一方在对数据进行写入的时候,就会触发写时拷贝,就会用写时拷贝的方式各自拷贝一份副本

进程在刚被创建出来的时候,操作系统会将父子进程页表的内容都设置成只读权限的,当一个进程触发修改的时候,触发写时拷贝,让修改后的数据重新在物理内存中开辟一段空间,并建立映射,同时将这部分数据在页表中修改成可写权限

fork函数的常规用法

  • 父进程希望子进程做复制自己,使得父子进程同时执行不同的代码段。
  • 一个进程要执行一个不同的程序。

fork函数调用失败的原因

  • 系统中存在过多的进程
  • 实际用户的进程数超过了限制

进程终止

cpp 复制代码
#define N 5

void runChild()
{
	int cnt = 5;
	while(cnt--)
	{
		printf("child pid : %d ; ppid : %d \n", getpid(), getppid());
		sleep(1);
	}
}

int main()
{
	for(size_t i = 0; i < N; ++i)
	{
		pid_t id = fork();
		if(id == 0)
		{
			// 子进程开始
			runChild();
			exit(1);
			// 子进程结束
		}
	}	

	// 让父进程先不要退出
	sleep(50);
	return 0;

}
  • 使用循环的方式创建出5个进程,由于父进程没有对这5个进程进行回收,导致这5个进程变成僵尸进程。
  • 当创建多进程程序的时候,不管是父子进程还是兄弟进程,谁用运行这件事情是由调度器来决定的,由于这几个进程的优先级相等,谁先被调度器放在运行队列中,谁就会被先运行。
  • exit();会终止一个进场。

进程退出的场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

问题:为什么main函数总是return 0?会返回其他值吗??这个返回值返回到哪里了?为什么?

  • return 0; // 表示进程的退出码,表征进程的运行结果是否正确。
  • 0->表示success
  • return 0;这个退出码会被这个进程的父进程获取到,也就是bash进程获取到。

一般而言,只有父进程才会关心子进程退出是否正常,尤其是当代码运行结束,结果不正确的情况------这里就可以用return的不同的返回值数字,来表示不同的出错原因------退出码。

main函数的返回值,其本质是进程运行完成时,是否是正确的结果,如果不是,可以用不同的数字,表示不同的出错原因!

$? 表示最近一次进程的退出码。第一次打印的退出码是上一个进程的退出码,第二次和第三次表示的是执行echo命令进程的退出码。

cpp 复制代码
int main()
{
        // 打印错误码信息
        // 具体不知道存在多少错误码信息,选择一个较大的数
        for(int i = 0; i < 200; ++i)
        {
                printf("[%d]->%s\n", i, strerror(i));
        }

        return 0;
}
bash 复制代码
[0]->Success
[1]->Operation not permitted
[2]->No such file or directory
[3]->No such process
[4]->Interrupted system call
[5]->Input/output error
[6]->No such device or address
[7]->Argument list too long
[8]->Exec format error
[9]->Bad file descriptor
[10]->No child processes
[11]->Resource temporarily unavailable
[12]->Cannot allocate memory
[13]->Permission denied
[14]->Bad address
[15]->Block device required
[16]->Device or resource busy
[17]->File exists
[18]->Invalid cross-device link
[19]->No such device
[20]->Not a directory
[21]->Is a directory
[22]->Invalid argument
[23]->Too many open files in system
[24]->Too many open files
[25]->Inappropriate ioctl for device
[26]->Text file busy
[27]->File too large
[28]->No space left on device
[29]->Illegal seek
[30]->Read-only file system
[31]->Too many links
[32]->Broken pipe
[33]->Numerical argument out of domain
[34]->Numerical result out of range
[35]->Resource deadlock avoided
[36]->File name too long
[37]->No locks available
[38]->Function not implemented
[39]->Directory not empty
[40]->Too many levels of symbolic links
[41]->Unknown error 41
[42]->No message of desired type
[43]->Identifier removed
[44]->Channel number out of range
[45]->Level 2 not synchronized
[46]->Level 3 halted
[47]->Level 3 reset
[48]->Link number out of range
[49]->Protocol driver not attached
[50]->No CSI structure available
[51]->Level 2 halted
[52]->Invalid exchange
[53]->Invalid request descriptor
[54]->Exchange full
[55]->No anode
[56]->Invalid request code
[57]->Invalid slot
[58]->Unknown error 58
[59]->Bad font file format
[60]->Device not a stream
[61]->No data available
[62]->Timer expired
[63]->Out of streams resources
[64]->Machine is not on the network
[65]->Package not installed
[66]->Object is remote
[67]->Link has been severed
[68]->Advertise error
[69]->Srmount error
[70]->Communication error on send
[71]->Protocol error
[72]->Multihop attempted
[73]->RFS specific error
[74]->Bad message
[75]->Value too large for defined data type
[76]->Name not unique on network
[77]->File descriptor in bad state
[78]->Remote address changed
[79]->Can not access a needed shared library
[80]->Accessing a corrupted shared library
[81]->.lib section in a.out corrupted
[82]->Attempting to link in too many shared libraries
[83]->Cannot exec a shared library directly
[84]->Invalid or incomplete multibyte or wide character
[85]->Interrupted system call should be restarted
[86]->Streams pipe error
[87]->Too many users
[88]->Socket operation on non-socket
[89]->Destination address required
[90]->Message too long
[91]->Protocol wrong type for socket
[92]->Protocol not available
[93]->Protocol not supported
[94]->Socket type not supported
[95]->Operation not supported
[96]->Protocol family not supported
[97]->Address family not supported by protocol
[98]->Address already in use
[99]->Cannot assign requested address
[100]->Network is down
[101]->Network is unreachable
[102]->Network dropped connection on reset
[103]->Software caused connection abort
[104]->Connection reset by peer
[105]->No buffer space available
[106]->Transport endpoint is already connected
[107]->Transport endpoint is not connected
[108]->Cannot send after transport endpoint shutdown
[109]->Too many references: cannot splice
[110]->Connection timed out
[111]->Connection refused
[112]->Host is down
[113]->No route to host
[114]->Operation already in progress
[115]->Operation now in progress
[116]->Stale file handle
[117]->Structure needs cleaning
[118]->Not a XENIX named type file
[119]->No XENIX semaphores available
[120]->Is a named type file
[121]->Remote I/O error
[122]->Disk quota exceeded
[123]->No medium found
[124]->Wrong medium type
[125]->Operation canceled
[126]->Required key not available
[127]->Key has expired
[128]->Key has been revoked
[129]->Key was rejected by service
[130]->Owner died
[131]->State not recoverable
[132]->Operation not possible due to RF-kill
[133]->Memory page has hardware error
[134]->Unknown error 134
[135]->Unknown error 135
[136]->Unknown error 136
[137]->Unknown error 137
[138]->Unknown error 138
[139]->Unknown error 139

在Linux操作系统中,系统提供了[0, 133]个错误码信息。

系统提供的错误码和错误码描述是有对应关系的。

自己设计一套退出码体系

【总结】进程退出的时候,会返回退出码来表示进程运行的结果。例如,0表示成功等等。

与进程退出码类似的,系统提供了一个全局变量:errno

cpp 复制代码
int main()
{
        int ret = 0;
        char* p = (char* )malloc(1000 * 1000 * 1000 * 4);
        if(p == NULL)
        {
                // 描述错误信息
                printf("malloc fail, %d : %s\n", errno, strerror(errno));
                ret = errno; // 使用系统的错误返回值
        }
        else
        {
                printf("malloc success\n");
        }

        return ret;
}

通过这段代码可以获取到错误码,也可以得到错误码信息,同时也可以将错误码返回给父进程,让父进程获取到错误码信息,errno是C语言库实现的一个全局变量。

程序异常退出

【程序异常退出】当程序异常退出的时候,本质可能就是代码没有跑完,所以其进程的退出码没有任何意义,不需要关心。

在程序退出时,需要注意程序是否时异常退出,如果没有出异常,再看结果是否正确。

进程出现异常,本质就是进程收到了对应的信号。

这段内容证明了进程异常结束是收到了信号

进程退出的方式

exit函数

在main函数中exit()函数和return函数具有相同的效果。

exit和return的区别
cpp 复制代码
void show()
{
	printf("show begin-------\n");
	printf("show begin-------\n");
	printf("show begin-------\n");
	printf("show begin-------\n");

	exit(13);

	printf("show end-------\n");
	printf("show end-------\n");
	printf("show end-------\n");
	printf("show end-------\n");


}

int main()
{
	show();
	printf("success show");
	printf("success show");
	printf("success show");
	return 0;
}

exit函数表示在任意地方被调用,都表示调用进程之间退出。而return只表示当前函数返回,程序仍然会继续向后运行。

_exit函数

exit和_exit都可以让进程退出,但是却有不同,_exit系统调用接口,而exit是C语言封装的_exit函数,相当于exit最有也会调用_exit,但是在调用_exit之前,还做了其他工作:

  1. 执行用户通过atexit或者on_exit定义的清理函数
  2. 关闭所有打开的流,所有的缓存数据均被写入
  3. 调用_exit函数。

【总结】_exit是系统调用,当终止的时候,_exit会直接终止进程,缓冲区数据不做刷新,而exit在终止进程之前会冲刷缓冲区等操作。

printf()函数会先将数据写入缓冲区内,在合适的时候(例如\n)进行刷新,因此缓冲区一定不会在内核中,因为如果缓冲区在内核区,则_exit一定会将内核区中的数据刷新出来。

相关推荐
晨曦夜月2 分钟前
头文件与目标文件的关系
linux·开发语言·c++
Xyz996_3 分钟前
Ansible进行Nginx编译安装的详细步骤
运维·ansible
白仑色4 分钟前
java中的anyMatch和allMatch方法
java·linux·windows·anymatch·allmatch
云和数据.ChenGuang8 分钟前
自动化运维工程师之ansible启动rpcbind和nfs服务
运维·服务器·运维技术·数据库运维工程师·运维教程
yimengsama9 分钟前
VMWare虚拟机如何连接U盘
linux·运维·服务器·网络·windows·经验分享·远程工作
松涛和鸣13 分钟前
32、Linux线程编程
linux·运维·服务器·c语言·开发语言·windows
云和数据.ChenGuang15 分钟前
AB压力测试运维工程师技术教程
运维·压力测试·运维工程师
꧁坚持很酷꧂16 分钟前
把虚拟机Ubuntu中的USB设备名称改为固定名称
linux·数据库·ubuntu
少年白char28 分钟前
【AI漫剧】开源自动化AI漫剧生成工具 - 从文字到影像:AI故事视频创作的全新可能
运维·人工智能·自动化
容智信息30 分钟前
容智Report Agent智能体驱动财务自动化,从核算迈向价值创造
大数据·运维·人工智能·自然语言处理·自动化·政务