本文共 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/