Linux 进程操作

news2024/11/19 2:30:55

文章目录

    • 进程的基本知识
      • 进程pid
      • 进程常用的函数
    • fork
    • wait和waitpid
    • exec函数簇
    • system函数
    • 信号处理signal函数
      • Linux的SIGUSR1SIGUSR2
    • 讨论

进程的基本知识

一个程序的执行称为一个进程,所有的代码都是在进程中执行的,进程是操作系统资源分配的基本单位。

在Linux中,除了内核的启动进程外,其他所有的进程都是由它的父进程通过fork函数调用产生的。

操作系统通过识别每一个进程的pid来识别每一个进程,pid在进程中是唯一的,可以重复利用(比如说前一个为pid=11的进程死掉了,那么pid=11的这个进程就可以分配给其他进程使用了:当pid达到最大限制时,内核会从头开始查找闲置的pid并使用最先找到的那一个作为新进程的id)
在这里插入图片描述

进程pid

操作系统中有一些进程的id是专用的:

  • id=0的进程为调度进程,调度进程是内核的一部分,并不执行任何磁盘上的程序
  • id=1的进程叫做 init 进程,init 进程是以超级用户权限运行着的普通用户进程,不是系统进程。

进程常用的函数

  • 头文件:#include<unistd.h>
  • __pid_t getpid (void):调用进程的进程ID
  • __pid_t getppid (void):调用进程的父进程ID
  • __uid_t getuid (void) :调用进程的实际用户ID
  • __uid_t geteuid (void):调用进程的有效用户ID
  • __gid_t getegid (void):调用进程的有效组ID

fork

  • fork用于进程的创建(完整的复制),使用fork()函数要引入头文件#include<unistd.h>,其函数原型为pid_t fork(void),fork返回值是pid_t,本质上是int类型。
  • 对于fork的返回值,如果是成功,子进程的id号会返回给父进程,并且把0返回给子进程;如果失败了,-1会返回给父进程,并且不会有子进程的创建,
//举一个简单的例子
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
	pid_t n=fork();
	if(n==0){//说明这是一个子进程
		printf("child:my pid=%d.,my ppid =%d\n",getpid(),getppid());
	}else if(n==-1){
		printf("程序创建错误\n");
		return -1;
	}else{
		sleep(1)//保证子进程可以执行完成
		printf("parent:my pid =%d,n=%d\n",getpid(),n);
	}
}
[cch@aubin s]$ gcc file.c
[cch@aubin s]$ ./a.out
child:my pid=3585.,my ppid =3584
parent:my pid =3584,n=3585
[cch@aubin s]$ 

以上的程序结果显示的是子进程先执行,而后父进程开始执行,但是实际上,在执行了fork之后,父进程与子进程都是同时存在的,也就是child:my pid=3585.,my ppid =3584语句和parent:my pid =3584,n=3585语句是同时执行的(并发执行),只是在执行父进程的时候进行了睡眠而已。

可能会出现以下的执行情况:

parent:my pid =3584,n=3585
[cch@aubin s]$ child:my pid=3585.,my ppid =3584

这是因为在并发执行的过程中,父进程先执行完毕,父进程执行完毕会返回给父进程的父进程,也就是系统调用,然后显示出来美元符号$,紧接着子进程也执行完毕,想要返回父进程会发现父进程已经结束了,只能返回上一层的上一层,也就是在美元符号后面显示。

wait和waitpid

  • 库函数exit(status)终止一进程,将进程占用的所有资源(内存、文件描述符等)归还内核。参数status为一整形变量,标识进程的退出状态。父进程可以通过系统调用wait()来获取该状态
  • 系统调用wait(&status)的目的有二:
    其一:如果子进程尚未调用exit()终止,那么wait()会挂起父进程直到有一个子进程终止
    其二:子进程的终止状态通过 wait()的 status 参数返回
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(){
	pid_t pid;
	char *message;
	int n;
	int exit_code;

	pid=fork();
	switch(pid){
		case -1:exit( -1);
		case 0:
			message="child child";
			n=5;
			exit_code=37;
			break;
		default:
			message="father father";
			n=3;
			exit_code=38;
			break;
	}
	for(;n>0;n--){
		puts(message);
		sleep(1);
	}

	if(pid){//父进程
		int stat_val;
		pid_t child_pid;
		child_pid=wait(&stat_val);//父进程等待子进程的结束

		printf("child finish:pid=%d\n",child_pid);
		if(WIFEXITED(stat_val))//子进程正常终止的情况
			printf("child exit with code %d\n",WEXITSTATUS(stat_val));
		else//子进程非正常终止的情况
			printf("child terminated abnormally\n");
	}
	exit(exit_code);
}
[cch@aubin s]$ gcc file.c 
[cch@aubin s]$ ./a.out
father father
child child
father father
child child
father father
child child
child child
child child
child finish:pid=4459
child exit with code 37
[cch@aubin s]$ 
  • pid_t wait(int *status)用于等待子进程结束,回收并且释放子进程资源;其中status用来保存子进程退出时的状态,如果status为NULL,表示忽略子进程退出时的状态;如果函数执行成功,返回子进程的进程号,失败则返回-1。
  • pid_t waitpid(pid_t pid ,int *status , int options)waitpid相比于wait多了两个参数,options=0时,同wait,阻塞父进程,等待子进程退出。

op

exec函数簇

  • 用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。
  • exec本身不是一个函数,而是有6种不同的函数可供使用,所以被称为exec函数簇
#include <unistd.h>
 
int execl( const char *pathname, const char *arg0, ... /* (char *)0 */ );
 
int execv( const char *pathname, char *const argv[] );
 
int execle( const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );
 
int execve( const char *pathname, char *const argv[], char *const envp[] );//进程映像的名字,映像所携带的参数,参数为0表示结束
 
int execlp( const char *filename, const char *arg0, ... /* (char *)0 */ );
 
int execvp( const char *filename, char *const argv[] );
 
6个函数返回值:若出错则返回-1,若成功则不返回值
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(){
	printf("exec:\n");
	execlp("ls","ls,0");
	printf("dome\n");
	exit(0);
}
[cch@aubin s]$ gcc file.c 
[cch@aubin s]$ ./a.out
exec:
dome
[cch@aubin s]$ 

system函数

  • int system(const char * command)system函数包含在头文件stdlib.h中,该函数会执行dos(windows系统) 或 shell(Linux/Unix系统) 命令,参数字符以command为命令名字。
  • 在windows系统中,system函数直接在控制台调用一个command命令。
    在Linux/Unix系统中,system函数会调用fork函数产生子进程,由子进程来执行command命令,并且使用wait阻塞父进程,直到子进程执行完毕后父进程继续执行
  • 命令执行成功返回0,执行失败返回-1。
#include<stdlib.h>
#include<stdio.h>

int main(){
	printf("system:\n");
	system("ls");
	printf("dome\n");
	exit(0);
}
[cch@aubin s]$ gcc file.c 
[cch@aubin s]$ ./a.out
system:
a.out  file.c
dome
[cch@aubin s]$ 
system("ls &");//表示把子进程放到后台去做

信号处理signal函数

Linux中的信号可以通过kill -l命令获取

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

void ouch(int sig){
	printf("signal:%d\n",sig);
	(void)signal(SIGINT,SIG_DFL);//将ctrl+c的对应信号处理函数恢复成默认的值,也就是中断当前程序的执行
}

int main(){
	(void)signal(SIGINT,ouch);//捕获信号ctrl+c,ouch是信号处理函数
	while(1){
		printf("hello world\n");
		sleep(1);
	}
}
  • void(* signal(int sig,void(* func)(int)))(int);指定使用sig指定的信号编号处理信号的方法。 其中void(* func)(int)表示的是一个带有int类型参数的函数指针;而signal本身的函数的返回值也是一个函数指针
  • 常用的sig包括(共32):
    • SIGINT:2,表示interrupt中断
    • SIGKILL:9,表示发送不可屏蔽的kill信号
    • SIGSEGV:11,表示segmentation violation,段违规
    • SIGUSR1:10,开放用户使用,默认处理进程终止
    • SIGUSR2:12,开放用户使用,默认处理进程终止
  • 参数func指定程序可以处理信号的三种方式之一:
    • 默认处理(SIG_DFL):信号由该特定信号的默认动作处理。
    • 忽略信号(SIG_IGN):忽略信号,即使没有意义,代码执行仍将继续。
    • 函数处理程序:定义一个特定的函数来处理信号。

除了在终端,也可以使用kil -2 pid来给对应的进程发送信号,表示系统向程序发送信号

Linux的SIGUSR1SIGUSR2

  • 当一个进程调用fork时,因为子进程在开始时复制父进程的存储映像,信号捕捉函数的地址在子进程中是有意义的,所以子进程继承父进程的信号处理方式。
  • 但是当子进程调用exec后,因为exec运行新的程序后会覆盖从父进程继承来的存储映像,那么信号捕捉函数在新程序中无意义,所以exec会将原本设置要捕捉的信号都更改为默认动作。

讨论

fotk语句执行成功后,会返回一个子进程的pid给父进程,并且返回0给子进程,由于父子进程并发执行,所以这个时候x的值为1或者-1都有可能,看父子进程谁先被调用。

尝试使用wait和waitid来同步父子进程的执行

child_pid=wait(&stat_val);//父进程等待子进程的结束
child_pid=wait(child_id,&stat_val,-1);//父进程等待子进程的结束

什么是execl函数簇

execl函数簇不是指一个函数,而是6个execl函数的统称,execl函数可以在不创建新进程的情况下,以一个全新的程序替换当前的程序的正文、数据和堆栈。

在一个程序中有多少种信号,怎么使用一个用户定义信号

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

void signal_handle(int sig_num){
	if(sig_num==SIGUSR1)
		printf("SIGUSR1-----\n");//在命令行中发送SIGUSR1信号可以执行signal_handle函数
	printf("signal_handle\n");
}

int main(){
	signal(SIGUSR1,signal_handle);
	while(1){
		printf("main------\n");
		sleep(1);
	}
	return 0;
}

在程序运行过程中,使用kill -s SIGUSR1 pid可以在命令行中发送对应的执行给程序,从而执行信号所对应的信号处理函数。

signal和sigaction的区别是什么

下面所指的signal都是指以前的older signal函数,现在大多系统都用sigaction重新实现了signal函数,不会出现空窗期。

  • signal在调用函数hander之前会有一段空窗期,在空窗期期间会先把hander指针恢复,这样会导致signal丢失信号,不能处理重复的信号;sigaction调用之后不会恢复hander指针,只有当再次调用sigaction修改handle指针才会恢复。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1112659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在启智平台上安装anconda(启智平台中新建调试任务,选的基础镜像中有conda的,就无需安装)

安装Anaconda3-5.0.1-Linux-x86_64.sh python版本是3.6 在下面的网站上找到要下载的anaconda版本&#xff0c;把对应的.sh文件下载下来 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 把sh文件压缩成.zip文件&#xff0c;拖到启智平台的调试页面 上传到平台上 un…

tlaplus-vscode插件使用记录

参考官方教程Getting Started 和油管视频A gentle intro to TLA 入门和命令 首先在vscode的扩展里面下载 然后新建一个squares.tla文件 在代码区域先输入module生成上下的分隔符&#xff0c;然后输入pluscal来调用模版&#xff0c;生成一堆预设代码 小改一下&#xff0c;编写一…

WGBS项目文章 | 在缺氮情况下,细胞自噬对植物DNA甲基化会产生怎样的影响?

发表单位&#xff1a;中国科学院江西省庐山植物园 发表日期&#xff1a;2023年9月13日 研究期刊&#xff1a;International Journal of Molecular Sciences&#xff08;IF: 5.6&#xff09; 研究材料&#xff1a;拟南芥 2023年9月13日&#xff0c;中国科学院江西省庐山植物…

【二:Spring-AOP】

目录 一 、AOP1、什么是AOP2、AOP的类型3、AOP&#xff08;底层原理&#xff09;&#xff08;1&#xff09;第一种有接口情况&#xff0c;使用JDK 动态代理&#xff08;2&#xff09;第二种没有接口情况&#xff0c;使用[CGLIB](https://so.csdn.net/so/search?qCGLIB&spm…

【MyBatis进阶】mybatis-config.xml分析以及try-catch新用法

目录 尝试在mybatis项目中书写增删改查 遇见问题&#xff1a;使用mybaties向数据库中插入数据&#xff0c;idea显示插入成功&#xff0c;但是数据库中并没有数据变化? MyBatis核心配置文件剖析 细节剖析&#xff1a; try-catch新用法 截至目前我的项目存在的问题&#xf…

Milk-V Duo快速上手

前言 &#xff08;1&#xff09;此系列文章是跟着汪辰老师的RISC-V课程所记录的学习笔记。 &#xff08;2&#xff09;该课程相关代码gitee链接&#xff1b; &#xff08;3&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;4&#xff09;最近实习需要…

Linux下shell编写脚本指南

文章目录 &#x1f31f; Linux下Shell编写脚本&#x1f34a; 为什么要使用Shell编写脚本&#x1f34a; Shell脚本的基础知识&#x1f389; 基本语法&#x1f389; 常用命令&#x1f389; 脚本文件的执行 &#x1f34a; Shell脚本的编写技巧&#x1f389; 脚本文件的注释&#x…

我总结了3个做好事情的万能动作,简单高效!

01 最近做公众号爆文项目&#xff0c;将用GPT写的文章发布在公众号赚取收益&#xff0c;爆了一篇之后&#xff0c;其他文章的数据并不理想。 同期做的闲鱼小项目很快出单&#xff0c;复盘出单经验&#xff0c;并将这些经验用到公众号爆文项目上&#xff0c;文章的数据又在逐渐好…

彩虹商城知识付费程序

1&#xff0c;下载程序&#xff0c; 2.宝塔新建站点&#xff0c;&#xff0c;自己的域名直接用&#xff08;别忘记解析了&#xff09;教程直接用IP测试。。 3.上传你下载的压缩包&#xff08;这里暂停一下&#xff0c;传好了继续&#xff09;有点慢等不了了&#xff0c; 4.上传…

基础MySQL的语法练习

基础MySQL的语法练习 create table DEPT(DEPTNO int(2) not null,DNAME VARCHAR(14),LOC VARCHAR(13) );alter table DEPTadd constraint PK_DEPT primary key (DEPTNO);create table EMP (EMPNO int(4) primary key,ENAME VARCHAR(10),JOB VARCHAR(9),MGR …

react配置 axios

配置步骤&#xff08;基本配置&#xff09;&#xff1a; 1.安装 axios cnpm install axios --save2.src/utils 新建一个 request.js文件(没有utils就新建一个目录然后再建一个request.js) 3.request代码如下&#xff1a; 这个是最简单的配置了&#xff0c;你可以根据自己的需…

【试题029】C语言Switch case语句小例题

1.题目&#xff1a; #include <stdio.h> void main(){ int i11,j; ji%3; switch(j){ case1: case2:printf("%d\n",j); break; default:printf("%d\n",i); } } 该段代码的输出结果是&#xff1f; 2.代码分析&#xff1a; int i 11, j;j …

切水果游戏开发1

多数无益&#xff0c;上代码&#xff1a; import pygame import random# 初始化pygame pygame.init()# 设置窗口尺寸 window_width 800 window_height 600 window_size (window_width, window_height) window pygame.display.set_mode(window_size)# 设置窗口标题 pygame.…

Leetcode—260.只出现一次的数字III【中等】

2023每日刷题&#xff08;三&#xff09; Leetcode—260.只出现一次的数字III 借助lowbit的解题思想 参考的灵茶山艾府大神的题解 实现代码 /*** Note: The returned array must be malloced, assume caller calls free().*/ int* singleNumber(int* nums, int numsSize, in…

python安装gdal

下载whl https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal 安装 pip install GDAL-3.1.4-cp36-cp36m-win_amd64.whl

uniapp 小程序优惠劵样式

先看效果图 上代码 <view class"coupon"><view class"tickets" v-for"(item,index) in 10" :key"item"><view class"l-tickets"><view class"name">10元优惠劵</view><view cl…

SLAM中相机姿态估计算法推导基础数学总结

相机模型 基本模型 内参 外参 对极几何 对极约束 外积符号 基础矩阵F和本质矩阵E 相机姿态估计问题分为如下两步: 本质矩阵 E t ∧ R Et^{\wedge}R Et∧R因为 t ∧ t^{\wedge} t∧其实就是个3x3的反对称矩阵&#xff0c;所以 E E E也是一个3x3的矩阵 用八点法估计E…

C语言求 n 阶勒让德多项式的值

完整代码&#xff1a; // 用递归法求 n 阶勒让德多项式的值 // 递归公式为&#xff1a; // n0,P(n)(x)1 // n1,P(n)(x)x // n>1,P(n)(x)((2*n-1)*x - P(n-1)(x) - (n-1)*P(n-2)(x)) / n #include<stdio.h>double func(int n,int x){if (n0){return 1;}if (n1){return…

爬虫基础 JS逆向

爬虫核心 1. HTTP协议与WEB开发 1. 什么是请求头请求体&#xff0c;响应头响应体 2. URL地址包括什么 3. get请求和post请求到底是什么 4. Content-Type是什么 &#xff08;1&#xff09;简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;…

行列转换:MySQL中的数据变形魔法

行转列 使用CASE函数聚合函数 SELECTMAX(CASE WHEN salesperson John THEN sales_amount END) AS John_Sales,MAX(CASE WHEN salesperson Alice THEN sales_amount END) AS Alice_Sales FROM sales_data;列转行 使用UNIO连接每列数据 SELECT product_id,store1 store,sto…