• Linux 编程学习笔记----过程管理和项目发展(在)


    转载请注明出处,http://blog.csdn.net/suool/article/details/38406211,谢谢。


    Linux进程存储结构和进程结构

    可运行文件结构

    例如以下图:

    能够看出,此ELF可运行文件存储时(没有调入内存)分为代码区、数据区和未出花数据区三部分。

    代码区:存放cpu的运行的机器指令。

    数据区:包括程序中的已经初始化的静态变量,以及已经初始化的全局变量。

    未初始化数据区:存入的是未初始化的全局变量和未初始化的静态变量。

    如今在上面的程序代码中添加一个int的静态变量,结果例如以下:

    代码部分添加了4个字节。是int的大小。

    进程结构

    假设将一个ELF格式可执行文件载入到内存中执行,则将演变成一个或多个进程。进程是Linux事务管理的基本单元,全部的进程均拥有独立的环境和资源。

    下图展示一个ELF可运行文件的存储结构和Linux进程基本结构的对比:

    上图仅给出了一个进程的在内存中申请的代码区、初始化数据区、未初始化数据区、堆区、栈区五个部分。

    为了更好的管理linux所訪问的资源,系统定义了进程控制块(PCB)结构体来管理每一个进程资源。如图:

    而进程资源分为:

    内核空间进程资源和用户空间进程资源

    内核空间进程资源即PCB相关的信息。用户空间进程资源包含:通过成员mm_struct映射的内存空间。

    进程状态

    // 内核进程结构
    #define TASK_RUNNING		0   // 就绪
    #define TASK_INTERRUPTIBLE	1   // 中断等待
    #define TASK_UNINTERRUPTIBLE	2   // 不可中断等待
    #define __TASK_STOPPED		4   // 僵死
    #define __TASK_TRACED		8   // 暂停       
    /* in tsk->exit_state */
    #define EXIT_ZOMBIE		16
    #define EXIT_DEAD		32
    /* in tsk->state again */
    #define TASK_DEAD		64
    #define TASK_WAKEKILL		128
    #define TASK_WAKING		256


    系统中的每一个进程都必定处于以上所列进程状态中的一种。

        TASK_RUNNING表示进程要么正在运行。要么正要准备运行。

        TASK_INTERRUPTIBLE表示进程被堵塞(睡眠),直到某个条件变为真。条件一旦达成。进程的状态就被设置为TASK_RUNNING。

        TASK_UNINTERRUPTIBLE的意义与TASK_INTERRUPTIBLE类似。除了不能通过接受一个信号来唤醒以外。

        __TASK_STOPPED表示进程被停止运行。

        __TASK_TRACED表示进程被debugger等进程监视。

        EXIT_ZOMBIE表示进程的运行被终止,可是其父进程还没有使用wait()等系统调用来获知它的终止信息。

        EXIT_DEAD表示进程的终于状态。

        EXIT_ZOMBIE和EXIT_DEAD也能够存放在exit_state成员中。进程状态的切换过程和原因大致例如以下图(图片来自《Linux Kernel Development》)

    进程基本属性

    进程号(PID)

    PID是系统维护的唯一标示一个进程的正整数。进程号是无法在用户层改动的。

    在Linux系统中,系统的第一个用户进程是init进程。PID是1,其它的进程的PID依次添加。例如以下:


    在应用编程中能够使用getpid()函数获取当前进程的PID.详细參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getpid.html

    父进程号(PPID)

    除init进程外的全部进程都是由还有一进程创建的。该进程成为父进程,被创建的进程成为子进程。

    PPID一样无法在用户层改动。

    使用getppid()函数获取当前进程的父进程号。參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getppid.html

    进程组号(PGID)

    和用户管理一样,进程也有自己的进程号(PID)和进程组号(PGID)。进程组是一个或多个进程的集合。他们与同一个作业相关联。能够接受来自同一个终端的各种信号,可是进程组号能够在用户层改动。

    使用getpgid()函数获取指定进程的进程组号。详见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getpgid.html

    以下一个程序是总和上面三个函数使用的演示样例:

    #include<stdio.h>
    #include<unistd.h>
    int main(int argc,char *argv[])
    {
    	int i;
    	printf("	pid	 ppid 	 pgid
    ");             // 提示信息
    	printf("parent	%d	%d	%d
    ",getpid(),getppid(),getpgid(0));  // 当前进程信息
    	for(i=0;i<2;i++)
    		if(fork()==0)
    			printf("child	%d	%d	%d
    ",getpid(),getppid(),getpgid(0)); // 子进程信息
    	return 0;
    }


    此外,getpgrp()也能够用来获取当前进程的进程组号。

    參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getpgrp.html

    会话

    会话(session)是一个或多个进程的合集。

    系统调用函数getsid()用来获取某个进程会话的SID。

    參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getsid.html

    某进程的sid是能够改动的,函数setsid()用于创建新的会话。

    here : http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

    控制终端

    会话和进程组有例如以下特点:

    为了让终端设备驱动程序将信号送到进程。能够调用tcgetpgrp()获取前台进程组的进程组号。返回与打开的终端相关联的前台进程组号。http://pubs.opengroup.org/onlinepubs/009695399/functions/tcgetpgrp.html

    tcsetpgrp()函数用来设置某个进程组是前台还是后台进程组。http://pubs.opengroup.org/onlinepubs/009695399/functions/tcsetpgrp.html

    假设进程有一个控制终端,则将前台进程组ID设置为pgrpid,这个值应该在用一个会话中的一个进程组的id,參数fileds是控制终端文件描写叙述符。

    tcgetsid(*)函数获取当前控制终端的会话首进程的会话id。http://pubs.opengroup.org/onlinepubs/009695399/functions/tcgetsid.html


    以下是使用上面的函数演示样例:

    #include<stdio.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<stdlib.h>
    int main()
    {
        int fd;	
        pid_t pid;
        pid=fork();     // 创建新进程
        if(pid==-1)
    	perror("fork");
        else if(pid>0)
        {
           wait(NULL);
           exit(EXIT_FAILURE);
        }
        else
        {
            if((fd=open("/dev/pts/0",O_RDWR))==-1) // 由于是网络终端,在此打开终端以确认
            {
               perror("open");
            }
    	printf("pid=%d,ppid=%d
    ",getpid(),getppid());         // 获取进程号& 父进程
            printf("sid=%d,tcgetsid=%d
    ",getsid(getpid()),tcgetsid(fd)); // 读取会话sid和终端sid
            printf("tcgetpgrp=%d
    ",tcgetpgrp(fd));        // 读取终端前台进程
            printf("pigd=%d
    ",getpgid(getpid()));      // 读取进程组的ID
       }
    }  
    

    执行结果:

    进程用户属性

    Linux是权限哟有严格控制的操作系统,某个进程拥有真实的用户号(RUID)、真有用户组号(RGID)、有效用户号(EUID)、有效用户组号(EGID)信息。

    在Linux系统中,文件的创建者是文件的拥有者,即文件的真有用户号为文件的拥有者号,使用ls -l 命令查看。

    进程真有用户号(RUID)

    对于进程而言,创建该进程的用户uid即是此进程的真有用户号。使用getuid(0函数获取当前进程的RUID。函数定义在unistd.h中。

    详细參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getuid.html

    进程有效用户组号(EUID)

    主要用于权限检查。

    多数情况下,EUID和UID同样,可是假设可执行文件的setuid位有效,则该文件的拥有者之外的用户在执行程序时,EUID和UID不一样。即当某个可执行文件设置了  setgid位后。不论什么用户(包含root)执行此程序时。其有效用户组EUID为该文件的拥有者。sunch as:

    注意setuid位的特殊性,这个以后会专门解说。

    进程用户组号(GID)

    创建进程的用户所在的组号为该进程的用户组号(GID)。能够调用getgid()函数来获取当前进程的真有用户组号。

    详细參见:http://pubs.opengroup.org/onlinepubs/009695399/functions/getgid.html

    以下是读取当前进程的UID/GID/EUID/EGID的演示样例程序:

    #include<stdio.h>
    #include<unistd.h>
    int main(int argc,char *argv[])
    {
    	printf("	uid	gid	euid	egid
    ");
    	printf("parent	%d	%d	%d	%d
    ",getuid(),getgid(),geteuid(),getegid());
    	if(fork()==0)
    	{
    		printf("child	%d	%d	%d	%d
    ",getuid(),getgid(),geteuid(),getegid());
    	}
    	return 0;
    }


    Next

    进程管理及控制

    Linux 特殊进程


    转载请注明出处。http://blog.csdn.net/suool/article/details/38406211,谢谢!


    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    wireshark1
    攻防世界Crypto高手进阶区部分Writeup
    flag_in_your_hand1
    扩展GridView之添加单选列
    C#关于日期月天数和一年有多少周及某年某周时间段的计算
    C# 开发和使用中的32个技巧
    TSQL编程的全局变量
    计算当前月底天数
    存储过程编写经验和优化措施
    ASP.NET 程序中常用的三十三种代码
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4864771.html
Copyright © 2020-2023  润新知