/*GUN/Linux进程有两种基本类型:内核线程和用户进程。 用户进程由fork()和clone()创建。内核线程是由内核中的kernel_thread()创建。 使用fork()创建子进程的时候,系统为它复制了父进程使用的内存部分映像,但是开始时他们都指向同一块内存, 但是把内存页面标记为copy-on-write。当任何一个进程试图向这些内存中写入数据时,就会产生一组新的内存页面由这个进程私有。 这提高了创建新进程的效率,因为内存空间的复制推迟到发生写操作的时候。默认情况下,子进程继承文件描述符、内容映像以及CPU状态(如PC和寄存器分配)。 在调用fork的时候,父进程当时所有的变量对子进程都是可见的。但在fork执行完之后,父进程变量的任何改动对子进程来说都是不可见的。 */ /*传统的进程API fork 创建一个新的子进程 wait 将进程挂起直到任意一个子进程退出 waitpid 将进程挂起直到指定的子进程退出 signal 注册一个新的信号句柄 pause 将进程挂起直到捕获到信号 kill 向某个指定进程发送信号 raise 向当前进程发送信号 exec 将当前进程映像用一个新的进程映像替换 exit 正常终止当前进程(退出) */ //第一个进程实例 #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main( ) { pid_t myPid; pid_t myParentPid; gid_t myGid; uid_t myUid; myPid=getpid( ); myParentPid=getppid( ); myGid=getgid( ); myUid=getuid( ); printf( "my process id is %d/n",myPid ); printf( "my parent's process id is %d/n",myParentPid ); printf( "my user id is %d/n",myUid ); printf( "my group id is %d/n",myGid ); return 0; } //调用fork的应用实例 /* 注意fork首先把CPU的控制权交给子进程。 fork的返回值: On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <string.h> #include <sys/wait.h> int main( ) { pid_t ret; int status,i; int role=-1; ret=fork( ); if( ret==0 ) //child process { printf( "Child:this is the child process ( pid %d )/n",getpid( ) ); for( i=0;i<6;++i ) { printf( "Child: at count %d /n ",i ); sleep( 1 ); } role=1; } else if( ret>0 ) //parent process ,ret is the child process's pid { printf( "Parent:this is the parent process ( pid %d )/n",getpid( ) ); for( i=0;i<6;++i ) { printf( "Parent: at count %d /n ",i ); sleep( 1 ); } wait( &status ); role=0; } else { printf( "Parent:Error trying to fork( )( %s )/n",strerror(errno)); } printf ( "%s:Exiting.../n",( (role==0)?"Parent":"Child" ) ); //both parent and child process execute return 0; } /*信号,即GUN/Linux中进程的回调符号。可以为某个进程注册为在某事件发生时接受信号或 是在某个默认操作退出时忽略信号。 */ //为捕获信号注册句柄实例 #include <stdio.h> #include <sys/types.h> #include <signal.h> #include <unistd.h> void catch_ctlc( int sig_num ) { printf( "/nCaught Ctrl+C/n" ); fflush( stdout ); return; //可有可无 } int main( ) { //查看系统信号列表:man 7 signal signal( SIGINT,catch_ctlc ); printf( "Go ahead,make my day/n" ); //pause()causes the calling process (or thread) to sleep until a signal is delivered pause( ); return 0; } /************发出信号****************/ /* kill()可以向任意一个具有相应权限的进程发送信号并发挥作用; raise( sig )可以向自身进程发送信号 */ //从子进程向父进程发出一个信号 #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <signal.h> #include <errno.h> void usr1_handler( int sig_num ) { printf( "Parent ( %d ) got the SIGUSR1/n",getpid( ) ); } int main( ) { pid_t ret; int status; int role=-1; ret=fork( ); if( ret>0 ) { printf( "Parent:this is the parent process ( pid %d )/n",getpid( ) ); signal( SIGUSR1,usr1_handler ); role=0; pause( ); printf( "Parent:waiting for child exit/n" ); ret=wait( &status ); } else if( ret==0 ) { printf( "Child:this is the child process ( pid %d )/n",getpid( ) ); role=1; sleep( 1 ); //注意,当程序中出现sleep()的时候,就要注意对输出产生的滞后影响 printf( "Child:Sending SIGUSR1 to pid %d/n",getppid( ) ); //此处的‘/n’不仅有换行的作用,还有刷新stdout的作用 //kill(pid,signal)可用于向某个进程发送信号 kill( getppid( ),SIGUSR1 ); sleep( 2 ); } else { printf( "Parent:Error trying to fork( )( %s )/n",strerror(errno)); } printf("/n%s:Exiting.../n",( (role==0)?"Parent":"Child" )); return 0; } /*这个例子中父子进程都没有发送SIGUSR1信号 这个例子可用于展示进程间进行异步通讯的有力方法 ./test & kill -s 10 -p parent'pid // 10 represent SIGUSR1 kill -s SIGUSR1 -p child'pid */ #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <signal.h> #include <error.h> void usr1_handler( int sig_num ) { printf( "Process ( %d ) got the SIGUSR1/n",getpid( ) ); } int main( ) { pid_t ret; int status; int role=-1; signal( SIGUSR1,usr1_handler ); ret=fork( ); if( ret>0 ) { printf("This is parent process ( pid %d )/n",getpid( )); role=0; printf( "Parent process waiting for signal/n" ); pause( ); printf( "Parent process waiting for child exit/n" ); ret=wait( &status ); } else if( ret==0 ) { printf( "This is the child process ( pid %d )/n",getpid( ) ); role=1; pause( ); } else { printf( "Can't create the child process!/n" ); } printf( "%s:Exiting.../n",( role==0 )?"Parent":"Child" ); return 0; } //使用execlp的简单shell解释实例 #include <sys/types.h> #include <sys/wait.h> #include <wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LENGTH 80 int main( ) { int status; pid_t childpid; char cmd[ MAX_LENGTH+1 ]; char* sret; funcloop: while( 1 ) { printf( "mysh>" ); sret=fgets( cmd,sizeof( cmd ),stdin); printf( "your command is :%s/n",cmd ); if( sret==NULL ) { printf( "You have input nothing! Please input command:/n" ); goto funcloop; //exit( -1 ); } if( !strncmp(cmd, "bye",3 ) ) exit( 0 ); cmd[ strlen( cmd )-1 ]=0; childpid=fork( ); if( childpid==0 ) { execlp( cmd,cmd,NULL); } else if( childpid>0 ) { waitpid( childpid,&status,0 ); goto funcloop; } } free( cmd ); return 0; } /*参数和进程环境方面的问题 int execve( const char* filrname,char* const argv[ ],char* const envp[ ] ); char* envp[ ]={"PATH=/bin","FOO=99",NULL}; ... ret=execve( command,args,envp ); 变量envp提供了一系列变量,为新创建的进程设置了环境。 */ /*unsigned int alarm( unsigned int secs ) alarm在预先设定的时间长度达到时会发出一个SIGALRM信号, 在其他函数超时的情况下非常有用。 */ #include <stdio.h> #include <unistd.h> #include <signal.h> #include <string.h> #define MAX_BUFFER 80 void wakeup( int sig_num ) { raise( SIGKILL ); } int main( ) { char buffer[ MAX_BUFFER+1 ]; int ret; signal( SIGALRM,wakeup ); printf( "You have 3 seconds to enter your password/n" ); alarm( 3 ); ret=read( 0,buffer,MAX_BUFFER ); //取消SIGALRM消息,以0参调用alarm即可 alarm( 0 ); if( ret==-1 ) printf( "Password wrong/n" ); else { buffer[ strlen( buffer )-1 ]=0; printf( "User entered: %s/n",buffer ); } return 0; } /*exit函数 viod exit( int status ); 传入exit的参数会返回给父进程,为wait或waitpid调用提供所需要的状态信息。 exit调用时还会向父进程发送SIGCHLD信号,释放当前进程所占用的资源。 如果进程注册了atexit或on_exit函数,这些函数也会在退出时执行,调用顺序与它们的注册顺序相反。 */ /* POSIX标准信号API函数:sigaction( ) int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); sigaction()提供的机制比传统的信号句柄更先进,而且有更强的兼容性。因此,应当使用sigaction来取代signal。 */ //演示使用sigaction注册信号句柄的简单应用 #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <unistd.h> #include <errno.h> static int stopChild=0; void sigHandler( int sig,siginfo_t* siginfo,void* ignore ) { printf( "Got SIGUSR1 from %d/n",siginfo->si_pid ); stopChild=1; } int main( ) { pid_t ret; int status; int role=-1; ret=fork( ); if( ret>0 ) { printf( "Parent:this is the parent process ( pid %d )/n",getpid( ) ); //let the child init sleep( 1 ); kill( ret,SIGUSR1 ); ret=wait( &status ); role=0; } else if( ret==0 ) { struct sigaction act; printf( "This is the child process ( pid %d )/n",getpid( ) ); act.sa_flags=SA_SIGINFO; //使用sa_sigaction指向的方法作为消息函数 act.sa_sigaction=sigHandler; //指向特定方法 sigaction( SIGUSR1,&act,0 ); printf( "Child waiting.../n" ); while( !stopChild ); role=1; } else { printf( "Parent: Error trying to fork( )( %d )/n",errno ); } printf( "%s:Exiting.../n",( ( role==0 )?"Parent":"Child" ) ); return 0; }