[GNU/Linux] Linux系统调用-进程相关

本文详细介绍了进程标识符的获取方法、进程创建、进程终止的方式、如何等待子进程结束、进程的调度以及如何执行另一个程序等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程标识符

函数原型及解释

pid_t getpid(void);
pid_t getppid(void);
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
  • 返回值:成功时getpid返回自身的pidgetppid返回自身父进程的pidgetuid返回自身的实际运行uidgeteuid返回自身的有效运行uidgetgid返回自身实际运行gidgetegid返回自身有效运行gid。这些函数均没有出错返回

创建子进程

函数原型及解释

pid_t fork(void);
pid_t vfork(void);
  • 返回值:若函数执行失败,则返回-1,若函数执行成功,由于产生了子进程,因此函数将有两个返回值。在父进程中,函数将返回子进程的pid,在子进程中,函数将返回0

其他相关解释

  • 返回值为何如此设计:一个父进程可以有多个子进程,因此fork需要告诉父进程新的子进程的pid的值是多少,然而一个子进程只有一个父进程,因此子进程可以轻易的使用getppid函数获得到父进程的pid
  • 执行完函数之后会发生什么:若执行成功,从父进程和子进程会共同执行从fork函数开始的接下来的所有代码,但是使用fork函数创建子进程的话父进程和子进程谁先开始执行是不确定的。
  • forkvfork函数的区别:vfork函数创建的子进程的目的应该是执行exec族函数。除此之外,vfork函数保证了子进程在创建成功之后一定优先运行,且在子进程执行exec族函数之前或子进程退出之前(两者满足其一即可)父进程都不会被内核调度运行。
  • fork族函数中父进程与子进程的关系:①调用fork族函数时,父进程打开的所有文件描述符将被复制到子进程中;②父子进程将共享同一个文件偏移量;③子进程将从父进程中继承实际uid实际gid有效uid有效gid附属组id进程组id会话id控制终端当前工作目录umask值环境变量资源限制等等一系列属性。
  • fork族函数运行失败的原因:一般有以下两种情况:①系统中已经有了太多的进程;②该实际用户ID下的进程数量超过了系统的限制。

进程的终止

正常终止

进程的正常终止方式有以下六种:

main函数中调用了return
②在进程的任意位置调用了exit函数。

以上两种情况是同义的,他们都将调用终止处理程序,即使用了atexit函数登记的函数,并关闭所有标准I/O流。

③ 在进程的任意位置调用_exit函数。
④ 在进程的任意位置调用_Exit函数。

以上两个情况也是同义的,他们都不会调用终止处理程序,而且不会冲洗I/O流。

⑤最后一个线程在启动例程中执行了return语句。
⑥最后一个线程调用了pthread_exit函数。

以上两个情况中,终止的进程返回值一定为0,与线程的返回值没有任何关系。

异常终止

进程的异常终止有以下三种情况

①进程的任意位置调用了abort函数(这将产生SIGABRT信号)。
②进程收到一些信号时。
③最后一个线程对取消请求做出相应。

可以看到前两种情况中,第一种情况是第二种情况的一个特例。

其他相关

  • 若子进程在运行时父进程已经结束,此时子进程会成为“孤儿进程”,内核会将之置为init进程的子进程。

等待子进程结束

函数原型及解释

pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
  • 返回值:若函数执行成功,则返回得到状态的进程id;若函数执行出错则在大部分情况下返回-1,在指定了一定参数时返回0
  • stat_loc:指定为一个int指针。函数将把获取到的进程终止时的状态存储在该指针指向的区域。若将参数设置为NULL,则表示父进程不关心子进程的终止状态,将会丢弃这部分内容。
  • pid:指定要等待其结束的进程的pid,函数将仅在它结束的时候返回。若指定为-1,此种情况下waitpidwait等效;若指定为0,此时将等待gid等同于调用进程组id的任一子进程;其他情况下等同于等待pid等于该参数绝对值的进程。
  • optionswaitpid函数的选项设定,可以参看下面参数一节。

其他相关解释

  • 两种函数的区别:①wait会使调用者阻塞,waitpid可以通过options参数实现不阻塞;②waitpid可以指定等待哪一个具体的进程结束。
  • zombie(僵死进程):当子进程终止时,不会完全地清除掉其所有状态,而是保存了至少pid终止状态使用的CPU时间总量等状态的信息。父进程可以通过wait或者waitpid来获取这些属性。若父进程没有处理这些信息,此时的子进程就称之为zombie(僵死进程)。
  • 信号系统相关:子进程终止时内核会向父进程发送SIGCHLD信号,父进程既可以以此来设计控制系统处理该信号,也可以选择忽略该信号(但子进程的终止状态会一直保存)。

参数

其中,options可以设置为以下四种情况相互做位或运算得到的值。

选项意义
0不设置任何特殊功能
WNOHANGpid指定的子进程不是立即可用的,则waitpid不会阻塞,而是直接返回0
WCONTINUED若实现支持作业控制,则由pid指定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态。
WUNTRACED若实现支持作业控制,而由pid指定的任一子进程已处于停止状态,并且其状态自停止以来还未报告过,则返回其状态。

其中,通过stat_loc参数拿到的值可以使用以下宏来检测其终止状态:

意义
WIFEXITED(stat_loc)若为正常终止的子进程返回的状态,则为真
WEXITSTATUS(stat_loc)获得正常终止的子进程传给exit等函数参数的低8位
WIFSIGNALED(stat_loc)若为异常终止的子进程返回的状态,则为真
WTERMSIG(stat_loc)获得使子进程异常终止的信号编号
WIFSTOPPED(stat_loc)若为当前暂停的子进程返回的状态,则为真
WSTOPSIG(stat_loc)获得使子进程暂停的信号编号
WIFCONTINUED(stat_loc)若在作业控制暂停后已经继续的子进程返回的状态,则为真
WCOREDUMP(stat_loc)若产生了终止进程的core文件,则为真

执行另一个程序

函数原型及解释

int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *)NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
  • 返回值:若函数执行成功无返回值,函数执行失败则返回-1
  • path:要执行的程序的路径。既可以是绝对路径也可以是相对路径。
  • file:要执行的程序的文件名。若指定的参数中包含“/”,则将他认为是一个路径,若不包含,则将在PATH环境变量中指定的目录下进行搜索。
  • arg ...:在此处罗列所有传递给程序的命令行参数,最后一个参数之后要附加一个(char *) NULL来表示结束。
  • argv:提供一个命令行参数的数组,数组的最后一个字符串一定为NULL
  • envp:提供一个环境变量数组。
  • fd:要执行的文件的文件描述符。

其他相关解释

  • 为何成功执行无返回值:因为exec族函数会将新的程序的代码段完全地代替子进程的代码,因此若成功执行了exec族函数,返回值已经没有意义,所以没有返回值。
  • 执行完exec族函数之后,子进程的pid并没有发生变化,从父进程继承的各种属性也依然有效。
  • 在以上函数中,但凡不需要指定envp[]参数的,都会将environ变量传入,当做环境变量来使用。

区分exec族函数

exec族函数的区分

  • exec后第一个字母
    • 凡是使用参数表的,均带有l,代表list
    • 凡是使用argv数组的,均带有v,代表vector
  • exec后第二个字母
    • 凡是使用路径的,此位均留空。
    • 凡是使用文件名的,此为均为p
  • exec后第三个字母
    • 凡是使用environ变量的,此位留空。
    • 凡是使用envp[]数组的,此位均为e
  • 使用文件描述符的仅有一个,就是fexecve

进程运行标识

函数原型及解释

int setuid(uid_t uid);      //设置进程实际用户ID
int setgid(gid_t gid);      //设置进程实际组ID
int seteuid(uid_t uid);     //设置进程有效用户ID
int setegid(gid_t gid);     //设置进程有效用户组ID
  • 返回值:函数若执行成功,则返回0,若失败则返回-1
  • uid:要设置的uid
  • gid:要设置的gid

进程调度

函数原型及解释

int nice(int incr);

int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int value);
  • 返回值:nicegetpriority若执行成功则返回新的nice值,若执行出错则返回-1setpriority若执行成功则返回0,出错则返回-1
  • incr:要增加的nice值。若incr取负数,则会减少nice值。当incr参数的取值超过指定范围,会自动将之设置为可以取到的最大值。
  • which:表示who属于那种类型。
  • who:指定要修改进程、进程组或用户的ID。 若指定为0,会根据参数解释为当前的进程ID、进程组ID或实际用户ID。
  • value:要改变多少。

参数

其中,which参数可取以下值:

选项意义
PRIO_PROCESS表示who是一个pid
PRIO_PGRP表示who是一个gid
PRIO_USER表示who是一个uid

会话

函数原型及解释

pid_t setsid(void);
pid_t getsid(pid_t pid);
  • 返回值:若成功,则返回进程组id,若执行失败则返回-1
  • pid:若指定为0,则使之返回调用进程的会话首进程的进程组ID。

其他相关解释

  • 调用setsid函数的进程若不是一个进程组的组长,此函数将创建一个新的会话,并:①将该进程变成新会话的首进程;②该进程成为一个新进程组的组长进程,进程组的ID为调用该函数进程的pid;③该进程没有控制终端,若在调用setsid之前该进程有一个控制终端,那么这种联系也被切断。
  • 可以使用这个程序来创建一个守护进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值