Linux进程控制

作者 : admin 本文共7834个字,预计阅读时间需要20分钟 发布时间: 2024-06-10 共2人阅读

目录

fork函数

1.问题 一

2.问题二

 3.问题三

fork常见用法

fork失败原因

进程退出

1.退出码

2.进程终止

进程终止常见方法:

 exit函数

_exit函数

进程等待

 1.为什么要进行进程等待

2.见一见猪跑

3.进程等待的方法

1.wait

2.waitpid


hello,my friend!今天,我们要开始学习新的内容了—>进程控制,进程控制涉及到操作系统如果管理和控制运行在计算机系统内的进程。我们将从fork函数,Linux进程退出,Linux进程等待,Linux进程替换等方面学习。那么接下来我们就开始敲黑板了!!

Linux进程控制插图

fork函数

话不多说,上码!!

#include 
#include 
#include 
int num = 0;
int wer = 100;
int main()
{
    pid_t fd = fork();
    if (fd < 0)
    {
        printf("fork fail!
");
    }
    else if (fd == 0)
    {
        while (2)
        {
            printf("我是子进程,wer:%d,&wer:%p
", wer, &wer);
            num++;
            if (num == 10)
            {
                printf("wer数据已被修改,修改是由子进程完成的
");
                wer=300;
            }
            sleep(1);
        }
    }
    else
    {
        while (1)
        {
            printf("我是父进程,wer:%d,&wer:%p
", wer, &wer);
            sleep(3);
        }
    }
    return 1;
}

在Linux上浅浅运行一下:

Linux进程控制插图(1)

这里,我有几个问题:

  1.  如何理解fork有两个返回值?
  2. 如何理解fork函数返回之后,给父进程返回子进程的pid,给子进程返回0这种现象?
  3. 如何理解一个fd会保存两个值这种现象?并让if else if同时进行?

1.问题 一

fork是操作系统创建子进程函数,函数在return返回时,已经把创建子进程该做的工作全部做完了(核心代码走就跑完了),也就是说:在return返回之前,子进程已经被创建好了,所以此时会存在父子两个进程。那么有两个返回值也就不奇怪了。

但是,是子进程先返回,还是父进程先返回,完全取决于调度器调度的顺序。

fork创建子进程会做如下的工作。

  • 创建子进程的PCB
  • 赋值
  • 创建子进程的地址空间
  • 赋值
  • 创建页表并建立映射关系
  • 将子进程放入进程队列list

2.问题二

在现实生活中,一个爹可能有不止一个儿子,但一个儿子仅有一个爹(亲爹)。孩子想要找到父亲很简单,但是父亲想要找到儿子,得需要儿子的名字。

人亦如此,进程亦如此,所以父进程返回子进程的pid,子进程只需要返回0即可!!

 3.问题三

返回的本质就是写入,所以谁先返回,谁就先写入fd。因为进程具有独立性,所以会发生写时拷贝

(创建一块新的内存空间,对数据进行修改)。

既然有两个进程,并且两个进程的fd值不同,那么if,else if同时进行就很正常了。

fork常见用法

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
  • 个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

fork失败原因

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制 

进程退出

1.退出码

我们在写C/C++代码时,总喜欢在main函数最后return 0是什么意思?为什么总是返回0呢?

 这里返回的0在系统中我们称为进程退出时的退出码,可以用来标定我们进程退出时的结果是否正确。

我们写代码是为了完成翁某项事请,那么我们怎么知道任务完成的如何呢?就是靠进程退出码

上码:

#include
int addfromto(int from,int to)
{
    int sum=0;
    for(int i=from;i<=to;i++)
    {
        sum+=i;

    }
    return sum;

}
int main()
{
    int ret=addfromto(1,100);
    if(ret==5050)
        return 0;
    else
        return 1;
}

 Linux进程控制插图(2)

这里我们隆重介绍一下:echo $?

./my.out  运行的一个进程。

echo $?: 用于记录最近的进程在命令行中运行的退出码,?是一个相当于一个环境变量。

Linux进程控制插图(3)

如何设定我们退出时的退出码呢?

  1. 如果不关心进程退出码,返回0即可。
  2. 如果我们要关心进程退出时的退出码,要返回特定的数组标识特定的错误。

失败的原因有很多种,成功的情况只有一种。人们仅关心失败的原因,不关心成功的原因。

一般,我们用0表示成功,!0表示失败 

但是,单纯的数字对计算机友好,但对人类不友好。所以,退出码要有对应的文字描述。1.可以自定义,2.使用系统中的退出码集。

系统进程退出码集

0:Success
1:Operation not permitted
2:No such file or directory
3:No such process
4:Interrupted system call
5:Input/output error
6:No such device or address
7:Argument list too long
8:Exec format error
9:Bad file descriptor
10:No child processes
11:Resource temporarily unavailable
12:Cannot allocate memory
13:Permission denied
14:Bad address
15:Block device required
16:Device or resource busy
17:File exists
18:Invalid cross-device link
19:No such device
20:Not a directory
21:Is a directory
22:Invalid argument
23:Too many open files in system
24:Too many open files
25:Inappropriate ioctl for device
26:Text file busy
27:File too large
28:No space left on device
29:Illegal seek
30:Read-only file system
31:Too many links
32:Broken pipe
33:Numerical argument out of domain
34:Numerical result out of range
35:Resource deadlock avoided
36:File name too long
37:No locks available
38:Function not implemented
39:Directory not empty
40:Too many levels of symbolic links
41:Unknown error 41
42:No message of desired type
43:Identifier removed
44:Channel number out of range
45:Level 2 not synchronized
46:Level 3 halted
47:Level 3 reset
48:Link number out of range
49:Protocol driver not attached
50:No CSI structure available
51:Level 2 halted
52:Invalid exchange
53:Invalid request descriptor
54:Exchange full
55:No anode
56:Invalid request code
57:Invalid slot
58:Unknown error 58
59:Bad font file format
60:Device not a stream
61:No data available
62:Timer expired
63:Out of streams resources
64:Machine is not on the network
65:Package not installed
66:Object is remote
67:Link has been severed
68:Advertise error
69:Srmount error
70:Communication error on send
71:Protocol error
72:Multihop attempted
73:RFS specific error
74:Bad message
75:Value too large for defined data type
76:Name not unique on network
77:File descriptor in bad state
78:Remote address changed
79:Can not access a needed shared library
80:Accessing a corrupted shared library
81:.lib section in a.out corrupted
82:Attempting to link in too many shared libraries
83:Cannot exec a shared library directly
84:Invalid or incomplete multibyte or wide character
85:Interrupted system call should be restarted
86:Streams pipe error
87:Too many users
88:Socket operation on non-socket
89:Destination address required
90:Message too long
91:Protocol wrong type for socket
92:Protocol not available
93:Protocol not supported
94:Socket type not supported
95:Operation not supported
96:Protocol family not supported
97:Address family not supported by protocol
98:Address already in use
99:Cannot assign requested address
100:Network is down
101:Network is unreachable
102:Network dropped connection on reset
103:Software caused connection abort
104:Connection reset by peer
105:No buffer space available
106:Transport endpoint is already connected
107:Transport endpoint is not connected
108:Cannot send after transport endpoint shutdown
109:Too many references: cannot splice
110:Connection timed out
111:Connection refused
112:Host is down
113:No route to host
114:Operation already in progress
115:Operation now in progress
116:Stale file handle
117:Structure needs cleaning
118:Not a XENIX named type file
119:No XENIX semaphores available
120:Is a named type file
121:Remote I/O error
122:Disk quota exceeded
123:No medium found
124:Wrong medium type
125:Operation canceled
126:Required key not available
127:Key has expired
128:Key has been revoked
129:Key was rejected by service
130:Owner died
131:State not recoverable
132:Operation not possible due to RF-kill
133:Memory page has hardware error

2.进程终止

进程退出的场景:

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

进程终止常见方法:

 正常退出,可以在命令行中使用echo $?查询退出码

  • 从main函数中返回
  • 在任何地方调用exit
  • 在任何地址调用_exit 

异常退出

  • 使用Ctrl+Z组合键,终止进程
  • 使用kill -9 命令杀死进程 

 exit函数

头文件:stdlib.h

功能:为退出程序的函数

用法:

  •        exit(1);  为异常退出     //只要括号内数字不为0都表示异常退出
  •        exit(0);  为正常退出

注意:括号内的参数都将返回给操作系统;

          return() 是返回到上一级主调函数,不一定会退出程序;

实例1

#include
#include
int main()
{
    printf("hello world
");
    exit(1);

}

 exit和return的区别

  • exit是一个C语言函数,return是一个关键字。
  • exit是系统调用级别的,表示整个进程的结束;return是语言级别的,表示调用堆栈的返回。
  • exit表示进程的退出,return函数的退出。
  • exit函数结束进程,删除进程使用的内存空间,并将进程的状态返回给操作系统(一般是用0表示正常终止,非0表示异常终止);return是结束函数的执行,将函数的执行信息传其他调用函数使用
  • 非主函数中调用exit和return区别很明显,但是在main函数中调用区分不大,多数情况效果一样

_exit函数

_exit是操作系统接口

函数原型:

#include  
void _exit(int status);  

用法:

  •       _ exit(1);  为异常退出     //只要括号内数字不为0都表示异常退出
  •        _exit(0);  为正常退出

实例: 

#include
#include

int main()
{
    printf("hello world
");
    _exit(1);

}

exit_和exit的区别

exit的底层是用exit实现的,这两个的区别在于:调用exit会主动刷新缓冲区,调用_exit不会主动刷新缓冲区

代码:

#include
#include
int main()
{
    printf("hello world");
    sleep(1);
    _exit(1);

}

 Linux进程控制插图(4)

#include
#include
#include
int main()
{
    printf("hello world");
    sleep(1);
    exit(1);

}

Linux进程控制插图(5)

Linux进程控制插图(6)

进程等待

 1.为什么要进行进程等待

  • 之前讲过,子进程退出,父进程如果不管不顾,就可能造成’僵尸进程’的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kil 9 也无能为力,因为谁也没有办杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

2.见一见猪跑

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    int cnt = 10;
    pid_t id = fork();
    if (id == 0)
    {
        while (cnt)
        {
            printf("我是子进程,pid:%d,ppif:%d
", getpid(), getppid());
            sleep(1);
            cnt--;
        }
        exit(1);
    }
    sleep(15);
    pid_t idd = wait(nullptr);
    if (id > 0)
    {
        printf("idd:%d", id);
    }
}

运行一下:

Linux进程控制插图(7)

3.进程等待的方法

1.wait

#include
#include
pid_t wait(int*status); 

返回值:
成功返回被等待进程pid,失败返回-1参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为nullptr

2.waitpid

这个系统调用接口有些复杂。

 #include

pid_t waitpid (pid_t pid, int* statusp, int options);

返回值:如果成功,则返回子进程的PID,如果options为WNOHANG,则返回0,如果发生其他错误,则返回-1。 

pid_t pid:要等待子进程的PID。 

 int options:

  • 0:若pid指定的子进程没有结束,父进程挂起。若正常结束,则返回该子进程的ID。
  • WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。正常结束,则返回该子进程的ID。(异常返回-1)

我们先尝试写一下代码:

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    int cnt = 10;
    pid_t id = fork();
    if (id == 0)
    {
        while (cnt)
        {
            printf("我是子进程,pid:%d,ppif:%d
", getpid(), getppid());
            sleep(1);
            cnt--;
        }
        exit(1);
    }
    int status=0;
    pid_t idd = waitpid(id,&status,0);
    if (idd > 0)
    {
        printf("idd:%d,stutus:%d
", id,status);
    }
}

Linux进程控制插图(8)

这里的status为什么是256呢?

这里就和waitpid的第二个参数有关了,我们好好分析一下:


int *status:

这是一个输出型参数由操作系统填充。输出子进程退出时的退出信息:退出码。退出信号等。

我们先从如何使用入手:

int status=0;
// 现在调用处;定义一个int类型的整数
pid_t id=waitpid(fd,&status,0);
//然后将指针传入

这里我们根据status会取到子进程退出信号,退出码core dump等等信息。为什么一个变量可以获得这么多的信息呢?因为他是一个4字节类型,有32位比特位,所以我们分区块,获取数据。

status不能只简单的当做整型来看待,要当做位图来看(只研究status低16位),如图所示:

Linux进程控制插图(9)

  1. 当进程正常终止时,终止信号为0,仅注意退出码会即可。
  2. 当程序中出现错误,如:除零错误,野指针问题。操作系统会向进程发送相关信号,来杀死进程,此时仅注意终止信号即可(因为进程没有正常退出,退出码无意义)。 

所以:上面的代码应该这样写:

#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    int cnt = 2;
    pid_t id = fork();
    if (id == 0)
    {
        while (cnt)
        {
            printf("我是子进程,pid:%d,ppif:%d
", getpid(), getppid());
            sleep(1);
            cnt--;
        }
        exit(1);
    }
    int status=0;
    pid_t idd = waitpid(id,&status,0);
    if (idd > 0)
    {
        printf("idd:%d,sig number:%d,chid exist code:%d
", id,(status&0x7f),((status>>8)&0xff00));
    }
}

 Linux进程控制插图(10)

 写在最后:

因作者水平有限,难免会有错误,请各位批评指正!!

Linux进程控制插图(11)

本站无任何商业行为
个人在线分享 » Linux进程控制
E-->