Unix环境高级编程代码(实时更新)

实例1-3 列出一个目录中所有文件(ls.c)

#include "apue.h"
#include <dirent.h>

int main(int argc, char *argv[]){
	DIR *dp;
	struct dirent *dirp;
	if(argc != 2)
		err_quit("ls dir");
	if((dp = opendir(argv[1])) == NULL)
		err_sys("can not open %s", argv[1]);
	while((dirp = readdir(dp)) != NULL)
		printf("%s\n", dirp->d_name);
	closedir(dp);
	exit(0);
}

关于apue.h得从官网获取源码:http://www.apuebook.com/src.3e.tar.gz

然后复制include下的apue.h和error.c到ls.c同目录下

用gcc编译出二进制文件:

gcc ls.c error.c -o ls

执行二进制文件可以看到文件名被输出了(包括隐藏文件):

 实例1-4 将标准输入复制到标准输出中(example2.c)

#include "apue.h"

#define BUFFSIZE 4096

int main(void){
	int n; //要读写的字节数
	char buf[BUFFSIZE];
	while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
		if(write(STDOUT_FILENO, buf, n) != n)
			err_sys("write error");
	if(n < 0){
		err_sys("read error");
	}
	exit(0);
}

 其中:

#include <unistd.h>
ssize_t read(int filedes, void *buf, size_t nbytes);
// 返回:若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
// filedes:文件描述符
// buf:读取数据缓存区
// nbytes:要读取的字节数
#include <unistd.h>
ssize_t write(int filedes, void *buf, size_t nbytes);
// 返回:若成功则返回写入的字节数,若出错则返回-1
// filedes:文件描述符
// buf:待写入数据缓存区
// nbytes:要写入的字节数

 STDIN_FILENOSTDOUT_FILENO分别是标准输入和标准输出的文件描述符,被定义在unistd.h中。

编译:

gcc example2.c error.c

执行:

./a.out > data

执行上述指令后后面键入命令行的任何内容会被保存到data文件中(不使用重定向则会输出你键入的内容两次)

 

 指令./a.out < file1 > file2 则会把file1中的内容复制到file2中

类似的功能还可以用getc和putc(这两个函数包含在stdio.h中)实现:

#include "apue.h"

#define BUFFSIZE 4096

int main(void){
	int c; //要读写的字符数
	
	while((c = getc(stdin)) != EOF)
		if(putc(c, stdout) == EOF)
			err_sys("output error");
	if(ferror(stdin)){
		err_sys("input error");
	}
	exit(0);
}

 实例1-6 获取自身进程的pid

#include "apue.h"

int main(){
	printf("my pid is: %ld\n", (long) getpid());
	exit(0);
}

实例1-7 从标准输入读取命令,然后执行这些命令

#include "apue.h"
#include <sys/wait.h>

void sig_int(int signo){
	printf("interrupt\n%% ");
}


int main(){
	char buf[MAXLINE]; //MAXLINE来自apue.h
	pid_t pid;
	int status;

   if(signal(SIGINT, sig_int) == SIG_ERR)
		err_sys("signal error");

	printf("%%");

	while(fgets(buf, MAXLINE, stdin) != NULL){
		if(buf[strlen(buf) - 1] == '\n')
			buf[strlen(buf) - 1] = 0; //用null替换换行符
		if((pid = fork()) < 0){
			err_sys("fork error");
		}else if(pid == 0){
			//在子进程中执行
			execlp(buf, buf, (char *) 0);
			//若上述指令执行成功则终止,若执行失败则继续往下执行
			err_ret("executr %s failed", buf); //报错,执行指令失败
			exit(127);
		}

		if((pid = waitpid(pid, &status, 0)) < 0)
			err_sys("waitpid error");
		printf("%%");
	}
	exit(0);
}

其中:

fork()函数会创建一个子进程,相当于父进程的一个副本,fork()函数返回子进程的pid给父进程,返回0给子进程,如果创建失败则返回负数。并且!!fork函数创建的子进程并不是从头开始执行,而是把进程当前的情况拷贝一份,执行fork时,进程已经执行完了上面的代码,所以只需要拷贝下面要执行的代码到新的进程执行。

execlp函数要求指令字符串以null结尾,而不是换行符\n。

execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束,即(char *)0。

如果execlp()函数执行成功,进程自己的执行代码就会变成加载程序的代码(exec()族函数用一个新的进程映像替换当前进程映像),execlp()后边的代码也就不会执行了。

waitpid函数对等待pid对应进程执行完毕。

signal(参数1,参数2);

参数1:我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号时系统定义的宏。

参数2:我们处理的方式(是系统默认还是忽略还是捕获)

实例3-2 创建一个具有空洞的文件

#include "apue.h"
#include <fcntl.h> //creat函数
#include <unistd.h> //write, lseek函数

char buf1[] = "AAAAAAAAAA";
char buf2[] = "bbbbbbbbAA";

int main(){
	int fd; //文件描述符

	if((fd = creat("file.hole", FILE_MODE)) < 0)
		err_sys("creat error"); //创建文件失败

	if(write(fd, buf1, 10) != 10)
		err_sys("write error");

	printf("ofset 10 is: %ld\n", (long) lseek(fd, 0, SEEK_CUR));

	if(lseek(fd, 16384, SEEK_SET) == -1)
		err_sys("lseek error");

	if(write(fd ,buf2, 10) != 10)
		err_sys("write error");

	printf("ofset 16384 is: %ld\n", (long) lseek(fd, 0, SEEK_CUR));

	exit(0);
}

creat(const char* path, mode_t mode); 用于创建一个文件,并且以只写方式打开,返回文件描述符,来自fcntl.h。

lseek(int fd, off_t offset, int whence); 用于设置文件的偏移量,返回文件的当前偏移量。

whence有SEEK_SET, SEEK_CUR, SEEK_END三种状态。

 实例3-11 对指定的描述符打印文件标志

#include "apue.h"
#include <fcntl.h>

int main(int argc, char *argv[]){
	int val; //接收状态标志

	if(argc != 2)
		err_quit("./a.out fd");
	
	if((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0)
		err_sys("fcntl error for fd: %d", atoi(argv[1]));

	switch(val & O_ACCMODE){
		case O_RDONLY:
			printf("read only");
			break;
		case O_WRONLY:
			printf("write only");
			break;
		case O_RDWR:
			printf("read write");
			break;
		default:
			err_dump("unknow access mode");
	}
	if(val & O_APPEND)
		printf(", append");
	if(val & O_NONBLOCK)
		printf(", nonblocking");
	if(val & O_SYNC)
		printf(", synchrinous writes");

#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
	if(val & O_FSYNC)
		printf(", synchronous writes");
#endif
	putchar('\n');
	exit(0);
}

 其中:

#include <fcntl.h>
int fcntl(int fd, int cmd, ... /*int arg*/);
// 返回:若成功则返回的结果依赖于cmd,若出错则返回-1
// fd:文件描述符
// cmd:指令,工具不同的指令可以实现不同的操作,返回不同的结果
// arg:目前用不到

对fcntl函数设置不同的cmd可以实现不同的功能,代码中cmd=F_GETFL,实现了获取文件状态标志的功能。

 

  实例4-3 针对每一个命令行参数打印其文件类型

#include "apue.h"

int main(int argc, char *argv[]){
	int i;
	struct stat buf; //
	char *ptr; //字符串

	for(i = 1;i < argc;i++){
		printf("%s: ", argv[i]); //打印文件名
		if(lstat(argv[i], &buf) < 0){
			err_ret("lstat error");
			continue;
		}
		if(S_ISREG(buf.st_mode))
			ptr = "regular";
		else if(S_ISDIR(buf.st_mode))
			ptr = "directory";
		else if(S_ISCHR(buf.st_mode))
                        ptr = "character special";
		else if(S_ISBLK(buf.st_mode))
                        ptr = "block special";
		else if(S_ISFIFO(buf.st_mode))
                        ptr = "fifo";
		else if(S_ISLNK(buf.st_mode))
                        ptr = "symbolic link";
		else if(S_ISSOCK(buf.st_mode))
                        ptr = "socket";
		else
                        ptr = "**unknow**";
		printf("%s\n", ptr);
	}
	exit(0);
}

 其中:

#include <sys/stat.h>
int lstat(const char *restrict pathname, struct stat *restrict buf);
//成功返回0,失败返回-1
//pathname:文件路径
//buf:stat结构,包含了文件的各种信息

  实例4-9 使用umask函数改变文件权限

#include "apue.h"
#include <fcntl.h>

#define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

int main(){
	umask(0); //不创建屏蔽字
	if(creat("foo", RWRWRW) < 0)
		err_sys("creat foo error");
	if(access("/root/unix/foo", R_OK) < 0)
		printf("read access error for foo");
	else 
		printf("read access ok\n");

	umask(S_IRUSR | S_IWUSR); //屏蔽用户读和写
	if(creat("bar", RWRWRW) < 0)
                err_sys("creat bar error");
        if(access("/root/unix/bar", R_OK) < 0)
                printf("read access error for bar");
        else
                printf("read access ok\n");
	exit(0);
}

其中:

#include <unistd.h>
int access(const char *pathname, int mode); //可用于对当前用户进行访问权限测试
//成功返回0,出错返回1
//pathname:文件绝对路径
//mode:要测试的权限,R_OK W_OK X_OK
#include <sys/stat.h>
mode_t umask(mode_t cmask);
//返回之前文件模式创建屏蔽字

实例4-16 使用unlink函数使文件计数减一

#include "apue.h"
#include <fcntl.h>

int main(){
	if(open("/root/unix/tempfile", O_RDWR) < 0)
		err_sys("open error");
	if(unlink("/root/unix/tempfile") < 0)
		err_sys("unlink error");
	printf("file unlinked\n");
	sleep(15);
	printf("done\n");
	exit(0);
}
#include <unistd.h>
int unlink(const char *pathname); //可以把文件的链接计数减一
//成功返回0,失败返回1
//pathname:文件的绝对路径

可以看到执行代码后tempfile文件消失(执行前先自己创建这个文件),因为链接计数为0会被删除,但是有进程如果打开了这个文件,那么这个文件在进程关闭该文件之前不会删除文件。如果想要进程执行期间创建临时文件,进程结束立刻删除临时文件,则可以利用这个特性(creat文件后立刻调用unlink,这样即使程序崩溃也能删除临时文件)。

实例7-3 使用atexit函数

#include "apue.h"
#include <stdlib.h>

static void my_exit1();
static void my_exit2();

int main(){
	if(atexit(my_exit1) != 0)
		err_sys("can not resgister my_exit1");
	if(atexit(my_exit2) != 0)
                err_sys("can not resgister my_exit2");
	if(atexit(my_exit1) != 0)
                err_sys("can not resgister my_exit1");
	exit(0);
}

static void my_exit1(){
	printf("第1个函数做的事情\n");
}

static void my_exit2(){
        printf("第2个函数做的事情\n");
}

其中:

#include <stdlib.h>
int atexit(void (*func) (void));
//成功返回0,出错返回非0
//传入函数名

调用该函数后,被注册的函数会在exit调用时执行。

实例8-1 研究子进程对变量所做的改变是否会影响父进程

#include <unistd.h>
#include "apue.h"

int globvar = 6;
char buf[] = "a write to stdout\n";

int main(){
	int var = 88;
	pid_t pid;

	if(write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		err_sys("write error");
	printf("before fork, we don't flush stdout\n");

	if((pid = fork()) < 0)
		err_sys("fork error");
	else if(pid > 0)
		sleep(2); //父进程
	else{
        //pid=0时为子进程
		globvar++;
		var++;
	}
	
	printf("pid = %ld, glob = %d, var = %d\n", (long) getpid(), globvar, var);
	exit(0);
}

 其中:

#include <unistd.h>
pid_t fork(void);
//子进程返回0,父进程返回子进程的id

 可以发现子进程对变量所做的操作不会影响父进程(父进程后输出,因为父进程sleep了两秒)

孤儿进程:若一个父进程退出,则他的还未执行完的子进程成为孤儿进程,他们会被init进程收养。

僵死进程:子进程退出后,父进程没有及时调用wait系列函数获取子进程的状态,则此时子进程为僵死进程。

消去僵死进程的方法:除掉父进程,让僵死进程变成孤儿进程让init收养,然后init会获取其状态而消去僵死进程。

 实例8-16 execlp的用法

#include "apue.h"
#include <sys/wait.h>
#include <unistd.h>

int main(){
	pid_t pid;
	if((pid = fork()) < 0)
		err_sys("fork error");
	else if(pid == 0){
		//子进程
		if(execlp("ls", "ls", "-al", (char *)0) < 0)
			err_sys("execlp error");
	}
	//父进程
	if(waitpid(pid, NULL, 0) < 0)
		err_sys("wait error");
	printf("execlp successful!\n");
	exit(0);
}

其中:

#include <unistd.h>
int execl(const char *pathname, const char *arg0, 000 /* (char *)0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, 000 /* (char *)0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, 000 /* (char *)0 */);
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
//出错返回-1,成功则不返回
//filename为文件名,若以/开头,则视为路径名;否则就去PATH环境变量中寻找可执行文件
//execlp可以将调用者的环境传到新程序

执行:

 实例10-2 signal的用法

#include "apue.h"

static void sig_usr(int);

int main(){
	if(signal(SIGUSR1, sig_usr) == SIG_ERR)
		err_sys("can not catch usr1");
	if(signal(SIGUSR2, sig_usr) == SIG_ERR)
                err_sys("can not catch usr2");
	for(;;)
		pause(); //pause函数使调用函数在接到一个信号前挂起
}

//接收到信号后执行的函数
static void sig_usr(int signo){
	if(signo == SIGUSR1)
		printf("usr1 received!\n");
	else if(signo == SIGUSR2)
                printf("usr2 received!\n");
	else
		err_dump("received signal %d\n", signo);
}

 其中:

pause函数使得调用函数在接到一个信号前挂起

signal函数调用后,在接收到对应信号后会执行对应函数,并且只会执行一次。

  实例10-6 SIGCHLD信号

#include "apue.h"
#include <signal.h>
#include <sys/wait.h>

static void sig_cld(int);

int main(){
	pid_t pid;

	if(signal(SIGCHLD, sig_cld) == SIG_ERR)
		perror("signal error");
	if((pid = fork()) < 0)
		perror("fork error");
	else if(pid == 0){
		//子进程
		sleep(2);
		_exit(0);
	}
	//父进程
	pause();
	exit(0);
}

static void sig_cld(int signo){
	pid_t pid;
	int status;
	printf("SIGCHLD RECEIVED!\n");
	if(signal(SIGCHLD, sig_cld) == SIG_ERR)
                perror("signal error");

	if((pid = wait(&status)) < 0)
		perror("wait error");

	printf("pid = %d\n", pid);
}

 当子进程终止时,会产生SIGCHLD信号,可以用来避免产生僵死进程。

实例10-15 sigprocmask设置信号屏蔽字

#include "apue.h"

static void sig_quit(int signo);

int main(void){
	sigset_t newmask, oldmask, pendmask;
	/*注册sigquit信号*/
	if(signal(SIGQUIT, sig_quit) == SIG_ERR){
		err_sys("can not catch SIGQUIT\n");
	}
	/*初始化信号集*/
	sigemptyset(&newmask);
	/*往信号集中添加SIGQUIT信号*/
	sigaddset(&newmask, SIGQUIT);
	/*阻塞SIGQUIT信号,并保存老的信号集*/
	if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0){
		err_sys("SIG_BLOCK error\n");
	}
	/*此时可以发出SIGQUIT信号,发现信号被阻塞,不会触发sig_quit函数*/
	sleep(5);

	/*查看sigquit信号是否被阻塞*/
	if(sigpending(&pendmask) < 0)
		err_sys("sigpending error\n");
	if(sigismember(&pendmask, SIGQUIT))
		printf("\nSIGQUIT pending\n");

	/*取消sigquit信号的阻塞,将原来的信号屏蔽字放进去*/
	if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
		err("SIGMASK error\n");
	printf("SIGQUIT UNBLOCK\n");

	sleep(5);
	exit(0);
}

static void sig_quit(int signo){
	printf("caught SIGQUIT\n");
	if(signal(SIGQUIT, sig_quit) == SIG_ERR){
                err_sys("can not catch SIGQUIT\n");
        }
}

实例11-2 打印线程id

#include <pthread.h>
#include "apue.h"

pthread_t ntid;

void printids(const char *s){
	pid_t pid;
	pthread_t tid;

	pid = getpid();
	tid = pthread_self();

	printf("%s pid %lu, tid %lu, (0x%lx)\n", s, (unsigned long) pid, (unsigned long) tid, (unsigned long) tid);
}

void *thr_fn(void *arg){
	printids("new thread: ");
	return ((void *) 0);
}

int main(void){
	int err;
	err = pthread_create(&ntid, NULL, thr_fn, NULL);
	if(err != 0)
		printf("create thread error\n");
	printids("main thread: ");
	sleep(1);
	exit(0);
}

 两个线程对全局变量a进行a++100次,最后a的值在100~200之间

实例12-16 同步信号处理

#include "apue.h"
#include <pthread.h>
#include <signal.h>

int quitflag;
sigset_t mask; //信号集

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //互斥量
pthread_cond_t waitloc = PTHREAD_COND_INITIALIZER; //条件变量

/*线程执行的函数*/
void *thr_fn(void *arg){
	int err, signo;
	for(;;){
		err = sigwait(&mask, &signo); //mask是线程等待的信号集,signo是接收到的信号
		if(err != 0)
			err_exit(err, "sigwait failed\n");
		switch(signo){
			case SIGINT:
				printf("\ninterrupt\n");
				break;
			case SIGQUIT:
				pthread_mutex_lock(&lock);
				quitflag = 1;
				pthread_mutex_unlock(&lock);
				pthread_cond_signal(&waitloc); //唤醒等待该条件变量的线程
				return 0;

			default:
				printf("unknow signal\n");
				exit(1);
		}
	}
}

int main(void){
	int err;
	sigset_t oldmask;
	pthread_t tid; //线程id

	sigemptyset(&mask); //初始化信号集
	sigaddset(&mask, SIGINT);
	sigaddset(&mask, SIGQUIT);

	if(pthread_sigmask(SIG_BLOCK, &mask, &oldmask) != 0)
		err_sys("sigmask error\n");

	err = pthread_create(&tid, NULL, thr_fn, 0);
	if(err != 0)
		err_exit(err, "create thread error\n");

	pthread_mutex_lock(&lock);
       	while(quitflag == 0)
		pthread_cond_wait(&waitloc, &lock); //等待条件变量waitloc,并且释放lock锁		
        pthread_mutex_unlock(&lock);

	quitflag = 0;

	if(pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0)
                err_sys("sigmask error\n");
	exit(0);
}

 编译:

gcc threadSig.c error.c  -lpthread

 执行结果:

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值