首页 > 代码库 > 进程篇(3: 基本进程控制)--请参照本博客“操作系统”专栏
进程篇(3: 基本进程控制)--请参照本博客“操作系统”专栏
1. 进程标识符:
每个进程都有一个非负整型表示的唯一进程ID。但进程ID可以重用,当一个进程终止之后,其进程ID就可以再次被重用了。
UNIX系统中常常有一些专用的进程:
- ID为0的进程通常是调度进程,常常被称为交换进程(swapper),该进程是内核的一部分,它并不执行磁盘上的任何程序,因此也被称为系统进程。
- ID为1的进程通常是init进程,在自举过程结束后由内核调用,在比较新的版本中是/sbin/init。此进程负责在自举内核后启动一个UNIX系统。init通常读取与系统有关的初始化文件,并将系统引导到一个状态!
- ID为2的进程是页守护进程,此进程负责支持虚拟存储系统的分页操作。
Unix中返回一些与进程相关的标识符的函数:
1) 返回调用进程及其父进程的ID:
1 SYNOPSIS 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 pid_t getpid(void); 6 pid_t getppid(void); 7 8 DESCRIPTION 9 getpid() returns the process ID of the calling process. (This is often used by routines that generate 10 unique temporary filenames.) 11 12 getppid() returns the process ID of the parent of the calling process. 13 14 ERRORS 15 These functions are always successful.
下面我们来编写一段程序返回一个程序的调用进程的id号:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int main(void) 6 { 7 pid_t calling_id = getpid(); 8 printf("The calling process id: %d\n",calling_id); 9 return 0; 10 }
我在我的计算机上运行./a.out 三次,结果如下:
1 The calling process id: 5539 2 The calling process id: 5542 3 The calling process id: 5543
可见getpid返回的是调用 pid_t calling_id = getpid(); 这段代码的进程的进程id号。因为每次调用都不会重用上一次的进程id号,所以呈现递增的趋势!
下面我们编一段代码返回调用进程的父进程id号:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int main(void) 6 { 7 pid_t pp_id = getppid(); 8 printf("The parent id of calling process: %d\n",pp_id); 9 return 0; 10 }
我们在shell中连续运行3次./a.out,结果如下:
The parent id of calling process: 5088 The parent id of calling process: 5088 The parent id of calling process: 5088
我们用ps命令查看当前用户系统中运行的程序id:
PID TTY TIME CMD 2651 pts/0 00:00:00 bash 5088 pts/0 00:00:00 bash 5670 pts/0 00:00:00 ps
显然这个pp_id就是当前运行a.outd的shell进程的id。
2)返回调用程序的用户及有效用户id:
1 SYNOPSIS 2 #include <unistd.h> 3 #include <sys/types.h> 4 5 uid_t getuid(void); 6 uid_t geteuid(void); 7 8 DESCRIPTION 9 getuid() returns the real user ID of the calling process. 10 11 geteuid() returns the effective user ID of the calling process. 12 13 ERRORS 14 These functions are always successful.
3) 返回进程的组id和有效组id:
1 SYNOPSIS 2 #include <unistd.h> 3 #include <sys/types.h> 4 5 gid_t getgid(void); 6 gid_t getegid(void); 7 8 DESCRIPTION 9 getgid() returns the real group ID of the calling process. 10 11 getegid() returns the effective group ID of the calling process. 12 13 ERRORS 14 These functions are always successful.
2. 进程的创建,执行和结束终止:
2.1 进程创建:
2.1.1 fork 函数:
NAME fork - create a child process(创建一个子进程) SYNOPSIS #include <unistd.h> pid_t fork(void); DESCRIPTION fork() creates a new process by duplicating the calling process(复制调用进程来创建新进程). The new process, referred to as the child, is an exact duplicate of the calling process, referred to as the parent, except for the following points: * The child has its own unique process ID, and this PID does not match the ID of any exist‐ ing process group (setpgid(2)). * The child‘s parent process ID is the same as the parent‘s process ID. * The child does not inherit its parent‘s memory locks (mlock(2), mlockall(2)).(子进程并不继承父进程的内存锁) * Process resource utilizations (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.(子进程的资源使用计数和CPU时间计数都被值成0) * The child‘s set of pending signals is initially empty (sigpending(2)).(子进程的挂起信号量的数目初始化为0) * The child does not inherit semaphore adjustments from its parent (semop(2)).(子进程并不继承父进程的信号量调节器) * The child does not inherit record locks from its parent (fcntl(2)).(子进程并不继承父进程的记录锁) * The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_cre‐ ate(2)).(子进程不从父进程中继承时间计数器) * The child does not inherit outstanding asynchronous I/O operations from its parent (aio_read(3), aio_write(3)), nor does it inherit any asynchronous I/O contexts from its parent (see io_setup(2)). (子进程并不继承父进程的异步I/O操作和异步I/O内容) The process attributes in the preceding list are all specified in POSIX.1-2001. The parent and child also differ with respect to the following Linux-specific process attributes: * The child does not inherit directory change notifications (dnotify) from its parent (see the description of F_NOTIFY in fcntl(2)). * The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not receive a sig‐ nal when its parent terminates. * The default timer slack value is set to the parent‘s current timer slack value. See the description of PR_SET_TIMERSLACK in prctl(2). * Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited across a fork(). * The termination signal of the child is always SIGCHLD (see clone(2)). * The port access permission bits set by ioperm(2) are not inherited by the child; the child must turn on any bits that it requires using ioperm(2). Note the following further points: * The child process is created with a single thread—the one that called fork(). The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause. * The child inherits copies of the parent‘s set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two descriptors share open file status flags, current file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)). * The child inherits copies of the parent‘s set of open message queue descriptors (see mq_overview(7)). Each descriptor in the child refers to the same open message queue description as the corresponding descriptor in the parent. This means that the two descriptors share the same flags (mq_flags). * The child inherits copies of the parent‘s set of open directory streams (see opendir(3)). POSIX.1-2001 says that the corresponding directory streams in the parent and child may share the directory stream positioning; on Linux/glibc they do not. RETURN VALUE 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.(返回值) ERRORS EAGAIN fork() cannot allocate sufficient memory to copy the parent‘s page tables and allo‐ cate a task structure for the child. EAGAIN It was not possible to create a new process because the caller‘s RLIMIT_NPROC resource limit was encountered. To exceed this limit, the process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability. ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight. ENOSYS fork() is not supported on this platform (for example, hardware without a Memory-Man‐ agement Unit). CONFORMING TO SVr4, 4.3BSD, POSIX.1-2001. NOTES Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time and memory required to duplicate the parent‘s page tables, and to create a unique task structure for the child. Since version 2.3.3, rather than invoking the kernel‘s fork() system call, the glibc fork() wrapper that is provided as part of the NPTL threading implementation invokes clone(2) with flags that provide the same effect as the traditional system call. (A call to fork() is equivalent to a call to clone(2) specifying flags as just SIGCHLD.) The glibc wrapper invokes any fork handlers that have been established using pthread_atfork(3).
下面一段程序演示了fork函数,可以看出,子进程对变量所做的改变并不影响父进程中变量的值!
1 #include "apue.h" 2 3 int glob = 6; /* external variable in initialized data */ 4 char buf[] = "a write to stdout\n"; 5 6 int 7 main(void) 8 { 9 int var; /* automatic variable on the stack */ 10 pid_t pid; 11 12 var = 88; 13 if(write(STDOUT_FILENO,buf,sizeof(buf)-1) != sizeof(buf)-1) 14 err_sys("write error"); 15 printf("before fork\n"); 16 17 if((pid = fork()) < 0) 18 { 19 err_sys("fork error"); 20 } 21 else if(pid == 0) /* child! */ 22 { 23 glob++; 24 var++; 25 } 26 else 27 sleep(2); 28 29 printf("pid = %d, glob = %d, var = %d\n",getpid(),glob,var); 30 exit(0); 31 }
$ ./a.out > result
a write to stdout before fork pid = 6275, glob = 7, var = 89 before fork pid = 6274, glob = 6, var = 88
一般来说,在调用fork函数之后,父进程和子进程的执行顺序是不确定的,这取决于内核所使用的进程调度算法!
如果要实现父子进程间的的同步,必须实现进程间的通信,这里,让父进程sleep两秒而让子进程先执行!
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。