Linux 进程总结

一、linux进程结构

  • 代码段:存放可执行代码
  • 数据段存放程序的全局变量、常量、静态变量
  • 堆栈段用于存放动态分配的内存变量

二、创建进程

1.fork
#include<stdio.h>
#include<unistd.h>
pid_t fork(void);

fork有两个返回值
- fork成功调用之后实际分为两个进程,一个是父进程调用fork后返回值是刚才创建的子进程的ID
- 另一个是子进程fork函数的返回值0

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

int main()
{
    pid_t pid;
    char *msg;
    int k;

    printf("process creation study\n");
    pid=fork();
    switch(pid)
    {
        case 0:
            msg="child process is running";
            k=3;
            break;
        case -1:
            perror("process creation failed\n");
            break;
        default:
            msg="parent process is running";
            k=5;
            break;
    }
    while(k>0)
    {
        puts(msg);
        sleep(1);
        k--;
    }
}
  • 可以看出父子进程是交替执行的,取决于内核所使用的调度算法
  • fork失败的原因是父进程拥有的子进程个数超过了规定的限制
  • 操作系统将父进程的内存地址完全复制到子进程
  • 子进程继承了父进程的用户ID,组ID、当前目录、根目录、打开的文件,创建文件时使用的屏蔽字、信号屏蔽、上下环境、共享的储存段、资源限制等,不会继承父进程的文件锁,警告
2、孤儿进程
  • 一个子进程的父进程先于子进程结束,子进程就变成了孤儿进程,它由init进程收养
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    pid_t pid;

    pid=fork();
    switch(pid)
    {
        case 0:
            while(1)
        {
            printf("A background process,PID:%d\n,parrent ID:%d\n",getpid(),getppid());
            sleep(3);
        }
        case -1:
            perror("process failed!\n");
            exit(-1);
        default:
            printf("i am parent process,my pid is %d\n",getpid());
            exit(0);
    }
}

3、vfork

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int globVar=5;

int main()
{
    pid_t pid;
    int var =1,i;
    //pid=fork();
    pid=vfork();
    switch(pid)
    {
        case 0:
            i=3;
        while(i-->0)
        {
            printf("child process is running\n");
            globVar++;
            var++;
            sleep(1);
        }
        printf("child's globVar=%d,var=%d\n",globVar,var);
        printf("%s\n",a);
        exit(0);
        case -1:
            perror("process failed\n");
        exit(0);
        default:
        i=5;
        while(i-->0)
        {
            printf("parent process is running\n");
            globVar++;
            var++;
            sleep(1);
        }
        printf("parent's globVar=%d,var=%d\n",globVar,var);
        exit(0);
    }
    return 0;
}
  • vfork创建的子进程共享父进程的地址空间,也就是说父进程完全运用在父进程的地址空间上,子进程对地址的任何数据修改同样为父进程看到
    4.守护进程
  • 是在后台运行的、没有控制终端与之链接的进程。它独立与控制终端
    * 方法 *
  • 让进程后台运行,用fork产生一个子进程,然后父进程退出
  • 调用setsid创建一个新的对话期 ,让进程成为会话组长,摆脱从父进程那里继承的控制终端,登录回话,进程组
  • 为了禁止重新打开控制终端,再fork一次,再让父进程退出
  • 将当前目录改为根目录。当守护进程当前的工作目录再一个装配文件系统中,该文件系统不能被拆卸。
  • 将文件创建时使用的屏蔽字设置为0.防止继承时没有一些权限
  • 父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。父进程也不会因为等待子进程结束而增加父进程的负担
#include<string.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<sys/param.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<time.h>
#include<syslog.h>
#include<stdlib.h>
int init_daemon(void)
{
    int pid;
    int i;

    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("/");
    umask(0);

    signal(SIGCHLD,SIG_IGN);//忽略信号
    while(1)
    {
        int fd=open("/home/cwh/protect.txt",O_CREAT|O_APPEND|O_RDWR,0644);//目录可以任意的改
        char str[100];
        strcpy(str,"just for test\n");
        write(fd,str,strlen(str));
        sleep(5);
    }
    return 0;
}
int main()
{
    time_t now;
    init_daemon();
}

5、进程的退出
- exit()把控制权交给系统,而return将控制权交给调用函数
- _exit()函数执行完后会立即返回给内核,而exit()要执行一些清理操作,然后才把控制权给内核

6、执行新程序

int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
//代表把每个参数罗列
//execl("/usr/bin/ls", "-l", "-R", "/", (char *)NULL);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *)NULL, char * const envp[] */);
// char* const envp[] = {"AA=aaa", "XX=abcd", NULL};
    execle("./hello", "hello", "-l", "-a", NULL, envp);
int execv(const char *path, char *const argv[]);
//char *argv[] = { "-l", "-R", "/" };execv("/usr/bin/ls", argv);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
int fexecve(int fd, char *const argv[], char *const envp[]);

返回值:若函数执行成功无返回值,函数执行失败则返回-1。
path:要执行的程序的路径。绝对和相对都可以。
file:要执行的程序的文件名。若指定的参数中包含“/”,则将他认为是一个路径,若不包含,则将在PATH环境变量中指定的目录下进行搜索。
arg …:在此处罗列所有传递给程序的命令行参数,最后一个参数之后要附加一个(char *) NULL来表示结束。
argv:提供一个命令行参数的指针数组,数组的最后一个字符串一定为NULL。
envp:提供一个环境变量数组。
fd:要执行的文件的文件描述符。

其他相关解释:
为何成功执行无返回值:因为exec族函数会将新的程序的代码段完全地代替子进程的代码,因此若成功执行了exec族函数,返回值已经没有意义,所以没有返回值。
执行完exec族函数之后,子进程的pid并没有发生变化,从父进程继承的各种属性也依然有效。
在以上函数中,但凡不需要指定envp[]参数的,都会将environ变量传入,当做环境变量来使用。

7、等待进程结束

pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *statloc, int options);
//waitpid(pid,NULL,0)例子

返回值:若函数执行成功,则返回得到状态的进程id;若函数执行出错则在大部分情况下返回-1,在指定了一定参数时返回0。
statloc:指定为一个int指针。函数将把获取到的进程终止时的状态存储在该指针指向的区域。若将参数设置为NULL,则表示父进程不关心子进程的终止状态,将会丢弃这部分内容。
pid:指定要等待其结束的进程的pid,函数将仅在它结束的时候返回。若指定为-1,此种情况下waitpid与wait等效;若指定为0,此时将等待gid等同于调用进程组id的任一子进程;其他情况下等同于等待pid等于该参数绝对值的进程。
options:waitpid函数的选项设定。

其他相关解释
两种函数的区别:
①wait会使调用者阻塞,waitpid可以通过options参数实现不阻塞;②waitpid可以指定等待哪一个具体的进程结束。
zombie(僵死进程):当子进程终止时,不会完全地清除掉其所有状态,而是保存了至少pid、终止状态、使用的CPU时间总量等状态的信息。父进程可以通过wait或者waitpid来获取这些属性。若父进程没有处理这些信息,此时的子进程就称之为僵死进程。
信号系统相关:子进程终止时内核会向父进程发送SIGCHLD信号,父进程既可以以此来设计控制系统处理该信号,也可以选择忽略该信号(但子进程的终止状态会一直保存)。

其中,options可以设置为以下四种宏定义相互做位或运算得到的值。

选项意义
0不设置任何特殊功能
WNOHANG若pid指定的子进程不是立即可用的,则waitpid不会阻塞,而是直接返回0
WCONTINUED若实现支持作业控制,则由pid指定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态。
WUNTRACED若实现支持作业控制,而由pid指定的任一子进程已处于停止状态,并且其状态自停止以来还未报告过,则返回其状态。

其中,通过statloc参数拿到的值可以使用以下宏来检测其终止状态:

意义
WIFEXITED(statloc)若为正常终止的子进程返回的状态,则为真
WEXITSTATUS(statloc)获得正常终止的子进程传给exit等函数参数的低8位
WIFSIGNALED(statloc)若为异常终止的子进程返回的状态,则为真
WTERMSIG(statloc)获得使子进程异常终止的信号编号
WIFSTOPPED(statloc)若为当前暂停的子进程返回的状态,则为真
WSTOPSIG(statloc)获得使子进程暂停的信号编号
WIFCONTINUED(statloc)若在作业控制暂停后已经继续的子进程返回的状态,则为真
WCOREDUMP(statloc)若产生了终止进程的core文件,则为真
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<wait.h>
int main()
{
    pid_t pid;
    char *msg;
    int k;
    int exit_code;

    printf("study how to get exit code!\n");
    pid=fork();
    switch(pid)
    {
        case 0:
            msg="child process is running";
            k=5;
            exit_code =37;
            break;
        case -1:
            perror("process creation failed!\n");
            exit(1);
        default:
            exit_code=0;
            break;
    }
    if(pid!=0)
    {
        int stat_val;
        pid_t child_pid;

        child_pid=wait(&stat_val);

        printf("child process has exited,pid=%d\n",child_pid);
        if(WIFEXITED(stat_val))
            printf("child exited with code %d\n",WEXITSTATUS(stat_val));
        else 
            printf("child exited abnormally\n");
    }
    else 
    {
        while(k-->0)
        {
            puts(msg);
            sleep(1);
        }
    }
    exit(exit_code);
}

8、设置用户ID等

int setuid(uid_t uid);      //设置进程实际用户ID
int setgid(gid_t gid);      //设置进程实际组ID
int seteuid(uid_t uid);     //设置进程有效用户ID
int setegid(gid_t gid);     //设置进程有效用户组ID
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<stdlib.h>
#include<error.h>
#include<string.h>
extern int erron;

int main()
{
    int fd;

    printf("uid study:\n");
    printf("process's uid =%d,euid =%d\n",getuid(),geteuid());

    if((fd=open("test.c",O_RDWR))==-1)
    {
        printf("open failture,errno is %d :%s \n",errno,strerror(errno));
        exit(1);
    }
    else 
        printf("Open successfully!\n");

    close(fd);
    exit(0);
}

9、改变进程优先级

int nice(int incr);
int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int value);

返回值:nice和getpriority若执行成功则返回新的nice值,若执行出错则返回-1;setpriority若执行成功则返回0,出错则返回-1。
incr:要增加的nice值。若incr取负数,则会减少nice值。当incr参数的取值超过指定范围,会自动将之设置为可以取到的最大值。
which:表示who属于那种类型。
who:指定要修改进程、进程组或用户的ID。 若指定为0,会根据参数解释为当前的进程ID、进程组ID或实际用户ID。
value:要改变多少。
其中,which参数可取以下值:

选项意义
PRIO_PROCESS一个特定的进程,who的值为ID
PRIO_PGRP一个进程组的所有进程,who的值为进程组ID
PRIO_USER一个用户拥有的所有进程,who的值为实际用户ID
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值