Linux_OS_code_to_sum_up(Linux系统编程总结)

    最近在刷深入理解linux内核这本书,所以将以前所学的linux系统编程相关的知识点总结了一下,当做复习。

一、系统组成

(1)系统:

1:软件(操作系统:内核,系统软件;应用程序)             

2:硬件:CPU,内存,硬盘,键盘,鼠标,显卡   操作系统:内核或者内核和系统工具软件的组合

(2)启动和登录流程:上电,主板BIOS,boot,kernel,init,login,shell      

         配置文件:/ect/profile 系统启动时被执行    

              ~/.bashrc 用户登录时会调用

(3)文件:信息的集合,数据的集合,计算机处理的对象   IT行业处理信息:转换传输,存储

(4)错误处理:errno,strerror, perror  

int main()  
{  
   int fd = open("a.txt", O_RDWR);
   if(fd < 0){  
       printf("errno is %d, error string = %s\n", errno, strerror(errno));  perror("open");  
   }  
}

二、文件IO

(1)open/creat  O_RDONLY、O_WRONLY、O_RDWR、O_CREAT(创建,如已存在,截断,需要增加文件权限)、O_TRUNC、O_APPEND、O_EXCL(与O_CREAT一起用,如存在则失败)

(2)close

(3)read/write 

(4)文件指针,lseek

(5)文件读写效率,缓冲区(buf)设置1024~4096比较合适

(6)文件共享:两个进程同时打开同一个文件进行操作,会相互覆盖,有各自的文件指针

(7)dup:复制文件描述符,让文件描述符指向同一个文件结构

(8)原子操作:例:用APPEND写Log文件

(9)fcntl:这只文件描述符属性,文件状态属性,文件描述符复制,设置文件锁,设置文件通知等功能   
        例:添加O_APPEND属性  

(10)文件映射  硬盘映射到进程的地址,效率高, 限制:文件长度必须大于等于映射长度,映射的offset必须是也得整数倍  
        页的尺寸获取方式:getconf -a | grep PAGE_SIZE    
                 sysconf(_SC_PAGE_SIZE)  

int main()  
{  
   int fd = open("a.txt", O_RDWR);  
   void *ptr = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);  
   close(fd);  
   *(char*)ptr = 'a';  
   munmap(ptr, 1024);  
   //close (fd);  
}  
  

(11)临时文件,mktemp  

(12)缓存,O_SYNC(保证数据写入硬盘,IO效率低),sync/fsync/fsyncdata    fwrite在用户空间和内核空间都有缓存,write只在内核空间有缓存  

(13)标准输入0,标准输出1,标准错误2    STDIN_FLIENO,STDOUT_FILENO,STDERR_FILENO  

(14)open返回值,可用最小文件描述符,文件描述符进程范围内唯一  

(15)dup2(int oldfd, int newfd);   

三、文件和目录:

Linux中的文件种类:普通文件、目录、符号链接,块设备,字符设备,管道,套接字  

1、文件属性:stat  

2、用户和组:实际账户和有限账户,文件权限(access测试文件某权限),umask     设置SUID,运行时使用拥有者的权限  

3、文件长度,truncate,fseek,ftell  

4、文件系统:inode(128-256Byte),数据块(1024-4096Byte)     文件、目录、引用计数器、stat(filename, struct stat *stat)/lstat  

5、目录操作:opendir,closedir,readdir,rewinddir,telldir,seekdir  

int rm(const char *path){  
    struct stat stat_buf;  
    int ret = stat(path, &stat_buf);  
    if(ret < 0){  perror("stat");  return -1;  }  
    if(!S_IS_DIR(stat_buf.st_mode)){  unlink(path);  return 0;  }  
    char buf[1024];  DIR*dir = opendir(path);  
    if(dir == NULL)  return -1;  
    struct dirent*entry = readdir(dir);  
    while(entry){  
        sprintf(buf, "%s/%s", path, entry->d_name);  
        if(entry->d_type == DT_REG || entry->d_type == DT_LNK)  unlink(buf);  
        if(entry->d_type == DT_DIR){  
            if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)  {  entry = readdir(dir);  continue;  }  
            rm(buf);  
        }  
        entry = readdir(dir);  
    }  
    close(dir);  
    rmdir(path);  
    return 0;  
}    
int main(){  
     long loc;  
     DIR*dir = opendir("testdir");  
     struct dirent*entry;  
     while(1){  
         loc = telldir(dir);  entry = readdir(dir);  
         if(entry == NULL) break;  
         if(strcmp(entry->d_name, "a") == 0  break;  
     }  
     seekdir(dir, loc);  
     while(1){  
         entry = readdir(dir);  
         if(entry == NULL)  break;  
         printf("loc is %d, entry->d_name=%s\n", (int)telldir(dir), entry->d_name);  
     }  
     seekdir(dir, loc);  
}   
四、进程环境:  

1、内核和进程的关系:当系统启动时,内核代码被夹在到内存,初始化之后,启动第一个用户进程,然后内核的代码就等着用户进程来调度了。  

2、进程是程序的实例;  

3、PCB:进程运行时,内核为每个进程分配一个PCB(进程控制块,结构体:task_struct),描述进程信息;  

4、虚拟地址空间  

5、CPU:内核将进程PCB放入一个队列,总是让CPU服务队列中的第一个进程,以时间片为单位运行,排队运行,时间到了丢到队尾;  

6、进程属性和状态:  
            PID:进程编号,不会重复,pid_t getpid()  
            PPID:父进程ID,pid_t getppid()  
            账户ID/组ID:getuid/geteuid(真实账户/有效账户),getgid/getegid  
            进程组ID:getpgrp、setpgid/会话组ID:getsid、setsid/控制终端:  
            环境变量:setenv、getenv,unsetenv  
            进程状态:  进程时间:times  
            当前工作目录:getcwd  
        动态库编译: gcc -fPIC -share xxx.c -o libxxx.so  
            程序运行时: export LD_LIBRARY_PATH=. 或将动态库放到/usr/lib  
        静态库打包:ar rcs libtest.a a.o b.o  链接:gcc a.c -llib -static   
五、进程控制:  

1、fork  

2、wait/waitpid  

3、僵尸进程:已经退出,但是父进程还没有调用wait回收的紫禁城  孤儿进程:父进程退出,子进程没有退出  

4、exec:鸠占鹊巢  使用fork和exec执行一个新程序  

5、不定参数:
        void myprint(const char *filename, int line, const char *fmt, ...)  
            va_list ap;  va_start(ap, fmt);  va_arg(ap, const char */int...);  va_end(ap);  

6、 进程间关系:在Linux中,父子关系,组关系,session关系,进程和终端进程关系   
六、信号:内核和进程通信的一种方式  
        1、信号类型:实时信号,非实时信号  
        2、信号的处理:通过signal函数,注册信号处理函数、如果没有注册信号处理函数,那么按照默认方式处理。  
        3、不可靠信号:如果进程收到一个信号来不及处理,这时候有收到一个同样的信号,MAME这两个信号回合秉承一个信号,因为进程保存该信号的值只有一位  
        4、终端系统调用:如果一个进程调用了某系统调用导致该进程处于挂起状态,而此时该进程收到一个信号,那么该系统调用被唤醒,通常该系统调用返回-1,错误码EINTR。  
        5、可重入问题:  
        6、忽略信号:signal(SIGPIPE, SIG_IGN);  
        7、屏蔽信号:sigprocmask  
        8、SIGCHLD:  
        9、sigaction用来注册信号处理函数,sigqueue用来发送信号  sigaction可以传递参数,可以获得发送信号的进程信息,可以设置SA_RESTART
七、线程:

线程也有PCB,他的PCB和进程的PCB结构完全一样,保存的虚拟地址空间和创建它的进程的虚拟地址空间完全一致 

1、pthread_create(pthread_t *thread, const pthread_attr_t *atr/NULL,void *(*start_routine)(void *), void *arg);  

2、线程标识:使用pthread_t标识线程,他是非负整数,由系统分配,保证在晋城范围内唯一  
          使用pthread_equal判断线程是否相等,使用pthread_self获取目前代码运行的线程  

3、线程终止:例程返回,pthread_exit,pthread_cancel(异常退出)  
           线程里调用exit退出整个进程,主线程调用pthread_exit进程不会退出,但是主线程已经推出了,如果主线程main函数return,那么其他线程也结束了  

4、线程回收:pthread_join回收线程PCB,等待编程结束  

5、线程的同步:  锁(临界量):避免两个线程同时访问一个全局变量  
           锁会有效率低和思索问题,解决方式:
             读写锁:    循环锁:  
           基本锁:类型:pthrea_mutex_t 一般在全局定义:pthread_mutex_t g_mutex;  初始化:ptread_mutex_init(&g_mutex,NULL);  加锁:pthread_mutex_lock(&g_mutex);  解锁:ptread_mutex_unlock(&g_mutex);  
           锁的粒度越大,效率越低  
           死锁:忘了解锁,重复加锁  
           循环锁:同一个线程进行多次加锁,不会阻塞:pthread_mutexattr_t attr;    pthread_mutexattr_init(&attr);    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);    pthread_Mutex_init(&mutex, &attr);  
           读共享写排他锁:pthread_rwlock_t mutex;  pthread_rwlock_init(&mutex, NULL);  pthread_rwlock_rdlock(&mutex);  pthread_rwlock_wrlock(&mutex);  pthread_rwlock_unlock(&mutex);  

6、C++使用构造函数和析构函数自动加锁解锁  

7、条件变量: pthread_cond_t g_cond;  pthread_cond_init(&g_cond);  pthread_cond_wait(&g_cond,&g_mutex);  pthread_cond_signal(&g_cond);//唤醒  pthread_cond_broadcast(&g_cond);//全部唤醒  

8、信号量: sem_t sem;  sem_init(&sem, 0, 0 // 信号量初始值);  sem_wait(&sem);//成功后sem值-1  sem_pose(&sem);//sem+=1  

9、重入:如果函数操作了全局变量,这个函数就不是可重入函数了  

10、分离的线程运行结束之后,他的PCB同时被释放了  

11、线程私有数据:线程私有数据可以在该县城调用函数中访问,其他线程调用的函数中,不可访问。  
     pthread_key_t key;  pthread_key_create(&key, 用来清理私有数据的函数指针);  pthread_set_specific(key, data);  void *data = pthread_get_specific(key);  

12、线程取消:pthread_cancel  pthread_setcancelstate(...);


八、守护进程:Daemon  
   规则:设置umask为0; 调用fork,并让父进程退出; 
      调用setuid创建新会话; 重新设置当前目录;关闭不需要的文件描述符;
      重定向标准输入,输出,错误到dev/null  
      出错处理:调式信息:<syslog.h> void openlog(const char *ident, int option, int facility);     
                      void syslog(int priority, const char *format, ...);     
                      void closelog(void);     
                     int setlogmask(int maskpri);  
   单例:使用文件锁来实现单例  
   惯例:单例文件路径在/var/run目录下,内容为该进程ID;配置文件在/etc目录下;启动脚本通常放在/etc/init.d目录下

九、高级IO  

1、非阻塞IO  O_NONblOCK标记打开文件,read不到会返回-1,errno被标记为EAGAIN;  如果open没有带上O_NONBLOCK,可以通过fcntl设置  

2、记录锁:

int main(){  
	int fd = open("a.txt", ORDWR);
	struct flock l;  
	l.l_type = F_WRLOCK;
	l.l_whence = SEEK_SET;  
	l.l_start = 0;  
	l.l_len = 128;  
	int ret = fcntl(fd, F_SETLKW, &l);    
	l.l_type = F_UNLCK;  
	fcntl(fd, F_SETLKW, &l);  
}  

3、IO多路转接:  
    select:使用位域来表示描述符集合  让内核监听一个fd集合,当集合中有fd事件时,会返回有消息的fd子集 

int main()  {  
  int fd_mice = open("/dev/input/mice", O_RDONLY);  
  int fd_keyb = open("/dev/input/event1", O_RDONLY);  
  int readlen;  char buf[1024];  
  while(1){  
	fd_set set;  
	FD_ZERO(&set);  
	FD_SET(fd_mice, &set);  
	FD_SET(fd_keyb, &set);  
	int nfds = fd_keyb+1;  
    struct timeval tv;  
	tv.tv_sec = 2;  
	tv.tv_userc = 0;  
	int ret = select(nfds, &set, NULL, NULL, &tv);  
    if(ret == -1){  if(errno == EINTR)  continue;  exit(1);  }  
    if(ret == 0)  printf("not input\n");  
    if(FD_ISSET(fd_mice, &set)  readlen = read(fd_mice, buf, sizeof(buf));  
    if(FD_ISSET(fd_keyb, &set)  readlen = read(fd_keyb, buf, sizeof(buf));  
  }  
} 

epoll:使用红黑树来保存文件集合

int main(){  
    int readlen;  char buf[1024];  
    int fd_mice = open("/dev/input/mice", O_RDONLY);  
    if(fd_mice < 0)  return 1;  
    int fd_keyb = open("/dev/input/event1",O_RDONLY);  
    if(fd_keyb < 0)  return 1;    
    int epollfd = epoll_create(512);  
    struct epoll_event ev;  ev.events = EPOLLIN;  ev.data.fd = fd_mice;  
    int ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd_mice, &ev);  
    if(ret < 0)  perror("epoll_ctl mice");  
    ev.events = EPOLLIN;  
	ev.data.fd = fd_keyb;  
	ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd_keyb, &ev);  
    if(ret < 0)  perror("epoll_ctl keyb");  
    sturct epoll_event out_events[2];  
    while(1){  
        int ret = epoll_wait(epollfd, out_events, 2, 2000);  
        printf("epoll_wait return %d\n", ret);  
        if(ret < 0){  if(errno == EINTR)  continue;  else  exit(1);  }  
        if(ret == 0)  printf("not input\n");  
        else{  
            int i;  
            for(i = 0; i < ret; ++i){  
                struct epoll_event* p = &out_events[i];  
                if(p->data.fd == fd_mice)  printf("mouse event");  
                else if(p->data.fd == fd_keyb)  printf("keyboard event");  
            }  
        }  
    }  
}
4、存储映射IO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值