Linux C进程总结(下)

来源:互联网 发布:阿里云服务器终端密码 编辑:程序博客网 时间:2024/05/20 20:43

守护进程

守护进程时指在后台运行的、没有控制终端与之相连的进程。他独立于控制终端,通常周期性地执行某种任务。Linux大多数服务器就是用守护进程方式实现的。

实现方法

  1. 让程序在后台运行。
    进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
  2. 脱离控制终端,登录会话和进程组 setsid()
    当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
  3. 禁止进程重新打开控制终端
    进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端。
  4. 关闭不再需要的文件描述符
    进程不再需要文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们: for(i=0;i 关闭打开的文件描述符close(i);
  5. 改变当前工作目录
    进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。
  6. 重设文件创建掩模
    进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
  7. 处理SIGCHLD信号
    处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。

    简单实现代码

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <signal.h>#include <sys/param.h>#include <sys/stat.h>#include <time.h>#include <syslog.h>int init_daemon(void) {    int pid;    int i;    /*忽略终端I/O信号,STOP信号*/    signal(SIGTTOU,SIG_IGN);    signal(SIGTTIN,SIG_IGN);    signal(SIGTSTP,SIG_IGN);    signal(SIGHUP,SIG_IGN);    pid = fork();    if(pid > 0) {        exit(0);        /*结束父进程,使得子进程成为后台进程*/    } else if(pid < 0) {        return -1;    }    /*创建一个新的进程组,在这个新的组中,子进程成为进程组的首进程,以使该进程脱离所有终端*/    setsid();    /*再创建一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法再打开一个新的终端*/        pid = fork();    if(pid > 0) {        exit(0);    } else if(pid < 0) {        return -1;    }    /*关闭所有从父进程继承的不再需要的文件描述符*/    for(i = 0;i < NOFILE;close(i++));    /*改变工作目录,使得进程不与任何文件系统联系*/    chdir("/");    /*将文件屏蔽字设置为0*/    umask(0);    /*忽略SIGCHLD信号*/    signal(SIGCHLD,SIG_IGN);    return 0;}int main(int argc, char *argv[]) {    time_t now;    init_daemon();    syslog(LOG_USER|LOG_INFO,"测试守护进程!");    while(1) {        sleep(8);        time(&now);        syslog(LOG_USER|LOG_INFO,"系统时间:\t%s\t\t\n",ctime(&now));    }    return 0;}

fork和vfork的区别

相同

  1. 都可以用来创建一个新进程
  2. 都是调用一次,返回两次

不同

  1. fork创建一个子进程的时候,子进程只是完整复制子进程的资源。这样得到的子进程独立于父进程,具有良好的并发性。而使用vfork创建子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork创建的子进程共享父进程的地址空间,也就是说子进程完全运行在父进程的地址空间上。子进程对该地址空间中任何数据的修改同样为父进程所见。
  2. 使用fork创建一个子进程时,哪个进程先运行取决于系统的调度算法。而vfork一个进程时,vfork保证子进程先运行,当它调用exec或exit后,父进程才可以被调度运行。如果在调用exec或exit之前子进程要依赖父进程的某几个行为,就会导致死锁。

进程退出

正常退出

  1. 在main函数中执行return
  2. 调用exit
  3. 调用_exit
    异常退出
  4. 调用abort函数
    2.进程收到某个信号,该信号使进程终止进程收到某个信号,该信号使进程终止

执行新进程

#include<unistd.h>int exevc(const char *pathname,char *const argv[]);

参数filename字符串所代表的文件路径,argv参数是一个以空指针结尾的数组,即该数组里面存放的是命令执行需要的参数,最后一个元素是NULL 。argv[0]存放名字,而参数在第二个及以后存放

#include<unistd.h>int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

参数filename字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
该函数如果执行成功则无返回值,执行失败则直接返回-1,失败原因存于errno 中。

#include <unistd.h>int execl(const char *path, const char *arg, ...);

第一个参数path字符指针所指向要执行的文件路径, 接下来的参数代表执行该文件时传递的参数列表:argv[0],argv[1]… 最后一个参数须用空指针NULL作结束。
该函数如果执行成功则无返回值,执行失败则直接返回-1,失败原因存在errno中。

#include<unistd.h>int execvp(const char *file ,char * const argv []);

execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名, 找到后便执行该文件, 然后将第二个参数argv 传给该欲执行的文件
该函数如果执行成功则无返回值,执行失败则直接返回-1,失败原因存在errno中。

#include<unistd.h>int execlp(const char * file,const char * arg,....);

execlp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0], argv[1], …, 最后一个参数必须用空指针(NULL)作结束.
该函数如果执行成功则无返回值,执行失败则直接返回-1,失败原因存在errno中。

#include<unistd.h>int execle(const char * path,const char * arg,....,char *const envp[]);

execl是用来执行参数path字符串所代表的文件路径,并为新程序复制最后一个参数所指示的环境变量。接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
该函数如果执行成功则无返回值,执行失败则直接返回-1,失败原因存在errno中。

等待进程结束

#include<sys/types.h>#include<sys/wait.h>pid_t wait(int *statloc);pid_t waitpid(pid_t pid,int *statloc,int options);

返回值:若函数执行成功,则返回得到状态的进程id;若函数执行出错则在大部分情况下返回-1,在指定了一定参数时返回0。
waitpid()可以指定等待哪一个具体的进程结束。

wait();

如果参数的值不是NULL,wait()就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的

waitpid();

参数pid的值有以下几种类型:
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去.
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样.
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬.
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值.
参数options的值有以下几种类型:

如果使用了WNOHANG参数,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去,非阻塞版本.
如果使用了WUNTRACED参数,则子进程进入暂停则马上返回,但结束状态不予以理会.

Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用”|”运算符把它们连接起来使用,如果我们不想使用它们,也可以把options设为0,如:ret=waitpid(-1,NULL,0);

waitpid的返回值比wait稍微复杂一些,一共有3种情况:
a、当正常返回的时候waitpid返回收集到的子进程的进程ID;
b、设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
c、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD.

获得进程ID

#include <sys/types.h>#include <unistd.h>pid_t getpid(void);pid_t getppid(void);

获得进程的ID号
获得父进程的ID号

setuid和setgid

#include <sys/types.h>#include <unistd.h>int setuid(uid_t uid);      //设置进程实际用户IDint setgid(gid_t gid);      //设置进程实际组ID

改变进程的优先级

#include <unistd.h>int nice(int increment);
#include <sys/resource.h>int getpriority(int which, id_t who);int setpriority(int which, id_t who, int prio);

which和who取值的意义:
getpriority
- PRIO_PROCESS 一个特定的进程,此时who的取值为ID进程
- PRIO_PGRP 一个进程组的所有进程,此时who取值为进程组ID
- PRIO_USER 一个用户拥有的所有进程,此时who值为实际用户
- ERSCH which和who的组合与现存的所有进程均不匹配
- EINVAL which是个无效的值
setpriority
- ERERM 要设置优先级的进程与当前的进程不属于同一个用户,并且当前进程没有CAP_SYS_NICE特许
- EACCES 该调用可能降低进程的优先级并且进程没有CAP_SYS_NICE特许

原创粉丝点击