linux系统调用——进程相关

概念

进程的概念: 进程是运行的程序,是动态的,一个程序运行多次就是多个进程。

进程的属性

  • 进程控制块
    • 操作系统使用一个结构体记录进程的各类属性,该结构体被称为进程控制块
  • 进程标识
    • 进程id,每个进程的id都是唯一的
    • 父进程id
  • 地址空间
    • 代码段的起始地址和长度
    • 数据段的起始地址和长度
    • 堆栈段的起始地址和长度
  • 打开文件列表
    • 打开文件时,在打开文件列表中记录被打开文件的信息
    • 关闭文件时,在打开文件列表中删除被关闭文件的信息
    • 进程终止时,操作系统遍历打开文件列表,将尚未关闭的文件关闭

进程的特性

并发性:

  • 父进程和子进程并发运行
    • 父进程创建子进程后,父子进程都处于运行状态中
    • 两个进程的输出结果是交织在一起的
  • 两者的代码段内容相同
    • 父进程从fork()返回处执行,fork()返回为子进程的PID
    • 子进程从fork()返回处执行,fork()返回0

隔离性:

  • 进程的地址空间是互相隔离的
      每个进程拥有自己的地址空间
      进程仅能访问自己的地址空间
      如果出现非法内存访问,仅仅当前进程受到影响
  • 全局变量
      全局变量存在于两个地址空间中,并非被两个进程共享
      父进程和子进程访问的是自己的全局变量,互相不影响

相关系统调用函数

获取进程id

  • 头文件
    #include <sys/types.h>
    #include <unistd.h>

  • 函数
    pid_t getpid(void);
    pid_t getppid(void);

  • 功能描述
    getpid获取当前进程ID
    getppid获取父进程ID
    pid_t是C语言中用户自定义类型,在sys/types.h中定义typedef int pid_t;

创建子进程

  • 头文件
    #include <unistd.h>
  • 函数
    pid_t fork(void);
  • 功能
    • 创建一个子进程,父子进程并发运行
    • 子进程复制父进程的如下属性
      • 代码段、数据段的内容,父子进程拥有相同的代码和数据
      • 打开文件列表
    • 不复制进程的PID属性,父子的进程的PID是唯一的
  • 返回值
    • pid是进程ID的缩写,pid_t是使用typedef定义的进程ID类型
    • 父进程从fork返回处继续执行,在父进程中,fork返回子进程PID
    • 子进程从fork返回处开始执行,在子进程中,fork返回0

例子

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

int main()
{ 
    pid_t pid;
    pid = fork();
    printf("pid = %d\n", pid);
    if (pid == 0)
        printf("In child process\n");
    else
        printf("In parent process\n");
    return 0;
}

在这里插入图片描述
输出两次pid = 是因为fork之后创建了子进程,子进程与父进程并行运行,所以打印了两次pid.

装入进程
execl函数
#include <unistd.h>
int execl(const char *path, const char *arg, ...);

  • 功能
    将当前进程的地址空间的内容全部清空
    将path指定的可执行程序的代码和数据装入到当前进程的地址空间
  • 参数
    该函数的参数个数可变
    最后一个参数必须是NULL
    第一个参数path指定被装入程序的路径
    可以是命令的绝对路径
    可以是命令的相对路径
  • 返回值
    装入失败后,返回值为-1
    装入成功后,从被装入程序的main函数开始执行

例子

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

int main()
{ 
    puts("before exec");
    int error = execl("/bin/echo", "echo", "a", "b", "c", NULL);
    if (error < 0)
        perror("execl");
    puts("after exec");
    return 0;
}

在这里插入图片描述
最后的after exex没有打印是因为execl函数把原来程序的代码段、数据段等都清空了,加载新的echo的代码。

execlp函数
#include <unistd.h>
int execlp(const char *file, const char *arg, ...);

  • 功能
    与execl相同

  • execl和execlp的区别
    在execl中,第一个参数指定可执行程序的路径
    该路径可以是绝对路径
    该路径可以是相对于当前工作目录的相对路径

    在execlp中,除此之外,该路径可以是PATH环境变量指定目录下的相对路径

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

int main()
{
    puts("before exec");
    int error = execlp("echo", "echo", "a", "b", "c", NULL);  
    if (error < 0)
        perror("execl");
    puts("after exec");
    return 0;
}

execv函数
#include <unistd.h>
int execv(const char *path, const char *argv[]);

  • 功能:与execl相同
  • 区别:参数不同, 函数名execv末尾的v表示vector,参数以数组的形式传递给可执行程序

execvp函数
#include <unistd.h>
int execvp(const char *file, char *argv[]);

  • 功能: 与execv相同
  • execv和execvp的区别
    在execvp中,第一个参数指定可执行程序的路径该路径可以是PATH环境变量指定目录下的相对路径。

退出程序
exit原型
#include <stdlib.h>
void exit(int status);

  • 功能
    正常退出当前进程
    将status & 0xFF作为退出码返回给父进程
  • 预定义常量
    EXIT_SUCCESS,为0的数值,表示程序正常退出
    EXIT_FAILURE,为非0的数值,表示程序执行过程发生了 错误,异常退出
  • 在linux shell中,可以通过特殊的环境变量$?获得程序的退出码.
  • 在正常的int main函数结束return时其实也调用了exit

atexit原型
#include <stdlib.h>
int atexit(void (*function)(void));

  • 功能
    • 注册一个回调函数function,进程正常结束时,function会被调用
    • 如果注册多个回调函数,进程结束时,以与注册相反的顺序调用回调函数

例子

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

void f1()
{
    puts("f1");
}
 
void f2()
{
    puts("f2");
}
 
void f3()
{
    puts("f3");
}

int main()
{ 
    atexit(f1);
    atexit(f2);
    atexit(f3);
    puts("main");
    return 0;
}

在这里插入图片描述

等待进程
wait原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);

  • 功能: 等待子进程结束
  • 参数: 如果status不为NULL,子进程的退出码保存在status指向的变量中

退出码

  • 进程可能由于不同的原因退出

    • 主动调用exit正常退出
    • 接受信号后退出
  • 查询退出原因的宏

名称功能
WIFEXITED(status)如果进程通过调用exit正常退出,则返回真
WEXITSTATUS(status)如果进程通过调用exit正常退出,返回进程的退出码
WIFSIGNALED(status)如果进程接受信号后退出,则返回真
WTERMSIG(status)如果进程接受信号后退出,返回导致进程退出的信号

例子

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

void child()
{
    exit(123);
}

int main()
{
    int pid;
    pid = fork();
    if (pid == 0)
        child(); 

    int status;
    wait(&status);
    if (WIFEXITED(status)) {
        printf("WIFEXITED = true\n"); 
        printf("WEXITSTATUS = %d\n",  WEXITSTATUS(status));
    }
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值