【本文谢绝转载,原文来自http://990487026.blog.51cto.com】
《大纲》 Linux系统编程 Linux 进程描述符结构体; 进程资源上限 进程环境变量 进程获取/修改环境变量 创建子进程fork() 最大进程数测试 程序的设置用户ID/组ID/黏住位 exec簇函数,执行程序覆盖堆栈 fork 与execl函数在一起 exec() 与主程序同一个PCB 僵尸进程 wait()回收僵尸进程 证明:父子进程同组pid waitpid() 非阻塞等待子线程发生变化 孤儿进程演示【父进程已经结束,子进程还在运行】
Linux 进程描述符结构体;
root@http://990487026.blog.51cto.com~# cat -n /usr/src/linux-headers-3.13.0-92-generic/include/linux/sched.h | grep "struct task_struct {" 1053 struct task_struct { root@http://990487026.blog.51cto.com~# sed -n '1053,$p' /usr/src/linux-headers-3.13.0-92-generic/include/linux/sched.h struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; atomic_t usage; unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; #ifdef CONFIG_SMP struct llist_node wake_entry; int on_cpu; struct task_struct *last_wakee; unsigned long wakee_flips; unsigned long wakee_flip_decay_ts; int wake_cpu; #endif int on_rq; int prio, static_prio, normal_prio; unsigned int rt_priority; const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; #ifdef CONFIG_CGROUP_SCHED struct task_group *sched_task_group; #endif #ifdef CONFIG_PREEMPT_NOTIFIERS /* list of struct preempt_notifier: */ struct hlist_head preempt_notifiers; #endif #ifdef CONFIG_BLK_DEV_IO_TRACE unsigned int btrace_seq; #endif unsigned int policy; int nr_cpus_allowed; cpumask_t cpus_allowed; #ifdef CONFIG_PREEMPT_RCU int rcu_read_lock_nesting; char rcu_read_unlock_special; struct list_head rcu_node_entry; #endif /* #ifdef CONFIG_PREEMPT_RCU */ #ifdef CONFIG_TREE_PREEMPT_RCU struct rcu_node *rcu_blocked_node; #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ #ifdef CONFIG_RCU_BOOST struct rt_mutex *rcu_boost_mutex; #endif /* #ifdef CONFIG_RCU_BOOST */ #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info; #endif struct list_head tasks; #ifdef CONFIG_SMP struct plist_node pushable_tasks; #endif struct mm_struct *mm, *active_mm; #ifdef CONFIG_COMPAT_BRK unsigned brk_randomized:1; #endif #if defined(SPLIT_RSS_COUNTING) struct task_rss_stat rss_stat; #endif /* task state */ int exit_state; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ unsigned int jobctl; /* JOBCTL_*, siglock protected */ /* Used for emulating ABI behavior of previous Linux versions */ unsigned int personality; unsigned did_exec:1; unsigned in_execve:1; /* Tell the LSMs that the process is doing an * execve */ unsigned in_iowait:1; /* Revert to default priority/policy when forking */ unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; unsigned long atomic_flags; /* Flags needing atomic access. */ pid_t pid; pid_t tgid; #ifdef CONFIG_CC_STACKPROTECTOR /* Canary value for the -fstack-protector gcc feature */ unsigned long stack_canary; #endif /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->real_parent->pid) */ struct task_struct __rcu *real_parent; /* real parent process */ struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */ /* * children/sibling forms the list of my natural children */ struct list_head children; /* list of my children */ struct list_head sibling; /* linkage in my parent's children list */ struct task_struct *group_leader; /* threadgroup leader */ /* * ptraced is the list of tasks this task is using ptrace on. * This includes both natural children and PTRACE_ATTACH targets. * p->ptrace_entry is p's link on the p->parent->ptraced list. */ struct list_head ptraced; struct list_head ptrace_entry; /* PID/PID hash table linkage. */ struct pid_link pids[PIDTYPE_MAX]; struct list_head thread_group; struct list_head thread_node; struct completion *vfork_done; /* for vfork() */ int __user *set_child_tid; /* CLONE_CHILD_SETTID */ int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */ cputime_t utime, stime, utimescaled, stimescaled; cputime_t gtime; #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE struct cputime prev_cputime; #endif #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN seqlock_t vtime_seqlock; unsigned long long vtime_snap; enum { VTIME_SLEEPING = 0, VTIME_USER, VTIME_SYS, } vtime_snap_whence; #endif unsigned long nvcsw, nivcsw; /* context switch counts */ struct timespec start_time; /* monotonic time */ struct timespec real_start_time; /* boot based time */ /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt; struct task_cputime cputime_expires; struct list_head cpu_timers[3]; /* process credentials */ const struct cred __rcu *real_cred; /* objective and real subjective task * credentials (COW) */ const struct cred __rcu *cred; /* effective (overridable) subjective task * credentials (COW) */ char comm[TASK_COMM_LEN]; /* executable name excluding path - access with [gs]et_task_comm (which lock it with task_lock()) - initialized normally by setup_new_exec */ /* file system info */ int link_count, total_link_count; #ifdef CONFIG_SYSVIPC /* ipc stuff */ struct sysv_sem sysvsem; #endif #ifdef CONFIG_DETECT_HUNG_TASK /* hung task detection */ unsigned long last_switch_count; #endif /* CPU-specific state of this task */ struct thread_struct thread; /* filesystem information */ struct fs_struct *fs; /* open file information */ struct files_struct *files; /* namespaces */ struct nsproxy *nsproxy; /* signal handlers */ struct signal_struct *signal; struct sighand_struct *sighand; sigset_t blocked, real_blocked; sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ struct sigpending pending; unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; struct callback_head *task_works; struct audit_context *audit_context; #ifdef CONFIG_AUDITSYSCALL kuid_t loginuid; unsigned int sessionid; #endif struct seccomp seccomp; /* Thread group tracking */ u32 parent_exec_id; u32 self_exec_id; /* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, * mempolicy */ spinlock_t alloc_lock; /* Protection of the PI data structures: */ raw_spinlock_t pi_lock; #ifdef CONFIG_RT_MUTEXES /* PI waiters blocked on a rt_mutex held by this task */ struct plist_head pi_waiters; /* Deadlock detection and priority inheritance handling */ struct rt_mutex_waiter *pi_blocked_on; #endif #ifdef CONFIG_DEBUG_MUTEXES /* mutex deadlock detection */ struct mutex_waiter *blocked_on; #endif #ifdef CONFIG_TRACE_IRQFLAGS unsigned int irq_events; unsigned long hardirq_enable_ip; unsigned long hardirq_disable_ip; unsigned int hardirq_enable_event; unsigned int hardirq_disable_event; int hardirqs_enabled; int hardirq_context; unsigned long softirq_disable_ip; unsigned long softirq_enable_ip; unsigned int softirq_disable_event; unsigned int softirq_enable_event; int softirqs_enabled; int softirq_context; #endif #ifdef CONFIG_LOCKDEP # define MAX_LOCK_DEPTH 48UL u64 curr_chain_key; int lockdep_depth; unsigned int lockdep_recursion; struct held_lock held_locks[MAX_LOCK_DEPTH]; gfp_t lockdep_reclaim_gfp; #endif /* journalling filesystem info */ void *journal_info; /* stacked block device info */ struct bio_list *bio_list; #ifdef CONFIG_BLOCK /* stack plugging */ struct blk_plug *plug; #endif /* VM state */ struct reclaim_state *reclaim_state; struct backing_dev_info *backing_dev_info; struct io_context *io_context; unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ struct task_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ cputime_t acct_timexpd; /* stime + utime since last update */ #endif #ifdef CONFIG_CPUSETS nodemask_t mems_allowed; /* Protected by alloc_lock */ seqcount_t mems_allowed_seq; /* Seqence no to catch updates */ int cpuset_mem_spread_rotor; int cpuset_slab_spread_rotor; #endif #ifdef CONFIG_CGROUPS /* Control Group info protected by css_set_lock */ struct css_set __rcu *cgroups; /* cg_list protected by css_set_lock and tsk->alloc_lock */ struct list_head cg_list; #endif #ifdef CONFIG_FUTEX struct robust_list_head __user *robust_list; #ifdef CONFIG_COMPAT struct compat_robust_list_head __user *compat_robust_list; #endif struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; #endif #ifdef CONFIG_PERF_EVENTS struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts]; struct mutex perf_event_mutex; struct list_head perf_event_list; #endif #ifdef CONFIG_NUMA struct mempolicy *mempolicy; /* Protected by alloc_lock */ short il_next; short pref_node_fork; #endif #ifdef CONFIG_NUMA_BALANCING int numa_scan_seq; unsigned int numa_scan_period; unsigned int numa_scan_period_max; int numa_preferred_nid; int numa_migrate_deferred; unsigned long numa_migrate_retry; u64 node_stamp; /* migration stamp */ struct callback_head numa_work; struct list_head numa_entry; struct numa_group *numa_group; /* * Exponential decaying average of faults on a per-node basis. * Scheduling placement decisions are made based on the these counts. * The values remain static for the duration of a PTE scan */ unsigned long *numa_faults; unsigned long total_numa_faults; /* * numa_faults_buffer records faults per node during the current * scan window. When the scan completes, the counts in numa_faults * decay and these values are copied. */ unsigned long *numa_faults_buffer; /* * numa_faults_locality tracks if faults recorded during the last * scan window were remote/local. The task scan period is adapted * based on the locality of the faults with different weights * depending on whether they were shared or private faults */ unsigned long numa_faults_locality[2]; unsigned long numa_pages_migrated; #endif /* CONFIG_NUMA_BALANCING */ struct rcu_head rcu; /* * cache last used pipe for splice */ struct pipe_inode_info *splice_pipe; struct page_frag task_frag; #ifdef CONFIG_TASK_DELAY_ACCT struct task_delay_info *delays; #endif #ifdef CONFIG_FAULT_INJECTION int make_it_fail; #endif /* * when (nr_dirtied >= nr_dirtied_pause), it's time to call * balance_dirty_pages() for some dirty throttling pause */ int nr_dirtied; int nr_dirtied_pause; unsigned long dirty_paused_when; /* start of a write-and-pause period */ #ifdef CONFIG_LATENCYTOP int latency_record_count; struct latency_record latency_record[LT_SAVECOUNT]; #endif /* * time slack values; these are used to round up poll() and * select() etc timeout values. These are in nanoseconds. */ unsigned long timer_slack_ns; unsigned long default_timer_slack_ns; #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* Index of current stored address in ret_stack */ int curr_ret_stack; /* Stack of return addresses for return function tracing */ struct ftrace_ret_stack *ret_stack; /* time stamp for last schedule */ unsigned long long ftrace_timestamp; /* * Number of functions that haven't been traced * because of depth overrun. */ atomic_t trace_overrun; /* Pause for the tracing */ atomic_t tracing_graph_pause; #endif #ifdef CONFIG_TRACING /* state flags for use by tracers */ unsigned long trace; /* bitmask and counter of trace recursion */ unsigned long trace_recursion; #endif /* CONFIG_TRACING */ #ifdef CONFIG_MEMCG /* memcg uses this to do batch job */ struct memcg_batch_info { int do_batch; /* incremented when batch uncharge started */ struct mem_cgroup *memcg; /* target memcg of uncharge */ unsigned long nr_pages; /* uncharged usage */ unsigned long memsw_nr_pages; /* uncharged mem+swap usage */ } memcg_batch; unsigned int memcg_kmem_skip_account; struct memcg_oom_info { struct mem_cgroup *memcg; gfp_t gfp_mask; int order; unsigned int may_oom:1; } memcg_oom; #endif #ifdef CONFIG_UPROBES struct uprobe_task *utask; #endif #if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE) unsigned int sequential_io; unsigned int sequential_io_avg; #endif };
进程资源上限值
chunli@ubuntu16:~$ cat /proc/self/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 3749 3749 processes Max open files 1024 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 3749 3749 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us chunli@ubuntu16:~$
进程获取环境变量
chunli@ubuntu16:~/linux_c$ cat main.c #include <stdio.h> int main(int num,char **args,char **env) { int i = 0; for(i = 0;env[i] != NULL;i++) { printf("%s\n",env[i]); } return 0; } chunli@ubuntu16:~/linux_c$ gcc main.c && ./a.out XDG_SESSION_ID=8 TERM=xterm SHELL=/bin/bash SSH_CLIENT=10.11.12.1 2826 22 SSH_TTY=/dev/pts/9 USER=chunli LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: MAIL=/var/mail/chunli PATH=/home/chunli/bin:/home/chunli/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin QT_QPA_PLATFORMTHEME=appmenu-qt5 PWD=/home/chunli/linux_c LANG=zh_CN.UTF-8 SHLVL=1 HOME=/home/chunli LANGUAGE=zh_CN:zh LOGNAME=chunli SSH_CONNECTION=10.11.12.1 2826 10.11.12.21 22 LESSOPEN=| /usr/bin/lesspipe %s XDG_RUNTIME_DIR=/run/user/1000 LESSCLOSE=/usr/bin/lesspipe %s %s _=./a.out OLDPWD=/home/chunli chunli@ubuntu16:~/linux_c$
getenv(),setenv()获取修改进程的环境变量
chunli@ubuntu16:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> int main(int num,char **args,char **env) { printf("PATH=%s\n", getenv("PATH")); setenv("PATH","Hello World!",1); printf("PATH=%s\n", getenv("PATH")); return 0; } chunli@ubuntu16:~/linux_c$ gcc main.c && ./a.out PATH=/home/chunli/bin:/home/chunli/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin PATH=Hello World! chunli@ubuntu16:~/linux_c$
fork()进程创建
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { int i = 0; //变量,读时共享,写时复制.把子进程需要的变量才复制到子进程一份 printf("主函数\n"); pid_t pid; pid = fork(); //子父进程诞生,从下面的语句开始运行,不再运行本条及上面的语句 //子进程,仅仅是pid不一样,开始执行语句的位置也不一样 if(pid > 0) { while(1) { printf("父进程 %d \n",i++); //共享变量,读共享,写操作才会复制一份 printf("父进程的pid =%d \n",getpid()); printf("父进程的ppid =%d \n",getppid()); sleep(5); } } else if(pid == 0) { while(1) { printf("子进程 %d \n",i++); printf("子进程的pid =%d \n",getpid()); printf("子进程的ppid =%d \n",getppid()); sleep(2); } } else { printf("进程创建失败\n"); } return 0; } chunli@ubuntu:~/linux_c$ chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out 主函数 父进程 0 父进程的pid =6189 父进程的ppid =5811 子进程 0 子进程的pid =6190 子进程的ppid =6189 子进程 1 子进程的pid =6190 子进程的ppid =6189 子进程 2 子进程的pid =6190 子进程的ppid =6189 父进程 1 父进程的pid =6189 父进程的ppid =5811 子进程 3 子进程的pid =6190 子进程的ppid =6189 ^C
测试最多能开启线程数
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { int time = 50; //让子进程存活n 秒 int n = 0; pid_t pid; while(1) { pid = fork(); if(pid == 0) { break; //如果是子进程,就跳出来 } if(pid < 0) { printf("\nfork 出错\n"); exit(1); } //printf("%d\t",n++); //如果启用这一句,会执行两次输出 printf("%d\n",n++); } while(time--) { sleep(1); //子进程干的事儿 //printf("."); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out ............................... 3735 3736 3737 fork 出错
设置用户ID,执行文件时以文件所有者运行
比如passwd命令,普通用户执行这个命令,修改的密码放在/etc/shadow文件中,
只有root权限才可以修改文件的内容
chunli@ubuntu:~$ which passwd /usr/bin/passwd chunli@ubuntu:~$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 54256 3月 29 17:25 /usr/bin/passwd chunli@ubuntu:~$ chunli@ubuntu:~$ ls -l /etc/shadow -rw-r----- 1 root shadow 1273 8月 2 19:19 /etc/shadow
文件所属chunli,chuli来运行,没问题
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { int fd = 0; fd = open("./abc",O_CREAT,0644);//如果没有这个文件就创建 if(fd == -1) { perror("文件打开/创建\n"); } printf("运行程序的实际用户id是%d\n",getuid()); printf("运行程序的有效用户id是%d\n",geteuid()); return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out 运行程序的实际用户id是1000 运行程序的有效用户id是1000 chunli@ubuntu:~/linux_c$ ll 总用量 16K -rw-r--r-- 1 chunli chunli 0 8月 5 11:31 abc -rwxrwxr-x 1 chunli chunli 8.7K 8月 5 11:31 a.out* -rw-rw-r-- 1 chunli chunli 456 8月 5 11:30 main.c
把上面的可执行程序放在根目录,无法创建文件
chunli@ubuntu:~/linux_c$ cp ./a.out / cp: 无法创建普通文件'/a.out': 权限不够 chunli@ubuntu:~/linux_c$ sudo cp ./a.out / [sudo] chunli 的密码: chunli@ubuntu:~/linux_c$ ll /a.out -rwxr-xr-x 1 root root 8.7K 8月 5 11:32 /a.out* chunli@ubuntu:~/linux_c$ chunli@ubuntu:/$ cd / chunli@ubuntu:/$ ./a.out 文件打开/创建: Permission denied 运行程序的实际用户id是1000 运行程序的有效用户id是1000
设置用户ID,程序运行时以root身份运行,可以创建文化
chunli@ubuntu:/$ sudo chmod 4611 a.out chunli@ubuntu:/$ ll a.out -rwS--x--x 1 root root 8.7K 8月 5 11:34 a.out* chunli@ubuntu:/$ ./a.out 运行程序的实际用户id是1000 运行程序的有效用户id是0 chunli@ubuntu:/$ ll abc -rw-r--r-- 1 root chunli 0 8月 5 11:37 abc 设置用户id和组ID chunli@ubuntu:/$ sudo chmod 6611 a.out chunli@ubuntu:/$ ll a.out -rwS--s--x 1 root root 8.7K 8月 5 11:34 a.out* chunli@ubuntu:/$ 还有一个粘住位,常驻内存程序 chunli@ubuntu:/$ sudo chmod 7777 a.out chunli@ubuntu:/$ ll a.out -rwsrwsrwt 1 root root 8.7K 8月 5 11:34 a.out* chunli@ubuntu:/$
exec簇函数
#include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ... /* (char *) NULL */); int execlp(const char *file, const char *arg, ... /* (char *) NULL */); int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[], char *const envp[]);
execl执行一个程序,完全替换掉当前程序的堆栈
exec下面的语句没有机会执行了
chunli@ubuntu:~/linux_c$ ll 总用量 16K -rwxrwxr-x 1 chunli chunli 8.5K 8月 5 11:56 a.out* -rw-rw-r-- 1 chunli chunli 277 8月 5 11:56 main.c chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { printf("Hello \n"); execl("/bin/ls","ls","-l",NULL);//下面的代码没有机会执行,程序从ls返回 printf("World \n"); return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out Hello 总用量 16 -rwxrwxr-x 1 chunli chunli 8656 8月 5 11:56 a.out -rw-rw-r-- 1 chunli chunli 277 8月 5 11:56 main.c chunli@ubuntu:~/linux_c$
证明:execl函数下面的代码没有机会执行
chunli@ubuntu:~/linux_c$ cat test.c int main() { return 9; //测试程序,返回值是9 } chunli@ubuntu:~/linux_c$ gcc test.c -o test chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { printf("Hello \n"); execl("/home/chunli/linux_c/test","./test",NULL); printf("World \n"); return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out Hello chunli@ubuntu:~/linux_c$ echo $? 9
fork() execl()在一起演示
子进程加载其他程序,替换当前的程序代码段
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { pid_t pid = fork(); if(pid == 0 ) { execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);//参数1,2,3 } if(pid > 0) { printf("I'm parent \n"); sleep(1); } if(pid < 0) { perror("fork"); exit(2); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm parent Error: GDK_BACKEND does not match available displays chunli@ubuntu:~/linux_c$
execlp在PATH里面找可执行程序
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { pid_t pid = fork(); if(pid == 0 ) { execlp("ls","ls","-lh",NULL);//参数1,2,3 } if(pid > 0) { printf("I'm parent \n"); sleep(1); } if(pid < 0) { perror("fork"); exit(2); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm parent 总用量 32K -rwxrwxr-x 1 chunli chunli 8.7K 8月 5 13:47 a.out -rw-rw-r-- 1 chunli chunli 396 8月 5 13:47 main.c -rwxrwxr-x 1 chunli chunli 8.4K 8月 5 12:04 test -rw-rw-r-- 1 chunli chunli 26 8月 5 12:04 test.c chunli@ubuntu:~/linux_c$
execvp()函数,指针数组的形式
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { char *str[]={"ls","-l",NULL}; pid_t pid = fork(); if(pid == 0 ) { execvp("ls",str);//参数1,2,3 } if(pid > 0) { printf("I'm parent \n"); sleep(1); } if(pid < 0) { perror("fork"); exit(2); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm parent 总用量 32 -rwxrwxr-x 1 chunli chunli 8920 8月 5 13:55 a.out -rw-rw-r-- 1 chunli chunli 415 8月 5 13:55 main.c -rwxrwxr-x 1 chunli chunli 8552 8月 5 12:04 test -rw-rw-r-- 1 chunli chunli 26 8月 5 12:04 test.c chunli@ubuntu:~/linux_c$
execle()不仅把代码堆栈替换,还把命令行参数环境变量也替换
exec() 与主程序同一个PCB【看图】
chunli@ubuntu:~/linux_c$ cat test.c #include <stdio.h> #include <ctype.h> int main() { int ch = 0; while((ch = getchar()) != EOF) { putchar(toupper(ch)); } return 0; } chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int argc,char **args,char **env) { int fd = 0; if(argc < 2) { fputs("需要1个参数\n",stdout); exit(1); } fd = open(args[1],O_RDONLY); //fd = 3,指向了文件结构体 if(fd < 0) { perror("open"); exit(1); } dup2(fd,STDIN_FILENO); //关闭了标准输入,fd就是0 close(fd); //关闭文件描述符与文件结构体的关系 execl("./upper","upper",NULL); //execl的程序与主程序公用PCB,PCB里面有文件描述符表 //upper里面的getchar操作从文件描述符为0的文件读内容 perror("exec ./upper"); exit(2); return 0; } chunli@ubuntu:~/linux_c$ chunli@ubuntu:~/linux_c$ gcc test.c -o upper chunli@ubuntu:~/linux_c$ gcc main.c -o main chunli@ubuntu:~/linux_c$ ./main 需要1个参数 chunli@ubuntu:~/linux_c$ gcc main.c && ./main main.c #INCLUDE <STDIO.H> #INCLUDE <STDLIB.H> #INCLUDE <SYS/TYPES.H> #INCLUDE <SYS/STAT.H> #INCLUDE <FCNTL.H> #INCLUDE <UNISTD.H> #INCLUDE <ERRNO.H> INT MAIN(INT ARGC,CHAR **ARGS,CHAR **ENV) { INT FD = 0; IF(ARGC < 2) { FPUTS("需要1个参数\N",STDOUT); EXIT(1); } FD = OPEN(ARGS[1],O_RDONLY); //FD = 3,指向了文件结构体 IF(FD < 0) { PERROR("OPEN"); EXIT(1); } DUP2(FD,STDIN_FILENO); //关闭了标准输入,FD就是0 CLOSE(FD); //关闭文件描述符与文件结构体的关系 EXECL("./UPPER","UPPER",NULL); //EXECL的程序与主程序公用PCB,PCB里面有文件描述符表 //UPPER里面的GETCHAR操作从文件描述符为0的文件读内容 PERROR("EXEC ./UPPER"); EXIT(2); RETURN 0; } chunli@ubuntu:~/linux_c$
僵尸进程 演示
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,
那么他将变成一个僵尸进程。
子进程运行结束,内核PCB并没有被释放,等待回收
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main(int num,char **args,char **env) { pid_t pid; pid = fork(); if(pid > 0) { while(1) sleep(5); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out chunli 5908 0.0 0.0 4224 656 pts/1 S+ 15:06 0:00 ./a.out chunli 5909 0.0 0.0 0 0 pts/1 Z+ 15:06 0:00 [a.out] <defunct> chunli 5913 0.0 0.3 44432 3176 pts/21 R+ 15:06 0:00 ps aux
wait()回收僵尸进程
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> int main(int num,char **args,char **env) { pid_t pid; pid_t pid_c; pid = fork(); if(pid > 0) { while(1) { printf("I'm parent %d\n",getpid()); pid_c = wait(NULL); //阻塞函数,等待子进程死 printf("wait for child %d\n",pid_c); sleep(5); } } else if(pid == 0) { printf("I'm child %d\n",getpid()); sleep(2); } return 0; } chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm parent 6265 I'm child 6266 wait for child 6266 I'm parent 6265 wait for child -1 I'm parent 6265 wait for child -1 ^C chunli@ubuntu:~/linux_c$ chunli 6104 0.0 0.0 4356 744 pts/1 S+ 16:03 0:00 ./a.out chunli 6108 0.0 0.3 44432 3224 pts/21 R+ 16:03 0:00 ps aux
证明:父子进程同组pid
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> int main(int num,char **args,char **env) { int i = 4; pid_t pid; pid_t pid_c; while(i--) { pid = fork(); if(pid == 0) { break; } } if(pid > 0) { printf("I'm parent %d\n",getpid()); wait(NULL); sleep(30); } if(pid == 0) { printf("I'm child %d\n",getpid()); sleep(3); } return 0; } 编译运行: chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm parent 6416 I'm child 6420 I'm child 6419 I'm child 6417 I'm child 6418 chunli@ubuntu:~/linux_c$ ps ajx PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 5242 6433 6433 5242 pts/1 6433 S+ 1000 0:00 ./a.out 6433 6434 6433 5242 pts/1 6433 S+ 1000 0:00 ./a.out 6433 6435 6433 5242 pts/1 6433 S+ 1000 0:00 ./a.out 6433 6436 6433 5242 pts/1 6433 S+ 1000 0:00 ./a.out 6433 6437 6433 5242 pts/1 6433 S+ 1000 0:00 ./a.out 5673 6438 6438 5673 pts/21 6438 R+ 1000 0:00 ps ajx 可以看出,PGID都是一样 chunli@ubuntu:~/linux_c$ man 2 wait < -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid. 从这句话意思 chunli@ubuntu:~/linux_c$ kill -9 6433 chunli@ubuntu:~/linux_c$ ps ajx | grep a.out chunli@ubuntu:~/linux_c$ 全部杀干净了
waitpid() 非阻塞等待子线程发生变化
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> int main(int num,char **args,char **env) { int i = 3; pid_t pid; pid_t pid_c; while(i--) { pid = fork(); if(pid == 0) { break; } } if(pid > 0) { i = 0; while(1) { sleep(1); printf("I'm parent %d\n",getpid()); pid_c = waitpid(0,NULL,WNOHANG); //非阻塞,子进程状态发生改变也会有返回值 //如果子进程没有发生变化返回0 //如果子进程发生改变返回子进程的pid if(pid_c == -1) { continue; } else if(pid_c == 0) { printf("子进程的状态没有发生变化 %d \n",pid_c); //如果子进程没有发生变化返回0 } else if(pid_c > 0) { printf("子进程的状态发生变化 %d \n",pid_c); //如果子进程没有发生变化返回0 } //sleep(1); //邪门呀,sleep放在这里一直刷屏 } } if(pid == 0) { printf("I'm child %d\n",getpid()); sleep(6); } return 0; } // pid_t waitpid(pid_t pid, int *status, int options); // wait for process to change state // < -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid. // -1 meaning wait for any child process. // 0 meaning wait for any child process whose process group ID is equal to that of the calling process. // > 0 meaning wait for the child whose process ID is equal to the value of pid. // The value of options is an OR of zero or more of the following constants: // RETURN VALUE // waitpid(): on success, returns the process ID of the child whose state has changed; // if WNOHANG was specified and one or more child(ren) specified by pid exist, // but have not yet changed state, then 0 is returned. On error, -1 is returned. chunli@ubuntu:~/linux_c$ gcc main.c && ./a.out I'm child 7004 I'm child 7006 I'm child 7005 I'm parent 7003 子进程的状态没有发生变化 0 I'm parent 7003 子进程的状态没有发生变化 0 I'm parent 7003 子进程的状态没有发生变化 0 I'm parent 7003 子进程的状态没有发生变化 0 I'm parent 7003 子进程的状态没有发生变化 0 I'm parent 7003 子进程的状态发生变化 7004 I'm parent 7003 子进程的状态发生变化 7005 I'm parent 7003 子进程的状态发生变化 7006 I'm parent 7003 I'm parent 7003 I'm parent 7003 I'm parent 7003 ^C chunli@ubuntu:~/linux_c$
孤儿进程演示【父进程已经结束,子进程还在运行】
chunli@ubuntu:~/linux_c$ cat main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> int main(int num,char **args,char **env) { int i = 3; pid_t pid; pid_t pid_c; while(i--) { pid = fork(); if(pid == 0) { break; } } if(pid > 0) { printf("我是父进程 %d\n",getpid()); exit(1); } else if(pid == 0) { i = 100; while(i--) { sleep(1); printf("我是子进程 %d 我的父进程是%d\n",getpid(),getppid()); } } else { perror("fork "); exit(1); } return 0; } chunli@ubuntu:~/linux_c$ 编译运行: chunli@ubuntu:~/linux_c$ gcc main.c -omain && ./main 我是父进程 4757 chunli@ubuntu:~/linux_c$ 我是子进程 4760 我的父进程是1 我是子进程 4759 我的父进程是1 我是子进程 4758 我的父进程是1 我是子进程 4760 我的父进程是1 我是子进程 4759 我的父进程是1 我是子进程 4758 我的父进程是1 我是子进程 4760 我的父进程是1 我是子进程 4759 我的父进程是1 chunli@ubuntu:~$ ps ajx PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 4803 4804 4804 4804 pts/9 4804 Ss+ 1000 0:00 -bash 1 4826 4825 4804 pts/9 4804 S 1000 0:00 ./a.out 1 4827 4825 4804 pts/9 4804 S 1000 0:00 ./a.out 1 4828 4825 4804 pts/9 4804 S 1000 0:00 ./a.out 4699 4829 4829 4699 pts/10 4829 R+ 1000 0:00 ps ajx 只有结束组pid这么干了 chunli@ubuntu:~$ kill -9 -4825