博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unix高级环境编程
阅读量:7197 次
发布时间:2019-06-29

本文共 6727 字,大约阅读时间需要 22 分钟。

[07] Unix进程环境

==================================

1、 进程终止

    atexit()函数注册终止处理程序。

    exit()或return语句:

        终止处理程序->终止处理程序->标准I/O清除->_exit()->进入内核。

    _exit()直接进入内核。


2、 环境表

    extern char **environ;

    例:

    for( i=0; environ[i] != NULL; i++)

    {

        printf( "env[%d]: %s\n", i, environ[i] );

    }


3、 C程序存储空间布局

    * 正文                    即代码段

    * 初始化数据段            如:int maxcount = 99;

    * bss(非初始化数据)        如:long sun[1000];

    * 栈                    自动变量及函数调用所需的信息

    * 堆                    动态存取分配,在bss顶端,栈的底端。

    -----------------------------------------------------------

    高地址     栈 -> .... <- 堆  bss  初始数据  正文    低地址

    -----------------------------------------------------------

    注: 可用size命令查看text data bss信息。


4、 存储器分配

    alloca()是在栈中分配空间,函数调用结束后空间自动释放,但有些系统不支持。


5、 环境变量

    char * getenv(char * name);

    int putenv( const char * str );

    int setenv(const char * name, const char * value, int rewrite );

    void unsetenv(const char *name );

    注:environ表及字串存放在栈的顶部,当调用上述函数时,可能需将其移到至堆中。


6、 setjmp和longjmp


    #include <setjmp.h>

        jmp_buf jmpbuffer;

        ......

    _@:    setjmp( jmpbuffer);

        ......

    call    my_function();

        ......

        my_function(){

            longjmp(jmpbuffer, 1 );

        }

    注:

    *    代码中,在_@处调用setjmp函数,然后经过多层调用至my_function函数,

    在该函数中调用longjmp(,1)函数,责程序调整至_@处。

    即:反绕过上层的所有栈帧,跳至_@所在处的栈状态。

    *    setjmp返回值: 如果直接调用,则返回0, 否则返回longjmp的第二个参数。


7、 getrlimit和setrlimit

    #include <sys/time.h>

    #include <sys/resource.h>

    int getrlimit( int resource, struct rlimit * rlptr );

    int setrlimit( int resource, const struct rlimit * rlptr );




[08] 进程控制

==================================

1、    进程标识

    进程ID:

        0    交换进程swapper

        1    init,在自举结束后由内核调用。它不会终止,但是它是普通用户进程,以超级用户权限运行。

        2    页精灵进程,pagedaemon

    函数:

    #include <sys/types.h>

    #include <unistd.h>

    pid_t getpid(void);

    pid_t getppid(void);

    uid_t getuid(void);

    uid_t geteuid(void);

    gid_t getgid(void);

    git_t getegid(void);


2、    fork函数

    fork() : 创建子进程。

    #include <sys/types.h>

    #include <unistd.h>

    pid_t fork(void);


    * 调用fork后,子进程和父进程继续执行fork之后的代码。

    * fork返回两次,子进程返回0,父进程返回子进程ID。

    * fork之后,无法知道父进程和子进程哪个先执行。

    例:

    int main()

    {

        if( (pdi = fork()) < 0 )

            error();

        else if( pid == 0 ){//子进程

        }

        else{                //父进程

        }

        ……                //父子进程继续执行fork之后的代码

    }

    

    两种fork用法

    * fork之后,父子进程各自执行自己的代码。在网络服务器中常见。

    * 进程执行不同的程序。fork后,子进程调用exec函数。


3、    vfork

    vfork()创建一个新进程。

    与fork区别:

    * vfork保证子进程先于父进程执行,

    * vfork子进程在父进程的地址空间内执行。


4、 wait和waitpid函数


5、 竞态条件

    多个进程企图对共享数据进行处理,但结果取决于进程的运行顺序。


6、 6个exec函数对比

    --------------------------------------------------------------

    函数    path    name        参数表    argv[]        environ    envp[]

    --------------------------------------------------------------

    execl    *                    *                    *

    execlp            *            *                    *

    execle    *                    *                            *

    execv    *                            *            *

    execvp            *                    *            *

    execve    *                            *                    *

    --------------------------------------------------------------

    p    取filename做参数

    l    取参数表

    v    表示去argv[]数组

    e    表示取envp[]数组    



[10] 信号

=================================

1、 概念

    信号:软件中断,以SIG开头,在<signal.h>中定义。

    三种方式处理信号:

    * 忽略此信号,但SIGKILL和SIGSTOP不能被忽略,因为他向超级用户提供了一种使进程终止或停止的可靠方法。如果忽略某些硬件异常产生的信号,则进程的行为未知。

    * 捕捉信号,要通知内核在某信号发生时,调用一个用户函数。

    * 执行系统默认动作。


2、 signal函数

    #include <signal.h>

    void (*signal (int signo, void (*func)(int))) (int );

    注:

        * 返回值 void (*signal)(int);

          返回老的信号处理程序指针。

        * 参数

          signo: 信号表示

          void (*func)(int); 新的信号处理程序,

           若为SIG_IGN - 忽略此信号

           若为SIG_DFL - 系统默认操作

    

    用typedef方法定义signal函数:

        typedef void Sigfunc(int);

        Sigfunc *signal( int, Sigfunc *);

    

    当进程调用fork时,子进程继承了父进程的信号处理方式。因为子进程开始时复制了父进程的存储映像,所有信号捕捉函数的地址在子进程中式有意义的。


3、kill和raise函数

    #include <sys/types.h>

    #include <signal.h>


    int kill( pid_t pid, int signo );

    int raise(int signo );

    * kill 将信号发送给进程或进程组

      pid>0 将信号发送给该进程

      pid==0 将信号发送给进程组ID等于发送进程的进程组ID,而且发生进程有许可权向其发送信号的所有进程。

      pid<0 将信号发送给进程组ID等于pid绝对值,与pid==0类似。


    * raise 进程向自身发送信号


4、alarm和pause函数

    alarm: 设置一时间段,当超过该时间时,产生SIGALRM信号,默认动作时终止该进程。

    #include <unistd.h>

    unsigned int alarm( unsigned int seconds );

    注:

    * seconds 是秒数。

    * 每个进程只能有一个闹钟时间,如果调用alarm时,以前以为该进程设置过闹钟时间,而且还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的返回值。以前登记的闹钟时间被新值替换。

    

    #include <unistd.h>

    int pause(void)

    注:

    只有执行了一个信号处理程序并从其返回,pause才返回-1, errno设为EINTR。


    (书中有很多alarm例子,说明使用信号所要注意的地方)


5、 信号集

    用sigset_t类型表示一信号集。

    #include <signal.h>

    int sigemptyset(sigset_t * set );

    int sigfillset(sigset_t *set );

    int sigaddset(sigset_t *set, int signo );

    int sigdelset(sigset_t *set, int signo );

    ----返回值: 成功 0, 错误 -1

    int sigismember( conset sigset_t *set, int signo );

    ----返回值: 真 1, 假 0


6、 sigprocmask函数

    功能:检测或更改进程的信号屏蔽字。

    #include <signal.h>

    int sigprocmask(int how, const sigset_t *set, sigset_t *oset );

    注:

    * 若oset非空, oset为当前信号屏蔽字

    * 若set非空, how指示如何修改当前信号屏蔽字。

    * how:

    *     SIG_BLOCK    或操作

    *     SIG_UNBLOCK 与

    *     SIG_SETMASK 赋值操作


7、 sigpending函数

    功能:返回调用进程的被阻塞不能递送和当前未决的信号集。

    #include <signal.h>

    int sigpending(sigset_t *set );


8、 sigaction函数

    功能,取代了signal函数。

    #include <signal.h>

    int sigaction(int signo, const struct sigaction *act, \

            struct sigaction * oact );


    struct sigaction{

        void        (*sa_handler)();

        sigset_t    sa_mask;

        int            sa_flags;

    };

    sa_handler    信号捕捉函数

    sa_mask        调用sa_handler之前需要添加的信号屏蔽字,调用结束后,进程的信号屏蔽字再恢复为原先值。

    sa_flags    信号处理选项。


9、 sigsetjmp和siglongjmp

    #include <setjmp.h>

    int sigsetjmp( sigjmp_buf env, int savemask );

    void siglongjmp( sigjmp_buf env, int val );

    区别:

    当savemask 为非0时,sigsetjmp在env中保存进程的当前屏蔽字,调用siglongjmp时,siglongjmp从中恢复保存的信号屏蔽字。

    而,setjmp和longjmp并不保证该操作。


10、sigsuspend函数

    #include<signal.h>

    int sigsuspend(const sigset_t sigmask );


    进程的信号屏蔽字设置为sigmask,在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程也被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且该进程的信号屏蔽字设置为调用sigsuspend之前的值。


[11] 终端


[12] 高级I/O    

====================================

1、 非阻塞I/O

    对一个给定的描述符有两种方法对其指定非阻塞I/O

    * 如果是调用open以获取描述符,则可以指定O_NONBLOCK标志

    * 对于已经打开的描述符,调用fcntl打开O_NONBLOCK文件状态标志。


2、 记录锁

    功能: 一个进程正在读或者修改文件的某个部分时,可以阻止其他进程修改同一文件区。它锁定的只是文件的一个区域,也可是整个文件。

    fcntl记录锁

    #include <sys/types.h>

    #include <unistd.h>

    #include <fcntl.h>


    int fcntl ( int filedes, int cmd, struct flock * flockptr );

    参数:

    * cmd        /* F_GETLK, F_SETLK, F_SETLKW */

        F_GETLK    如果存在一把锁,则把现存的锁的信息写到flockptr指向的结构中。若不存在,则将l_type设置为F_UNLCK,其他域保存不变。

        F_SETLK 设置由flockptr所描述的锁。或者用于清除所描述的记录锁(设置l_type为F_UNLCK)。

        F_SETLKW 这是F_SETLK得阻塞版本。


    * flockptr    以下结构指针

    struct flock {

        short l_type;    /* F_RDLCK, F_WRLCK, F_UNLCK */

        off_t l_start;    /* offset in bytes, ralative to l_whence */

        short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */

        off_t l_len;    /* length, in bytes; 0 means lock to EOF */

        pid_t l_pid;    /* returned with F_GETLK */

    };


    [注]

    1、当进程终止,锁全部释放,当描述符被关闭时,该描述符的锁也被释放。

    2、fork后,子进程不继承父进程的记录锁

    3、exec后,新程序可以继续原执行程序的锁


[13] 精灵进程

================================

1. 编程规则

    - 调用fork,然后父进程调用exit。

      用处: 1.如果该精灵进程有Shell启动,那么父进程终止,是的Shell认为该条命令已经执行完成。

          2.保证了子进程不是一个进程组的首进程,它进程了父进程的进程组ID。

    - 调用setsid创建一个新的会话期。

          1.是进程成为对话期首进程。 2.成为一个新进程组的首进程。 3.没有控制终端。

    - 将工作目录更改为跟目录。

    - 将文件方式创建屏蔽字设置为0。

          1.去除由父进程继承得来的屏蔽字。

    - 关闭不需要的文件描述符。这与具体的精灵进程有关。


#include <sys/type.h>

#include <sys/stat.h>

#include <fcntl.h>

int daemon_init(void)

{

    pid_t    pid;

    if( (pid = fork() )<0 )

        return -1;

    else if( pid!=0 )

        exit(0);        //parent goes bye-bye

    setsid();

    chdir("/");

    umask(0);        //clear file mode creation mask

    return 0;

}



[14] 进程间通信

================================

1、 管道

    #include <unistd.h>

    int pipe( int filedes[2]);

    filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。



14.6----

转载地址:http://bfkum.baihongyu.com/

你可能感兴趣的文章
YAFFS2移植到AliOS Things指南
查看>>
valgrind--内存泄漏检测(转)
查看>>
在IE9中为你的网站自定义JumpList
查看>>
使用WSS的Lists.UpdateListItems()方法之被截断的CAML
查看>>
微软AJax.net源码初步分析(2)--服务执行流程
查看>>
白话插件框架原理
查看>>
将Sublime Text 2配置为C#代码编辑器(附配置文件)
查看>>
iOS编程——Objective-C KVO/KVC机制[转]
查看>>
读书笔记2013第9本:《注意力曲线----打败分心与焦虑》
查看>>
Oracle Move a Datafile from Filesystem & ASM
查看>>
通往财富自由之路3--开始一年的财富自由之旅
查看>>
深度学习预测比特币价格
查看>>
NumPy和Pandas常用库
查看>>
asp.net 操作 cookie
查看>>
Go并发编程基础(译)
查看>>
extjs 登录
查看>>
SQL Server 如何执行一个查询
查看>>
C#中toolStrip或statusStrip遮挡了SplitContainer怎么办?
查看>>
通过 imagick 让 PHP 生成 PSD 文件缩略图
查看>>
sqlserver 行转列
查看>>