(004)linux系统编程

大憨熊 提交于 2019-12-24 04:31:42


fork 函数

fork 函数用于创建一个进程(创建当前调用此函数的子进程)

  • 父进程的fork返回 子进程ID ;而子进程的fork返回 0,表示进程创建成功
  • getpid函数返回调用此函数的进程ID;getppid函数返回调用此函数的父进程ID
  • getuid函数返回当前进程的实际用户ID;geteuid函数返回当前进程的有效用户ID
  • 在fork之后,父子进程有相同的权利去抢占CPU,并不意味着父进程的优先级一定会比子进程的高,取决于内核所使用的调度算法
  • fork函数遵循 “读时共享,写时复制” 的原则:如果fork后的代码只有读操作,那么共享同一块物理地址;否则复制一份

循环创建多个进程示例:

  • for() { fork(); }循环调用fork函数会创建 2n - 1 个子进程。正确的方法应该如下:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
	pid_t pid;
	int i;
	for (i = 0; i < 5; ++i) {
		pid = fork();
		if (pid == -1) {
			perror("fork error");
			exit(1);
		} else if (pid == 0) {
			break;
		}
	}

	if (i < 5) {
		sleep(i);	// 按创建子进程的顺序打印
		printf("I'm %d child, pid = %u, ppid = %u\n", i+1, getpid(), getppid());
	} else {
		sleep(i);
		printf("I'm parent, pid = %u, ppid = %u\n", getpid(), getppid()); // 这里的ppid是bash/shell
	}
	return 0;
}

在fork以后,父子进程有哪些相同,哪些不同呢?各自独立和共享的又有什么呢?

  • 相同:代码段、堆、栈、用户ID、进程工作目录、信号处理方式等
  • 不同:进程ID、父进程ID、定时器、未决信号集等
  • 全局变量位于data段,父子进程是各自独立的(在做写操作的情况下,否则是共享的)
  • 共享的是文件描述符mmap建立的映射区

gdb调试多进程程序

gdb在调试时默认跟踪父进程,在gdb中设置fork之后的跟踪进程的方法(要在fork函数调用前设置)是

  • set follow-fork-mode child
  • set follow-fork-mode parent

子进程状态(孤儿与僵尸)

孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程(1号)

僵尸进程:进程终止,父进程尚未回收,子进程0-4G进程地址空间都释放了,但唯独PCB残留在内核中,成为僵尸进程。

  • 僵尸进程不能被kill命令清除,因为kill命令是用来终止进程的,而僵尸进程已经终止
  • PCB存在的意义是为了父进程给子进程“收尸”,例如获取子进程的退出状态导致终止的信号信息

子进程回收

对于已经终止的子进程,它的父进程可以调用 wait 或 waitpid 来获取子进程终止时在内核中残留的PCB信息,然后彻底清除掉这个进程。

1、wait 函数

  • 父进程阻塞,等待子进程退出
  • 回收子进程残留资源
  • 获取子进程结束状态或退出原因

1)进程正常结束:WIFEXITED(status)

  • WEXITSTATUS(status),程序正常结束,用该宏函数获取进程的退出状态

2)进程异常终止:WIFSIGNALED(status)

  • WTERMSIG(status),程序异常终止,用该宏函数获取导致终止的信号编号

2、waitpid 函数


exec 函数族

作用是在程序中运行一个程序。当一个进程调用其中一个exec函数时,该进程的用户空间代码和数据完全被新程序替换(.text,.data),从新程序的启动例程(.text第一条指令)开始执行。

  • 调用exec并不创建新进程,所以调用exec前后该进程的ID并没有改变

示例:int execlp(const char* file, const char *arg, ...);,命令行参数以NULL结尾

  • execlp("ls", "ls", "-l", NULL);,第二个参数是argv[0],第二个以后的参数是传递给可执行程序 ls;使用程序名在PATH环境变量中搜索,有环境变量参与
  • execl("/bin/ls", "ls", "-l", NULL);,使用参数1给出的绝对路径搜索,没有环境变量参与

使用实例:利用exec函数,将当前系统中的进程信息打印到文件中

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

int main () {

    int fd = open("ps.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (fd < 0) {
		perror("open ps.out error");
		exit(1);
	}
	dup2(fd, STDOUT_FILENO);	// 把参1的文件描述符复制给参2,导致参2指向的文件现在指向参1指向的文件
	execlp("ps", "ps", "ax", NULL);
	perror("exec error");	// exec出错才会执行该语句
	exit(1);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!