迅为嵌入式Linux学习笔记4——进程

迅为嵌入式Linux学习笔记4——进程

进程指的是正在运行的程序,是操作系统分配资源的最小单位。

进程ID

每个进程都有唯一的标识符,这个标识符就是进程ID,简称pid

进程间通信的方法

  • 管道通信:分为有名管道和无名管道
  • 信号通信:信号的发送,信号的接收,信号的处理
  • IPC通信:共享内存,消息队列,信号量
  • socket通信

进程的三种状态

  • 就绪态
  • 执行态
  • 阻塞态

进程控制

创建进程

fork函数

头文件:

#include <unistd.h>

函数原型:

pid_t fork(void);

返回值:

fork函数有三种返回值:

  • 在父进程中,fork返回新创建的子进程的PID
  • 在子进程中,fork返回0
  • 如果出现错误,fork返回一个负值

获得pid函数

pid_t getpid(void);//获得当前进程的PID
pid_t getppid(void);//获得当前进程父进程的PID

例程1

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

int main(int argc, char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0) {
        printf("fork error\n");
        return -1;
    }
    // 父进程
    if (pid > 0) {
        printf("This is parent, parent pid is %d\n", getpid());
    }

    // 子进程
    if (pid == 0) {
        printf("This is child, child pid is %d, parent pid is %d\n", getpid(), getppid());
    }

    return 0;
}

// 子进程是从fork函数开始往下执行的
// 父子进程的执行顺序是不一定的,可能父进程先执行,也可能子进程先执行

exec函数族

在Linux中并没有exec函数,而是有6个以exec开头的函数族,下面列举了exec函数族的6个函数成员的函数原型

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);

换核不换壳

  • 当进程调用exec函数时,进程的用户空间代码和数据空间会完全被新的程序替代。
  • .test 和 .data 都会替换成新的程序。

在Linux中使用exec函数族主要有以下两种情况:

  1. 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec函数族让自己重生。
  2. 如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。

例程2

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

int main(int argc, char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0) {
        printf("fork error\n");
        return -1;
    }
    // 父进程
    if (pid > 0) {
        printf("This is parent, parent pid is %d\n", getpid());
    }

    // 子进程
    if (pid == 0) {
        printf("This is child, child pid is %d, parent pid is %d\n", getpid(), getppid());
        execl("/home/samba/linux/15/hello", "hello", NULL);// 第一个参数是程序路径,第二个参数是程序名,可变参数是程序所需参数,最后一个参数以NULL结尾
        exit(1);// 假如程序执行失败,退出当前进程,返回1(成功返回0,失败返回1)
    }

    return 0;
}

ps和kill命令

ps命令:

ps命令可以列出系统中当前运行的那些进程。

  • 命令格式:ps [参数]
  • 命令功能:用来显示当前进程的状态
  • 常用参数:aux(a:显示终端上的所有进程,u:显示归属用户、相关的内存使用情况,x:显示不关联终端的进程)
# 使用管道查找aux线程
ps aux | grep aux

kill命令:

kill命令用来杀死进程

  • 举例:kill -9(SIGKILL) PID
  • kill -l //查看有哪些信号

孤儿进程和僵尸进程

孤儿进程

父进程结束以后,子进程还未结束,这个子进程就叫做孤儿进程。孤儿进程会被init进程(PID号为1)领养,init进程成为孤儿进程的父进程。

例程3

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

int main(int argc, char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0) {
        printf("fork error\n");
        return -1;
    }
    // 父进程
    if (pid > 0) {
        printf("This is parent, parent pid is %d\n", getpid());
    }

    // 子进程
    if (pid == 0) {
        sleep(2);
        printf("This is child, child pid is %d, parent pid is %d\n", getpid(), getppid());
    }

    return 0;
}

僵尸进程

子进程结束以后,父进程还在运行,但是父进程不去释放进程控制块,这个子进程就叫做僵尸进程。

例程4

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

int main(int argc, char *argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0) {
        printf("fork error\n");
        return -1;
    }
    // 父进程
    if (pid > 0) {
        printf("This is parent, parent pid is %d\n", getpid());
        while(1);
    }

    // 子进程
    if (pid == 0) {
        printf("This is child, child pid is %d, parent pid is %d\n", getpid(), getppid());
        exit(0);
    }

    return 0;
}

wait函数

头文件与函数原型:

#include <sys/wait.h>
pid_t wait(int *status);
// 返回值:成功返回回收的子进程的pid,失败返回-1

wait函数是一个阻塞函数,调用一次只能回收一个进程。每当父进程调用wait函数,就会去分析当前进程的某个子进程是否已经退出,如果找到了,就回去回收这个子进程的信息,并将它彻底销毁。如果没有找到,wait函数就会一直阻塞在这里,直到出现某子进程退出为止。

与wait函数的参数有关的两个宏定义:

WIFEXITED(status): 如果子进程正常退出,则该宏定义为真
WEXITSTATUS(status): 如果子进程正常退出,则该宏定义的值为子进程的退出值。

例程5

#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
	pid_t pid;
	// 创建一个子进程
	pid = fork();
	if (pid < 0) {
		printf("error\n");
	}
	// 父进程
	if (pid > 0) {
		int status;
		wait(&status);
		if (WIFEXITED(status) == 1) {
			printf("return value is %d\n", WEXITSTATUS(status));
		}
	}
	// 子进程
	if (pid == 0) {
		sleep(2);
		printf("This is child\n");
		exit(6);
	}
	return 0;
}

守护进程

守护进程运行在后台,不跟任何控制终端关联。

守护进程创建有两个要求:

  1. 必须作为init进程的子进程
  2. 不跟控制终端交互

创建步骤:

  1. 使用fork函数创建一个新的进程,然后让父进程使用exit函数直接退出。(必须要的)
  2. 调用setsid函数。抛弃控制终端,退出当前进程组和会话。创建一个新的会话,并在新会话里创建新的进程组,调用函数的这个进程就会成为新会话新进程组里的唯一进程。(必须要的)
  3. 调用chdir函数,将当前的工作目录改成根目录,蹭强程序的健壮性。(不是必须要的)
  4. 重设我们umask文件掩码,增强程序的健壮性和灵活性。(不是必须要的)
  5. 关闭文件描述符,节省资源。(不是必须要的)
  6. 执行我们需要执行的代码。(必须要的)

例程6

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(void)
{
	pid_t pid;
	// 步骤一: 创建一个新的进程
	pid = fork();
	// 父进程直接退出
	if (pid > 0) {
		exit(0);
	}
	if (pid == 0) {
		// 步骤二: 调用setsid函数摆脱控制终端
		setsid();
		// 步骤三:更改工作目录
		chdir("/");
		// 步骤四:重设umask文件掩码
		umask(0);
		// 步骤五:关闭标准输入,标准输出,标准出错
		close(0);
		close(1);
		close(2);
		// 步骤六:执行我们要执行的代码
		while (1) {
		
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值