系统编程之进程控制

程序是一段可执行的代码,是在硬盘上的一个文件,进程则是程序执行的一个实例,每个进程都有自己的地址空间。

进程的三种状态及其转换:



进程的ID(PID)是唯一标识一个进程,是一个正整数。PPID为父进程的进程ID。

init是所有进程的祖先,称为祖先进程,进程ID为1。

获得ID的函数:

pid_t getpid(void);//获得本进程ID
pid_t getppid(void);//获得父进程ID
pid_t getuid(void);//获得当前用户ID

创建一个子进程

fork();

函数原型:

pid_t fork(void);

函数作用:

创建一个子进程,创建失败返回-1,创建成功,在父进程中返回子进程ID,在子进程中返回0。子进程复制父进程地址空间(除了代码段),与父进程共享代码段。两个进程都从fork()处往下执行。父子进程的执行先后顺序不确定(我在红帽系统下调试,绝大多数情况是子进程先执行)。

vfork();

函数原型:

pid_t vfork(void);

函数作用:

创建一个子进程,创建失败返回-1,创建成功,在父进程中返回子进程ID,在子进程中返回0。子进与父进程共享地址空间。两个进程都从vfork()处往下执行。子进程必须要显示调用exit(),否则会有段错误。子进程先于父进程执行,子进程执行完,CPU才会调度父进程。


exec函数族

用被执行的程序完全替换调用它的程序。

函数原型:

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

终止一个进程:

exit();

函数原型:

void _exit(int status);

函数作用:

用来正常终结目前进程的执行,并把参数status(子进程结束状态)返回给父进程,在停止进程之前,要检查文件的打开情况,并把文件缓冲区中的内容写回文件才停止进程。。

_exit();

函数原型:

void _exit(int status);

函数作用:

不会返回参数给父进程,父进程可以由wait函数取得子进程结束状态,该函数直接使进程停止,清除其使用的内存,并清除缓冲区中内容。


进程终止的两种情况:

父进程比子进程先退出:

子进程会被祖先进程接收,并且会在后台执行,当子进程结束,祖先进程负责处理退出状态。这样的进程被称为守护进程。守护进程是一个在后台运行并且不受任何终端控制的进程。

创建守护进程:

1、创建子进程,关闭父进程。

2、设置文件掩码。

3、设置新的会话,脱离当前会话和终端的控制。

4、改变当前工作目录。

5、关闭标准输出、标准输入、标准错误。

6、重定向标准输出、标准输入、标准错误。

源代码如下:

int daemonize(void)
{
	pid_t pid =  fork();//创建子进程
	if (pid < 0)
	{
		perror("fork");
		return -1;
	}
	else if (pid > 0)
	{
		exit(0);//关闭父进程
	}
	else
	{
		umask(0);//设置文件掩码
		if(setsid() < 0)//设置新的会话
		{
			perror("setsid");
			return -1;
		}
		if(chdir("/") < 0)//改变当前工作目录为根目录
		{
			perror("chdir");
			return -1;
		}
		//关闭标准输出、标准输入、标准错误
		close (STDIN_FILENO);
		close (STDOUT_FILENO);
		close (STDERR_FILENO);
		//重定向标准输出、标准输入、标准错误
		open ("/dev/null",O_RDONLY);
		open ("/dev/null",O_RDWR);
		open ("/dev/null",O_RDWR);
	}
	return 0;
}


运行程序时,在后面加上取地址符&也可以使进程跑在后台,但会受当前终端控制。


子进程比父进程先退出:

如果父进程没有处理子进程的结束状态,就会产生僵尸进程,使用ps命令查看时,进程后有<defunct>标志。此进程占内存很小,但是会占用一个进程ID,由于系统的进程ID是有限的,如果僵尸进程过多,系统可能无法再产生新的进程。所以僵尸进程的危害还是很大的,而很多人都不会注意处理。

解决办法:父进程要处理子进程退出状态,使用wait函数。

函数原型:

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);

wait(int *status)函数的参数为结束状态,一般不关心的话可填NULL,此函数的返回值是子进程ID,如果没有子进程,立刻返回错误,如果有,会一直等待,处于阻塞状态,直到等到子进程退出,此函数一次只能处理一个子进程,用起来不是很方便。

waitpid(pid_t pid, int *status, int options)函数的参数更多,第一个参数可以指定处理的子进程的ID,也可以设为通用(-1),第三个参数可指定是否是使用阻塞方式,可以设为非阻塞方式,没有检测到推出的子程序,就直接往下执行,使用更反方便。


进程控制对于系统编程非常重要,我整理的也仅仅是皮毛,欢迎大家一起交流讨论。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值