进程
1. 替换进程镜像
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char*arg, ..., (char*)0);
int execlp(const char *file, const char*arg, ..., (char*)0);
int execle(const char *path, const char*arg, ..., char * const envp[]);
int execv(const char *path, char *constargv[]);
int execvp(const char *file, char *constargv[]);
int execve(const char *file, char *const argv[], char *const envp[]);
1) execl、execlp、execle参数可变,以空指针结束;execv、execvp第二个参数为一字符串数组。
2) 函数会把argv参数传递给main函数。
3) 以p结尾的函数会搜索PATH环境变量来查找新程序,不存在则使用绝对路径来传递给函数。
4) exec函数不会返回调用它的函数。
2. 复制进程对象
#include <unistd.h>
#include<sys/types.h>
pid_t fork(void);
新进程与原进程一模一样,执行代码完全一样,只是有自己的数据空间、环境以及文件描述符。
返回值:
-1:创建失败;
0:当前进程为子进程;
非零:当前进程为父进程。
3. 进程等待
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*stat_loc);
pid_t waitpid(pid_t pid, int* stat_loc, int options);
int waitid(idtype_tidtype, id_t id, siginfo_t *infop, int options);
暂停父进程知道子进程结束为止。
stat_loc:
WIFEXITED | 子进程正常结束,WIFEXITD(stat_loc)取非零值 |
WEXITSTATUS | WEXITSTATUS(stat_loc)非零时,为进程退出码 |
WIFSIGNALED | 如果子进程因一个为捕获的信号终止,WIFSIGNALED(stat_loc)非零 |
WTERMSIG | WTERMSIG(stat_loc)非零,则为信号代码 |
WCOREDUMP | 子进程意外终止,WCOREDUMP(stat_loc)取非零 |
WIFSTOPPED | WIFSTOPPED(stat_loc)非零,则为一个信号代码 |
4. exit
退出进程。
进程通讯、信号
Linux 提供的大多数信号类型是供内核使用的,只有少数的几种信号可以用作在进程之间传送。下面给出常用的信号和它们的意义:
SIGHUP
当终止一个终端时,内核就把这一种信号发送给该终端所控制的所有进程。通常情况
下,一个进程组的控制终端是该用户拥有的终端,但不完全是如此。当进程组的首进程结
束时,就会向该进程组的所有进程发送这种信号。这就可以保证当一个用户退出使用时,
其后台进程被终止,除非有其它方面的安排。
SIGINT
当一个用户按了中断键(一般为Ctrl+C)后,内核就向与该终端有关联的所有进程发
送这种信号。它提供了中止运行程序的简便方法。
SIGQUIT
这种信号与SIGINT 非常相似,当用户按了退出键时(为ASCII 码FS,通常为Ctrl+\),
内核就发送出这种信号。SIGQUIT 将形成POSIX 标准所描述的非正常终止。我们称这种
UNIX 实现的实际操作为核心转贮(core dump),并用信息“Quit (coredump)”指出这一操
作的发生。这时,该进程的映象被转贮到一个磁盘文件中,供调试之用。
SIGILL
当一个进程企图执行一条非法指令时,内核就发出这种信号。例如,在没有相应硬件
支撑的条件下,企图执行一条浮点指令时,则会引起这种信号的发生。SIGILL 和SIGQUIT
一样,也形成非正常终止。
SIGTRAP
这是一种由调试程序使用的专用信号。由于他的专用行和特殊性,我们不再对它作进一步的讨论。SIGTRAP 也形成非正常终止。
SIGFPE
当产生浮点错误时(比如溢出),内核就发出这种信号,它导致非正常终止。
SIGKILL
这是一个相当特殊的信号,它从一个进程发送到另一个进程,使接收到该信号的进程
终止。内核偶尔也会发出这种信号。SIGKILL 的特点是,它不能被忽略和捕捉,只能通过
用户定义的相应中断处理程序而处理该信号。因为其它的所有信号都能被忽略和捕捉,所
以只有这种信号能绝对保证终止一个进程。
SIGALRM
当一个定时器到时的时候,内核就向进程发送这个信号。定时器是由改进程自己用系
统调用alarm()设定的。
SIGTERM
这种信号是由系统提供给普通程序使用的,按照规定,它被用来终止一个进程。
SIGSTOP
这个信号使进程暂时中止运行,系统将控制权转回正在等待运行的下一个进程。
SIGUSR1 和SIGUSR2
和SIGTERM 一样,这两种信号不是内核发送的,可以用于用户所希望的任何目的。
SIGCHLD
子进程结束信号。UNIX 中用它来实现系统调用exit()和wait()。执行exit()时,就向子进程的父进程发送SIGCHLD 信号,如果这时父进程政在执行wait(),则它被唤醒;如果这时候父进程不是执行wait(),则此父进程不会捕捉SIGCHLD 信号,因此该信号不起作用,子进程进入过渡状态(如果父进程忽略SIGCHLD,子进程就结束而不会进入过渡状态)。这个机制对大多数UNIX 程序员来说是相当重要的。
信号与处理
1. 发送信号
#include <unistd.h>
unsigned int alarm(unisigned int seconds);在seconds后发送一个SIGALRM信号
intkill(pid_t pid, int sig);发送信号到指定进程
2. 信号处理
int signal(int sig, __sighandler_t handler);
sig: 指明了所要处理的信号类型,它可以取除了SIGKILL 和SIGSTOP 外的任何一种信号。
handler:可以取以下三种值:
1) 一个返回值为整数的函数地址。
此函数必须在signal()被调用前声明,handler 中为这个函数的名字。当接收到一个类型为sig 的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
int func(int sig);
sig 是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig 的信号,
不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制
权返回进程被中断的那一点继续执行。
2) SIG_IGN
这个符号表示忽略信号。执行了相应的signal()调用好,进程会忽略类型为sig 的信号。
3) SIG_DFL
这个符号表示恢复系统对信号的默认处理。
进程间通信(管道)
1. #include <unistd.h>
Int pipe(intfd[2]);
功能:获取两个管道描述符;
返回值:-1表示失败,0表示成功。
2. 标准I/O函数中的管道
#include<stdio.h>
FILE *popen(constchar *cmdstring, const char *type);
int pclose(FILE*pf);
popen的功能:创建一个管道,然后调用fork产生一个子进程,调用exec执行cmdstring命令,关闭管道不使用短,执行shell以运行命令,然后等待命令终止。
参数:
cmdstring :需要执行的shell命令。
type:若为”r”,命令连接至标准输出;若为”w”,命令连接至标准输入。
3.
多线程
1. 创建一个线程的函数:
#include<pthread.h>
intpthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立线程返回0,否则返回错误的编号
参数:
pthread_t*restrict tidp 要创建的线程的线程id指针
constpthread_attr_t *restrict attr 创建线程时的线程属性
void* (start_rtn)(void)返回值是void类型的指针函数
void *restrictarg start_rtn的形参
一般可以这样用:
pthread_create(&id2,NULL, (void*)myThread1, NULL);
2. 等待一个线程的结束:pthread_join()函数,以阻塞的方式等待thread指定的线程结束
#include<pthread.h>
int pthread_join(pthread_t thread, void**retval);
返回值:
0代表成功。 失败,返回的则是错误号
参数:
thread:线程标识符,即线程ID,标识唯一线程。
retval:用户定义的指针,用来存储被等待线程的返回值。
3. 退出线程
void pthread_exit(void *retval);
4. 取消线程
int pthread_cancel(pthread_t thread);
5. system
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命>令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
#i nclude<stdlib.h>
int system(const char *string);
返回值:
-1:出现错误
0:调用成功但是没有出现子进程
>0:成功退出的子进程的id
参数:
String:命令行
内核驱动程序
1. 内核驱动程序主要框架
内核驱动的开始的函数必须有为:
module_init(function)加载,和module_exit(function)卸载内核。
内核驱动的函数头都被定义到linux/或者asm/中。
2. module_init( x);
driver initialization entry point.
参数:
x function tobe run at kernel boot time or module insertion
3. module_exit( x);
driver exit entry point.
参数:
x: function to be runwhen driver is removed
4. outb() I/O 上写入 8 位数据 ( 1 字节 );
inb() 从I/O端口读取一个字节(即八位)
inw 从I/O端口读取一个字(即两个字节,十六位)
outw() I/O 上写入 16 位数据 ( 2 字节 );
outl () I/O 上写入 32 位数据 ( 4 字节)。
void outb (unsigned char data, unsigned short port);
byte inb(word port);返回一个字节;
void outw (unsigned short data, unsigned short port);
word inw(word port);返回两个字节;
void outl (unsigned long data, unsigned short port);
5. 每个设备都对应一个结构体:
struct file_operations {
struct module *owner;//指向拥有这个模块的指针,该成员用来在它的操作还在是使用的时候不允许卸载该模块。
//通常情况下简单初始化为THIS_MODULE。
loff_t (*llseek) (struct file *, loff_t,int); //该操作用来改变当前文件的读写位置,并且将新位置作为返回值。
ssize_t (*read) (struct file *, char __user*, size_t, loff_t *);//该操作用来从设备中获取数据。
ssize_t (*write) (struct file *, const char__user *, size_t, loff_t *);//该操作用来发送数据给设备。
ssize_t (*aio_read) (struct kiocb *, conststruct iovec *, unsigned long, loff_t); //该操作用来初始化一个异步的读操作。
ssize_t (*aio_write) (struct kiocb *, conststruct iovec *, unsigned long, loff_t);//该操作用来初始化一个异步的写操作。
int (*readdir) (struct file *, void *,filldir_t);//该操作用来读取目录。
unsigned int (*poll) (struct file *, structpoll_table_struct *);//该操作用来查询一个或者多个文件描述符的读或写是会否堵塞。
int (*ioctl) (struct inode *, struct file*, unsigned int, unsigned long);//该操作用来提供发出设备特定命令的方法。
long(*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *,unsigned int, unsigned long);
int (*mmap) (struct file *, structvm_area_struct *);//该操作用来请求将设备内存映射到进程的地址空间。
int (*open) (struct inode *, struct file*);//该操作用来打开设备文件,也是对设备进行的第一个操作。
int (*flush) (struct file *, fl_owner_tid);
int (*release) (struct inode *, struct file*);//该操作用来释放文件结构。可以为空。
int (*fsync) (struct file *, struct dentry*, int datasync);
int (*aio_fsync) (struct kiocb *, intdatasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, structfile_lock *);
ssize_t (*sendpage) (struct file *, structpage *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(structfile *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp,unsigned long arg);
int (*flock) (struct file *, int, structfile_lock *);
ssize_t (*splice_write)(structpipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *,loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, structfile_lock **);
int (*fsetattr)(struct file *, struct iattr*);
};
6. 将内核数据复制到用户空间中
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
n = __copy_to_user(to, from, n);
return n;
}