高级Linux程序设计第三章:进程
?
高级Linux程序设计第一章:入门
http://forfuture1978.iteye.com/blog/649981
高级Linux程序设计第二章:编写良好的Linux软件
http://forfuture1978.iteye.com/blog/652338
?
?
每个进程都有一个唯一的进程号。每个进程都有一个父进程。系统中的进程以树的形式组织,init进程(进程号为1)作为根。进程0是调度进程,没有程序与之对应,是内核的一部分。进程1是init进程,是在系统启动的阶段由内核启动的,对应/sbin/init程序,是普通的用户进程。程序中可以通过getpid()得到进程号,通过getppid()得到父进程的进程号。
系统中的进程以树的形式组织,init进程(进程号为1)作为根。进程0是调度进程,没有程序与之对应,是内核的一部分。进程1是init进程,是在系统启动的阶段由内核启动的,对应/sbin/init程序,是普通的用户进程。程序中可以通过getpid()得到进程号,通过getppid()得到父进程的进程号。
进程1是init进程,是在系统启动的阶段由内核启动的,对应/sbin/init程序,是普通的用户进程。程序中可以通过getpid()得到进程号,通过getppid()得到父进程的进程号。
?
#include <stdio.h>
#include <unistd.h>
int main ()
{
??? printf (“The process ID is %d\n”, (int) getpid ());
??? printf (“The parent process ID is %d\n”, (int) getppid ());
??? return 0;
}
通过ps命令可以得到系统中运行的所有进程。通过kill命令可以杀掉某个进程。1、创建进程1.1、system函数system函数提供了一种在程序中运行一个命令的简单方法。
1、创建进程1.1、system函数system函数提供了一种在程序中运行一个命令的简单方法。
system函数提供了一种在程序中运行一个命令的简单方法。
?
#include <stdlib.h>
int main ()
{
??? int return_value;
??? return_value = system (“ls -l /”);
??? return return_value;
}
1.2、fork及exec函数当程序调用fork的时候,则一个完全复制的子程序被创建。父进程将从fork被调用的地方继续执行下去。子进程也是从相同的地方运行下去。父进程中fork函数的返回值是子进程的进程号。子进程中fork函数的返回值是零。
父进程将从fork被调用的地方继续执行下去。子进程也是从相同的地方运行下去。父进程中fork函数的返回值是子进程的进程号。子进程中fork函数的返回值是零。
父进程中fork函数的返回值是子进程的进程号。子进程中fork函数的返回值是零。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main ()
{
??? pid_t child_pid;
??? printf (“the main program process ID is %d\n”, (int) getpid ());
??? child_pid = fork ();
??? if (child_pid != 0) {
??????? printf (“this is the parent process, with id %d\n”, (int) getpid ());
??????? printf (“the child’s process ID is %d\n”, (int) child_pid);
??? }
??? else
??????? printf (“this is the child process, with id %d\n”, (int) getpid ());
??? return 0;
}
exec函数将当前运行的进程替换为另一个程序。exec函数是一组函数:包含p的函数(execvp, execlp)接收的参数是程序名。不包含p的函数接收的参数是程序的全路径。包含v的函数(execv, execvp, execve)以数组的形式接收参数列表。包含l的函数(execl, execlp, execle)以列举的形式接收参数列表。包含e的函数(execve, execle)以数组的形式接收环境变量。
包含p的函数(execvp, execlp)接收的参数是程序名。不包含p的函数接收的参数是程序的全路径。包含v的函数(execv, execvp, execve)以数组的形式接收参数列表。包含l的函数(execl, execlp, execle)以列举的形式接收参数列表。包含e的函数(execve, execle)以数组的形式接收环境变量。
包含v的函数(execv, execvp, execve)以数组的形式接收参数列表。包含l的函数(execl, execlp, execle)以列举的形式接收参数列表。包含e的函数(execve, execle)以数组的形式接收环境变量。
包含e的函数(execve, execle)以数组的形式接收环境变量。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
/* Spawn a child process running a new program. PROGRAM is the name of the program to run; the path will be searched for this program. ARG_LIST is a NULL-terminated list of character strings to be passed as the program’s argument list. Returns the process ID of the spawned process. */
int spawn (char* program, char** arg_list)
{
??? pid_t child_pid;
??? /* Duplicate this process. */
??? child_pid = fork ();
??? if (child_pid != 0)
??? /* This is the parent process. */
??????? return child_pid;
??? else {
??????? /* Now execute PROGRAM, searching for it in the path. */
??????? execvp (program, arg_list);
??????? /* The execvp function returns only if an error occurs. */
??????? fprintf (stderr, “an error occurred in execvp\n”);
??????? abort ();
??? }
}
int main ()
{
??? /* The argument list to pass to the “ls” command. */
??? char* arg_list[] = {
??????? “ls”, /* argv[0], the name of the program. */
??????? “-l”,
??????? “/”,
??????? NULL /* The argument list must end with a NULL. */
??? };
??? /* Spawn a child process running the “ls” command. Ignore the returned child process ID. */
??? spawn (“ls”, arg_list);
??? printf (“done with main program\n”);
??? return 0;
}
?
2、信号信号是发送给进程的特殊信息。当一个进程接收到一个信号的时候,它会立即处理此信号,并不等待完成当前的函数调用甚至当前一行代码。系统会在特殊情况下向进程发送信号:SIGBUS:总线错误SIGSEGV:段违规SIGFPE:浮点数异常一个进程可以向另一个进程发送信号。可以发送SIGTERM和SIGKILL信号来结束一个进程。信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
当一个进程接收到一个信号的时候,它会立即处理此信号,并不等待完成当前的函数调用甚至当前一行代码。系统会在特殊情况下向进程发送信号:SIGBUS:总线错误SIGSEGV:段违规SIGFPE:浮点数异常一个进程可以向另一个进程发送信号。可以发送SIGTERM和SIGKILL信号来结束一个进程。信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
SIGBUS:总线错误SIGSEGV:段违规SIGFPE:浮点数异常一个进程可以向另一个进程发送信号。可以发送SIGTERM和SIGKILL信号来结束一个进程。信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
SIGFPE:浮点数异常一个进程可以向另一个进程发送信号。可以发送SIGTERM和SIGKILL信号来结束一个进程。信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
可以发送SIGTERM和SIGKILL信号来结束一个进程。信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
sigaction函数设定信号处理方式SIG_DFL表示使用默认的信号处理方式。SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
SIG_IGN表示此信号可以被忽略。第二个参数是sigaction结构体,其包括一个信号处理函数。由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。信号处理函数中应该尽量处理最少的东西。信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
信号处理函数也可能被另一个信号中断。如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是sig_atomic_t类型。
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
sig_atomic_t sigusr1_count = 0;
void handler(int signal_number)
{
++sigusr1_count;
}
int main(int argc, char* argv[])
{
??? printf("the process ID is %d\n", (int)getpid());
??? struct sigaction sa;
??? memset(&sa, 0, sizeof(sa));
??? sa.sa_handler = &handler;
??? sigaction(SIGUSR1, &sa, NULL);
??? int i = 0;
??? while(i < 100)
??? {
??????? sleep(1);
??????? i++;
??? }
??? printf("SIGUSR was raised %d times\n", sigusr1_count);
??? return 0;
}
编译上述代码为程序sigusr1
gcc -o sigusr1 sigusr1.c
运行程序
[liuchao@localhost Signal]$ ./sigusr1
the process ID is 3401
在另一个终端,用ps命令得到sigusr1的进程号
[liuchao@localhost ~]$ ps -a
PID TTY TIME CMD
3401 pts/1 00:00:00 sigusr1
3403 pts/3 00:00:00 ps
向此进程号发送多个sigusr1信号
[liuchao@localhost ~]$ kill -s SIGUSR1 3401
[liuchao@localhost ~]$ kill -s SIGUSR1 3401
[liuchao@localhost ~]$ kill -s SIGUSR1 3401
[liuchao@localhost ~]$ kill -s SIGUSR1 3401
[liuchao@localhost ~]$ kill -s SIGUSR1 3401
当进程结束后
[liuchao@localhost Signal]$ ./sigusr1
the process ID is 3401
SIGUSR was raised 5 times
?
3、结束进程一个进程可以有两种方式结束:进程本身调用exit函数,或者其main函数结束。进程接收到信号后异常结束。ctrl+c发送SIGINT信号kill命令发送SIGTERM信号abort函数发送SIGABRT信号SIGKILL函数会立即结束一个进程,此信号不能被阻止,被程序自己处理。所有这些信号都可以用kill命令发送
进程本身调用exit函数,或者其main函数结束。进程接收到信号后异常结束。ctrl+c发送SIGINT信号kill命令发送SIGTERM信号abort函数发送SIGABRT信号SIGKILL函数会立即结束一个进程,此信号不能被阻止,被程序自己处理。所有这些信号都可以用kill命令发送
ctrl+c发送SIGINT信号kill命令发送SIGTERM信号abort函数发送SIGABRT信号SIGKILL函数会立即结束一个进程,此信号不能被阻止,被程序自己处理。所有这些信号都可以用kill命令发送
abort函数发送SIGABRT信号SIGKILL函数会立即结束一个进程,此信号不能被阻止,被程序自己处理。所有这些信号都可以用kill命令发送
所有这些信号都可以用kill命令发送
?
% kill -KILL pid用kill函数可以在程序中向一个进程发送信号。kill (child_pid, SIGTERM);有关wait函数:wait函数阻止当前的进程,直到其中一个子进程结束。waitpid函数等待一个指定的子进程结束。wait3函数等待子进程结束并返回子进程的各种资源使用的统计。wait4函数等待特定的子进程结束并返回子进程的各种资源使用的统计。
wait函数阻止当前的进程,直到其中一个子进程结束。waitpid函数等待一个指定的子进程结束。wait3函数等待子进程结束并返回子进程的各种资源使用的统计。wait4函数等待特定的子进程结束并返回子进程的各种资源使用的统计。
wait3函数等待子进程结束并返回子进程的各种资源使用的统计。wait4函数等待特定的子进程结束并返回子进程的各种资源使用的统计。
int main ()
{
??? int child_status;
??? /* The argument list to pass to the “ls” command. */
??? char* arg_list[] = {
??????? “ls”, /* argv[0], the name of the program. */
??????? “-l”,
??????? “/”,
??????? NULL /* The argument list must end with a NULL. */
??? };
??? /* Spawn a child process running the “ls” command. Ignore the returned child process ID. */
??? spawn (“ls”, arg_list);
??? /* Wait for the child process to complete. */
??? wait (&child_status);
??? if (WIFEXITED (child_status))
??????? printf (“the child process exited normally, with exit code %d\n”, WEXITSTATUS (child_status));
??? else
??????? printf (“the child process exited abnormally\n”);
??? return 0;
}
所谓僵死的进程指的是一个进程结束了但资源尚未被回收。父进程有责任在子进程结束的时候用wait函数回收子进程的资源。在子进程尚未被父进程回收的时候,其在系统中作为僵死进程存在。
在子进程尚未被父进程回收的时候,其在系统中作为僵死进程存在。
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main ()
{
??? pid_t child_pid;
??? /* Create a child process. */
??? child_pid = fork ();
??? if (child_pid > 0) {
??????? /* This is the parent process. Sleep for a minute. */
?????? sleep (60);
??? }
??? else {
??????? /* This is the child process. Exit immediately. */
??????? exit (0);
??? }
??? return 0;
}
% ps -e -o pid,ppid,stat,cmd
3824 2888 S+ ./zombie
3825 3824 Z+ [zombie] <defunct>
当一个程序结束的时候,其所有的子进程都被一个特殊的进程(init进程)继承。init进程负责回收所有它继承过来的僵死的子进程。当一个子进程结束的时候,将对其父进程发送SIGCHID信号。一个进程可以通过处理SIGCHID信号来回收子进程。
一个进程可以通过处理SIGCHID信号来回收子进程。
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
sig_atomic_t child_exit_status;
void clean_up_child_process (int signal_number)
{
??? /* Clean up the child process. */
??? int status;
??? wait (&status);
??? /* Store its exit status in a global variable. */
??? child_exit_status = status;
}
int main ()
{
??? /* Handle SIGCHLD by calling clean_up_child_process. */
??? struct sigaction sigchld_action;
??? memset (&sigchld_action, 0, sizeof (sigchld_action));
??? sigchld_action.sa_handler = &clean_up_child_process;
??? sigaction (SIGCHLD, &sigchld_action, NULL);
??? /* Now do things, including forking a child process. */
??? /* ... */
??? return 0;
}
?