【无标题】

2.7 特殊的进程(PID:0、1、2)

1> 0号进程(idel进程):他是linux系统启动后的第一个进程,这个进程也叫做空闲进程,当没有其他进程执行时,会执行该进程

2> 1号进程(init进程):他是在0号进程产生,这个进程主要用于硬件的初始化工作,也是其他进程的父进程,可以完成对一些进程的收尸工作

3> 2号进程(kthreadd进程):也称为调度进程,这个进程也是由0号进程产生,他的任务是完成任务调度工作

2.8 进程的相关命令

1> 查看进程的指令:ps

ps -ef :可以显示进程之间的关系

UID         PID   PPID  C STIME TTY          TIME CMD

root          1      0  0 五月21 ?     00:01:16 /sbin/init splash
root          2      0  0 五月21 ?     00:00:00 [kthreadd]
root          3      2  0 五月21 ?     00:00:00 [rcu_gp]
root          4      2  0 五月21 ?     00:00:00 [rcu_par_gp]
root          6      2  0 五月21 ?     00:00:00 [kworker/0:0H-kb]
root          9      2  0 五月21 ?     00:00:00 [mm_percpu_wq]
root         10      2  0 五月21 ?     00:00:14 [ksoftirqd/0]
root         11      2  0 五月21 ?     00:02:05 [rcu_sched]
root         12      2  0 五月21 ?     00:00:04 [migration/0]
root         13      2  0 五月21 ?     00:00:00 [idle_inject/0]
root         14      2  0 五月21 ?     00:00:00 [cpuhp/0]
root         15      2  0 五月21 ?     00:00:00 [kdevtmpfs]
root         16      2  0 五月21 ?     00:00:00 [netns]
root         17      2  0 五月21 ?     00:00:00 [rcu_tasks_kthre
UID:用户的id号
PID:当前进程的id号
PPID:当前进程的父进程的pid
C:目前不用关注
STIME:运行日期
TTY:如果显示的是问号,则说明没有终端与该进程对应
TIME:时间
CMD:进程名

ps -ajx:查看进程的运行状态

 PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
     0      1      1      1 ?            -1 Ss       0   1:16 /sbin/init splash
     0      2      0      0 ?            -1 S        0   0:00 [kthreadd]
     2      3      0      0 ?            -1 I<       0   0:00 [rcu_gp]
     2      4      0      0 ?            -1 I<       0   0:00 [rcu_par_gp]
     2      6      0      0 ?            -1 I<       0   0:00 [kworker/0:0H-kb]
     2      9      0      0 ?            -1 I<       0   0:00 [mm_percpu_wq]
     2     10      0      0 ?            -1 S        0   0:14 [ksoftirqd/0]
     2     11      0      0 ?            -1 I        0   2:05 [rcu_sched]
     2     12      0      0 ?            -1 S        0   0:04 [migration/0]
PPID:父进程号
PGID:组id号
SID:会话ID号
TPGID:如果值为-1说明是守护进程(服务进程)
STAT:进程的状态

ps -aux:显示进程在计算机资源的占比

ubuntu@ubuntu:~/c_language/day1$ ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.2 225904  5920 ?        Ss   五月21   1:16 /sbin/init splash
root          2  0.0  0.0      0     0 ?        S    五月21   0:00 [kthreadd]
root          3  0.0  0.0      0     0 ?        I<   五月21   0:00 [rcu_gp]
root          4  0.0  0.0      0     0 ?        I<   五月21   0:00 [rcu_par_gp]
root          6  0.0  0.0      0     0 ?        I<   五月21   0:00 [kworker/0:0H-kb]
root          9  0.0  0.0      0     0 ?        I<   五月21   0:00 [mm_percpu_wq]
root         10  0.0  0.0      0     0 ?        S    五月21   0:14 [ksoftirqd/0]
root         11  0.0  0.0      0     0 ?        I    五月21   2:05 [rcu_sched]
root         12  0.0  0.0      0     0 ?        S    五月21   0:04 [migration/0]
root         13  0.0  0.0      0     0 ?        S    五月21   0:00 [idle_inject/0]

2> kill指令:给进程发信号

kill -l:可以查看系统提供的信号都有哪些

 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
前30个是稳定信号,下面的是不稳定信号
 
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX    

2) SIGINT:中断进程,相当于ctrl + c
19) SIGSTOP:暂停进程,相当于ctrl + z
9) SIGKILL:杀死一个进程
18) SIGCONT:继续一个进程执行

kill -信号号 pid(给进程发射信号)

killall 进程名:杀死所有的该进行名对应的进程,例如killall a.out

 3> 查看进程号的指令:pidof 进程名

 4> top:动态查看进程状态

 

5> 查看能够创建多少个进程:cat /proc/sys/kernel/pid_max

6> pstree:显示进程树的关系

 

四、进程的创建

进程的创建过程,是通过拷贝父进程得到的,进程在内核空间是通过task_struct结构体表述的,新进程在创建过程中,直接拷贝父进程的该结构体得到,只需要稍作修改即可,保留了父进程大部分的遗传信息,这个拷贝过程,被封装在fork函数中。

4.1 进程创建函数fork

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

       pid_t fork(void);
功能:拷贝父进程得到一个子进程(创建子进程)
参数:无
返回值:成功
        父进程会得到子进程的pid,子进程会得到0
        失败:给父进程返回-1,子进程创建失败,并置位错误码

4.2 使用fork创建进程(不关注返回值)

#include<myhead.h>

int main(int argc, const char *argv[])
{
    sleep(7);        //休眠7秒数

    fork();


    while(1);

    return 0;
}

 说明:在程序执行前7秒是,只有父进程,并处于休眠状态,pid为:127343, 7秒后,执行fork时,会拷贝127343进程得到进程127346进程,此时,相当于a.out就有两个进程,这两个进程,同时执行while(1),此时,才会造成两个进程都处于运行态

#include<myhead.h>

int main(int argc, const char *argv[])
{

    fork();
    fork();
    fork();


    while(1);

    return 0;
}

 

没fork一次,会产生两个进程,所以在主函数中,三次fork会得到2的3次方=8个进程,进程关系如上图所示

注意:

1、父进程会拷贝一份资源给子进程,父进程和子进程的资源是一致的,但是,子进程不会执行fork之前的语句

2、父子进程先执行哪一个是不确定,由cpu调度机制来决定

3、子进程会拷贝父进程的虚拟空间,父子进程的0--3G的用户空间内容完全一致,3--4G内核空间共享

4.3 使用fork创建进程(关注返回值)

#include<myhead.h>

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

    pid = fork();        //生成一个子进程

    //对pid进行判断
    if(pid < 0)           //说明创建子进程失败
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)      //说明这段是子进程的内容
    {
        printf("这是子进程\n");   //子进程单独执行
    }else
    {
        printf("这是父进程\n");    //父进程单独执行
    }

    printf("hello world\n");    //父子进程同时执行
    while(1);

    return 0;
}

 

4.4 fork出来的进程与原进程的执行先后顺序

子进程和父进程执行没有先后顺序,原因是系统进行时间片轮询机制,上下文切换原则,调度到哪个就执行哪个

#include<myhead.h>

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

    if(pid<0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        while(1)
        {
            sleep(1);
            printf("好好学习\n");       //子进程内容
        }
    }else
    {
        while(1)
        {
            sleep(1);
            printf("天天向上\n");    //父进程内容
        }
    }


    return 0;
}

4.5 fork出来的进程与原进程内存问题

#include<myhead.h>
int num = 520;             //定义全局变量


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

    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        printf("这是子进程开始...\n");
        printf("num = %d, &num = %p\n", num, &num);

        num = 1314;
        printf("num = %d, &num = %p\n", num, &num);
        printf("这是子进程结束\n");
    }else
    {
        sleep(1);
        printf("这是父进程开始...\n");
        printf("num = %d, &num = %p\n", num, &num);

        num = 999;
        printf("num = %d, &num = %p\n",num, &num);
        printf("这是父进程结束...\n");
    }

    return 0;
}

 4.6 父子进程的进程号获取

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

       pid_t getpid(void);
       pid_t getppid(void);
功能:获取自己的进程号或父进程号
参数:无
返回值:成功返回自己的进程号和父进程进程号,没有失败
#include<myhead.h>

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

    //判断父子进程
    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        printf("这是子进程:pid = %d, ppid = %d\n", getpid(), getppid());        //获取进程号、父进程号
    }else
    {
        printf("这是父进程:pid=%d, ppid=%d,child pid = %d\n", getpid(),\      //获取进程号、父进程号,输出子进程号
                getppid(), pid);
    
    }

    while(1);

    return 0;
}

4.7 进程的退出(exit/_exit)

在main函数中,使用return也可以结束一个进程

可以调用相关函数,结束进程

exit函数是库函数,会刷新缓冲区

_exit()是系统调用,也是结束进程,但是不会刷新缓冲区

1、       #include <stdlib.h>

       void exit(int status);
功能:结束进程,并刷新缓冲区
参数:退出时的状态:
            EXIT_SUCCESS  成功
            EXIT_FAILURE  失败
返回值:无

2、       #include <unistd.h>

       void _exit(int status);
功能:结束进程,不刷新缓冲区
参数:退出时的状态:
            EXIT_SUCCESS  成功
            EXIT_FAILURE  失败
返回值:无
#include<myhead.h>

int main(int argc, const char *argv[])
{
    pid_t pid = fork();    //创建进程

    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        printf("333333333333333\n");    //会执行,因为有'\n'刷新缓冲区
        printf("11111111111111");    //不会打印,原因是没有'\n'并使用_exit退出
        _exit(EXIT_SUCCESS);        //结束进程时不会刷新缓冲区
        printf("444444444444444444\n");   //不会被打印,因为子进程已经结束
    }else
    {
        printf("22222222222222");        //会被打印, 因为使用的是exit退出,刷新缓冲区
        exit(EXIT_SUCCESS);      //该函数结束进程时会刷新缓冲区
        printf("55555555555555555555555\n"); //不会被打印,因为父进程已经结束
    }


    while(1);



    return 0;
}

4.8 进程资源回收(wait/waitpid)

1> 僵尸进程:子进程已经结束,但是,父进程没有为其收尸,那么子进程就是僵尸进程

2> 孤儿进程:父进程已经结束,子进程就变成了孤儿进程,被init进程收养,子进程结束后,有init进程为其收尸

    #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *wstatus);
       功能:阻塞等待回收子进程资源
       参数:子进程使用exit/_exit退出时的状态,一般不用接受,直接传进去NULL就行啦
       返回值:成功返回回收了资源的子进程的pid,失败返回-1,并置位错误码
       

       pid_t waitpid(pid_t pid, int *wstatus, int options);
        功能:可以阻塞也可以不阻塞等待回收子进程号为pid的进程资源
        参数1:要回收的子进程的pid号
                <-1:回收绝对值与pid相等的同一组id下的所有子进程
                =-1:回收任意一个子进程(常用)         waitpid(-1, NULL, 0)  <==>   wait(NULL)
                =0:回收多个pid中的一个,与当前进程所在的同一组的任意一个进程(常用)
                >0:回收值为pid的进程(回收指定的进程)
                
        参数2:子进程使用exit/_exit退出时的状态,一般不用接受,直接传进去NULL就行啦
        参数3:0表示阻塞等待回收资源,WNOHANG则非阻塞等待回收资源
        返回值:成功返回回收了资源的子进程的pid,如果是非阻塞等待,并且没有子进程结束,则返回0
                失败返回-1并置位错误码

4.9 如果不使用wait或waitpid会产生僵尸进程

#include<myhead.h>

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

    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        printf("子进程开始...\n");

        printf("hello world\n");

        printf("子进程结束....");
        exit(EXIT_SUCCESS);       //子进程结束
    }else
    {
        printf("这是父进程,我是老不死的\n");

        while(1);
    }

    return 0;
}

 4.10 使用wait或waitpid回收资源

#include<myhead.h>
#include <sys/wait.h>

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

    if(pid < 0)
    {
        perror("fork error");
        return -1;
    }else if(pid == 0)
    {
        printf("子进程开始...\n");

        printf("hello world\n");

        printf("子进程结束....");
        exit(EXIT_SUCCESS);       //子进程结束
    }else
    {
        printf("这是父进程,我是老不死的\n");


        //wait(NULL);    //阻塞等待子进程结束,并为其收尸
        waitpid(pid, NULL, 0);    //阻塞等待子进程的结束

        while(1);
    }

    return 0;
}

 练习:一个父进程创建出两个子进程(一共3个进程),然后在父进程中为两个子进程进行收尸工作

#include <myhead.h>
#include <sys/wait.h>

int main(int argc, const char *argv[])
{
    pid_t pid1,pid2;
    pid1 = fork();          //生成第一个子进程
    if(pid1<0){
        perror("fork pid1 error");
        return -1;
    }else if(pid1 == 0){
        printf("子进程1开始\n");
        printf("子进程1结束\n");
        exit(EXIT_SUCCESS);
    }else{
        printf("这是父进程\n");
        pid2 = fork();        //生成第二个子进程
        if(pid2<0){
            perror("fork pid2 error");
            return -1;
        }else if(pid2 == 0){
            printf("子进程2开始\n");
            printf("子进程2结束\n");
            exit(EXIT_SUCCESS);
        }else{
            waitpid(pid1,NULL,0);       //阻塞等待pid1的结束
            waitpid(pid2,NULL,0);       //阻塞等待pid2的结束
            while(1);
        }
    }
    return 0;
}

作业:

使用父子进程完成拷贝文件

五、守护进程(daemon)

所谓守护进程,其实就是一个服务,随着系统的启动而启动,随着系统的结束而结束,不依赖于终端而存在

因为在终端上运行的进程,随着终端被关闭,进程也随之关闭

而且一般不会被打断,需要将其执行放在根目录下

5.1 守护进程创建流程

1> 创建一个孤儿进程

2> 重新设置孤儿进程的SID(会话id)和组id(让进程独立出来)

3> 修改守护进程的目录为根目录,当然,也可以不修改,但是,一旦所在的目标被删除,该守护进程也被删除了

4> 修改创建文件的掩码给最大权限(umask(0))

5> 将标准输入、标准输出、标准出错重定向到某个文件(dup2)

6> 对应的文件就是日志文件

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{    
    int src_fd, dst_fd, n;   
     char buf[BUF_SIZE];   
     pid_t pid;    
    if (argc != 3) 
    {        
        fprintf(stderr, "Usage: %s <src_file> <dst_file>\n", argv[0]);        
        exit(EXIT_FAILURE);   
      }    
    src_fd = open(argv[1], O_RDONLY);    
    if (src_fd == -1)
    {       
     perror("open");       
     exit(EXIT_FAILURE);   
     }   
     dst_fd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);    
    if (dst_fd == -1) 
    {       
     perror("open");       
     exit(EXIT_FAILURE);   
     }   
     pid = fork();    
    if (pid == -1)
     {       
     perror("fork");       
     exit(EXIT_FAILURE);   
     }    
    if (pid == 0)
     {  // 子进程       
     while ((n = read(src_fd, buf, BUF_SIZE)) > 0) 
        {            
        if (write(dst_fd, buf, n) != n)
             {               
                 perror("write");                
                exit(EXIT_FAILURE);           
                 }        }       
             close(dst_fd);       
             exit(EXIT_SUCCESS);   
                 }
         else 
        {  // 父进程       
         waitpid(pid, NULL, 0);       
         close(src_fd);       
         exit(EXIT_SUCCESS);   
         }}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值