【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一定会将内核区中的数据刷新出来。

相关推荐
Zhao·o4 小时前
KafkaMQ采集指标日志
运维·中间件·kafka
P***25395 小时前
MCP负载均衡
运维·负载均衡
SAP庖丁解码5 小时前
【SAP Web Dispatcher负载均衡】
运维·前端·负载均衡
码上上班5 小时前
ubuntu 安装ragflow
linux·运维·ubuntu
HIT_Weston5 小时前
38、【Ubuntu】【远程开发】拉出内网 Web 服务:构建静态网页(一)
linux·前端·ubuntu
百***86465 小时前
服务器部署,用 nginx 部署后页面刷新 404 问题,宝塔面板修改(修改 nginx.conf 配置文件)
运维·服务器·nginx
XH-hui5 小时前
【打靶日记】HackMyVm 之 hunter
linux·网络安全·hackmyvm·hmv
渡我白衣6 小时前
五种IO模型与非阻塞IO
运维·服务器·网络·c++·网络协议·tcp/ip·信息与通信
xu_yule6 小时前
Linux_15(多线程)线程安全+线程互斥(加锁)+死锁
linux·运维·服务器
wa的一声哭了7 小时前
并行计算 PCAM方法学
linux·运维·服务器·arm开发·python·spring·django