进程的查看、创建、会话、守护进程、结束

 

环境变量操作函数

char*getenv(const char* name);获取环境变量值

intputenv(cahr* str);形式为name=value的字符串,将其放到环境表中,如果name已经存在,则先删除其原来的定义

intsetenv(name,value,rewrite);略

intunsetenv(name);删除name的定义

 

setjmp  功能:设置非局部跳转的跳转点

longjmp功能:进行非局部跳转

C程序缺乏异常处理的语法,可以用非局部跳转处理C的程序异常

一.             通过命令查看进程信息:
ps id        查看进程ID 号     Ps –u查看进程状态     Ps –x
Ps –aux   查看所有进程  
USER 进程属主     PID进程ID      PPID 父进程      %CPU 进程占的CPU百分比     %MEM 占用内存百分比    NI 进程的NICE值,数值大表示较少占用CPU时间      VSZ 进程虚拟大小             RSS 驻留中页的数量        TTY 终端ID       WCHAN 正在等待的进程资源         START 启动进程的时间    TIME 进程消耗的CPU时间COMMAND  命令的名称和参数   STAT 进程状态:(运行状态 stat=R 等待状态 stat=S 停止状态 stat=T僵尸状态  stat=Z)
Ctrl+z 暂停进程  fg 恢复暂停的进程

. 获取进程和进程组ID的函数设置进程和进程组ID

Pid_tgetpid(void)   获得当前进程          ID

Pid_tgetppid(void)  获得当前进程的父进程  ID

Pid_tgetuid(void)   获得当前进程的实际用户ID

uid_tgeteuid(void)  获得当前进程的有效用户ID

Pid_tgetgid(void)   获得当前进程的用户组ID

Pid_tgetpgid(void)  获得进程所在的进程组ID

Pid_tgetpgrp(void)  获得进程所在的进程组ID  getpgid(0)=getpgrp()

Pstree命令 查看进程树

int setpgid(pid_t pid, pid_t pgid); 将目前进程的进程ID设为目前进程所属的组ID

功能:将参数pid指定进程所属的组ID设为参数pgid指定的组ID。如果参数pid 为0,则会用来设置目前进程的组ID,如果参数pgid为0,则由pid指定的进程ID将用作进程组ID。一个进程只能为它自己或它的子进程设置进程组ID。

返回值: 成功返回组识别码,错误返回-1,错误原因存于errno中。

错误代码

EINVAL 参数pgid小于0。

EPERM 进程权限不足,无法完成调用。

ESRCH 找不到符合参数pid指定的进程

参数:

pid用于指定要修改的进程id。如果为0,则指当前进程。

pgid用于指定新的进程组id。如果为0,则指当前进程。

setpgid(0,0)等价于setpgrp(0,0) // 使当前进程为新进程组的组长

setpgrp()

参数:

 

例子1获取进程和进程组ID的函数

5 int main()

{
 6    pid_t pid; 7
 8     if((pid=fork())<0)

      {
 9         printf("fork error!");
10     }

      else if (pid==0)

{
11         printf("The child process PID is %d.\n",getpid());
12         printf("The Group ID is %d.\n",getpgrp());
13         printf("The Group ID is %d.\n",getpgid(0));
14         printf("The Group ID is %d.\n",getpgid(getpid()));
15         exit(0);
16     }

18    sleep(3);
19     printf("The parent process PID is %d.\n",getpid());
20     printf("The Group ID is %d.\n",getpgrp());
22     return 0;
23}

//进程组id = 父进程id,即父进程为组长进程

 

例2.设置进程和进程组ID

  组长进程标识: 其进程组ID==其进程ID

  组长进程可以创建一个进程组,创建该进程组中的进程,然后终止

  只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关

  进程组生存期: 进程组创建到最后一个进程离开(终止或转移到另一个进程组)

 

一个进程可以为自己或子进程设置进程组ID

  setpgid()加入一个现有的进程组或创建一个新进程组

eg:父进程改变自身和子进程的组id

 5int main() {
 6    pid_t pid;
 8     if((pid=fork())<0) {
 9        printf("forkerror!");
10         exit(1);
11     }else if (pid==0) {
12         printf("The child process PID is %d.\n",getpid());
13         printf("The Group ID of child is %d.\n",getpgid(0));// 返回组id
14        sleep(5);
15         printf("The Group ID of child is changed to %d.\n",getpgid(0));
16         exit(0);
17     }
19     sleep(1);
20     setpgid(pid,pid); // 改变子进程的组id为子进程本身21    
22     sleep(5);
23     printf("The parent process PID is %d.\n",getpid());
24     printf("The parent of parent process PID is %d.\n",getppid());
25     printf("The Group ID of parent is %d.\n",getpgid(0));
26     setpgid(getpid(),getppid());// 改变父进程的组id为父进程的父进程
27    printf("The Group IDof parent is changed to %d.\n",getpgid(0));
29     return 0;
30}

 

. 创建进程

Pidfork(void)  创建进程

Pidvfork(coid) 创建进程,不把父进程的地址空间完全复制到子进程中,不会复制页表。

 

子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。

 vfork保证子进程先运行,在她调用exec或exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁

#definetypedef _STD_TPYE 

 

. 进程的状态

1. 可运行

2. 等待

3. 暂停

4. 僵尸  通过top命令查看进程状态

 

孤儿进程:父进程结束 子进程没结束,子进程由1号进程结束

僵尸进程:子进程结束但没完全释放内存(内存中的task_struct没有释放)。僵尸进程的父进程结束后就会被Init进程领养,最终被回收

避免僵尸进程:进程在终止前向父进程发送SIGCLD信号,父进程调用wait(int*status)或waitpid(pid_tpid, int*status,int opration);等待子进程的退出!如果子进程没有调用exit,则父进程wait进入阻塞!

 

五.进程的优先级  nice  setpriority    getpriority

优先级数值越,则优先级越

优先级由优先级别(PR+进程的谦让值(NI  联合确定。

PR值是由父进程继承而来,是不可修改的。

Linux提供nice系统调用修改自身的NI值;setpriority系统调用可以修改其他进程以及进程组的NI值。

#include<unistd.h>
#include <errno.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <stdio.h>
main()
{
    int nPr;
    if(nice(3) == -1)//进程的谦让值是3,优先级降低
    {
        perror("nice");
        exit(0);
    }
    errno = 0;
    nPr = getpriority(PRIO_PROCESS,getpid());//获取当前进程的谦让值
    if(errno != 0)
    {
        perror("getpriority");
        exit(0);
    }
    printf("priority is %d\n",nPr);
}

输出:

priority is 3

 

.结束旧进程打开新进程

1. Exec族函数:一共有6个,

#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ..., char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

功能:在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

通过exec函数执行其他的程序之后,新打开的进程覆盖当前程序进程(金蝉脱壳),没有返回值,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

 

2.system(constchar* comment);   system =fork+exec+waitpid

例子1---------------------------------
system("your_program");
printf("You can see me! ");
在你的程序执行完毕以后,会执行printf语句
例子2---------------------------------
exec("your_program");
printf("You can't see me!");由于exec将程序your_program代替了本身,因此程序不再会执行printf语句
---------------------------------
在Linux下,exec通常会和fork语句一起用。看下面的这个例子
--------------------------------------------
pid_t pid = fork();
if (pid < 0) {
    printf(“fork error!”);
    exit(-1);
   }
else if (pid == 0) {//这里是子进程
     printf("I'm son! ");
     printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());
     exec("your_program");//执行其它的程序
  }
else {//这里是父进程
     printf("i'm father!");
     printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());
     //exitlp(“\bin\ls”,”mingling”,NULL);  //如果有这句 以后的都不会执行
     wait();//等待子进程结束后返回
     exit(0);
  }

 execsystem的区别

(1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。

(2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。

总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. system不需要你fork新进程,已经封装好了。

 

.会话:

1. 概念:一个或多个进程组的集合,开始于用户登录,终止于用户退出。此期间所有进程都属于这个会话期

2. pid_t setsid(void)函数

功能:在子进程中调用setsid()后,子进程成为新会话首进程,且成为一个组长进程,其进程组id等于会话id

用法:该调用进程不是组长进程,则创建一个新会话。

  •该进程变成新会话首进程(sessionheader)

    •该进程成为一个新进程组的组长进程。

    •该进程没有控制终端,如果之前有,则会被中断

组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程...

获取会话 pid_t  getsid(pid_t pid)函数

返回值:成功返回会话首进程的进程组ID,出错返回-1

 5 int main() {
 6    pid_t pid;
 7
 8     if ((pid=fork())<0) {
 9        printf("forkerror!");
10         exit(1);
11     }else if (pid==0) {
12         printf("The child process PID is %d.\n",getpid());
13         printf("The Group ID of child is %d.\n",getpgid(0));
14         printf("The Session ID of child is %d.\n",getsid(0));
15         sleep(10);
16         setsid(); // 子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
17        printf("Changed:\n");
18         printf("The child process PID is %d.\n",getpid());
19         printf("The Group ID of child is %d.\n",getpgid(0));
20         printf("The Session ID of child is %d.\n",getsid(0));
21         sleep(20);
22         exit(0);
23     }
25     return 0;
26

  

. 守护进程

 1. 概念 Linux大多数服务都是通过守护进程实现的,完成许多系统任务

  0: 调度进程,称为交换进程(swapper),内核一部分,系统进程

  1: init进程, 内核调用,负责内核启动后启动Linux系统

  让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程

 

2. 守护进程编程步骤

  1. 创建子进程,父进程退出

    •所有工作在子进程中进行

    •形式上脱离了控制终端

  2. 在子进程中创建新会话

    •setsid()函数

    •使子进程完全独立出来,脱离控制

  3. 改变当前目录为根目录

    •chdir()函数

    •防止占用可卸载的文件系统

    •也可以换成其它路径

  4. 重设文件权限掩码

    •umask()函数

    •防止继承的文件创建屏蔽字拒绝某些权限

    •增加守护进程灵活性

  5. 关闭文件描述符

    •继承的打开文件不会用到,浪费系统资源,无法卸载

    •getdtablesize()

    •返回所在进程的文件描述符表的项数,即该进程打开的文件数目

 9 int main() {
10    pid_t pid;
11    int i,fd;
12    char *buf="This is a daemon program.\n";
13
14    if ((pid=fork())<0) {
15        printf("forkerror!");
16        exit(1);
17    }else if (pid>0// fork且退出父进程
18        exit(0);
19    
20     setsid();   // 在子进程中创建新会话。
21     chdir("/");  //
设置工作目录为根
22    umask(0);    // 设置权限掩码
23
     for(i=0;i<getdtablesize();i++) //getdtablesize返回子进程文件描述符表的项数
24        close(i);                // 关闭这些不将用到的文件描述符
25
26    while(1) {// 死循环表征它将一直运行
27 // 以读写方式打开"/tmp/daemon.log",返回的文件描述符赋给fd
28        if ((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0) {
29             printf("Open file error!\n");
30             exit(1);
31        }
32         write(fd,buf,strlen(buf)+1); // 将buf写到fd中
34        close(fd);
35        sleep(10);
36        printf("Neveroutput!\n");
37    }
39    return 0;
40 }

因为stdout被关掉了,所以“Never ouput!”不会输出。

查看/tmp/daemon.log,说明该程序一直在运行

 

九.进程的终止

正常终止

1.      从main返回

2.      调用exit(标准c函数)

3.      调用_exit或_Exit(系统调用)

4.      Int atexit(fun);2和3调用4实现,可以登记进程终止时收尾工作的函数,可以多次登记

5.      最后一个线程调用pthread_exit

异常终止

1.      调用abort

2.      接收信号终止

3.      最后一个线程对取消请求做处理响应

进程返回

1.      通常程序运行成功返回0,失败返回非0

2.       在shell中可以查看进程返回值(echo$?)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值